Welis Garagen-Anzeige

This commit is contained in:
2018-02-04 20:14:45 +01:00
parent 9b4b42bdc0
commit 48d7ac9661
855 changed files with 134068 additions and 5 deletions
+1122
View File
File diff suppressed because it is too large Load Diff
+446
View File
@@ -0,0 +1,446 @@
/**
* Module dependencies.
*/
var spawn = require('cross-spawn');
var utils = require('./utils');
var debug = require('debug')('gm');
var series = require('array-series');
var PassThrough = require('stream').PassThrough;
/**
* Error messaging.
*/
var noBufferConcat = 'gm v1.9.0+ required node v0.8+. Please update your version of node, downgrade gm < 1.9, or do not use `bufferStream`.';
/**
* Extend proto
*/
module.exports = function (proto) {
function args (prop) {
return function args () {
var len = arguments.length;
var a = [];
var i = 0;
for (; i < len; ++i) {
a.push(arguments[i]);
}
this[prop] = this[prop].concat(a);
return this;
}
}
function streamToUnemptyBuffer(stream, callback) {
var done = false
var buffers = []
stream.on('data', function (data) {
buffers.push(data)
})
stream.on('end', function () {
var result, err;
if (done)
return
done = true
result = Buffer.concat(buffers)
buffers = null
if (result.length==0)
{
err = new Error("Stream yields empty buffer");
callback(err, null);
} else {
callback(null, result);
}
})
stream.on('error', function (err) {
done = true
buffers = null
callback(err)
})
}
proto.in = args('_in');
proto.out = args('_out');
proto._preprocessor = [];
proto.preprocessor = args('_preprocessor');
/**
* Execute the command and write the image to the specified file name.
*
* @param {String} name
* @param {Function} callback
* @return {Object} gm
*/
proto.write = function write (name, callback) {
if (!callback) callback = name, name = null;
if ("function" !== typeof callback) {
throw new TypeError("gm().write() expects a callback function")
}
if (!name) {
return callback(TypeError("gm().write() expects a filename when writing new files"));
}
this.outname = name;
var self = this;
this._preprocess(function (err) {
if (err) return callback(err);
self._spawn(self.args(), true, callback);
});
}
/**
* Execute the command and return stdin and stderr
* ReadableStreams providing the image data.
* If no callback is passed, a "through" stream will be returned,
* and stdout will be piped through, otherwise the error will be passed.
*
* @param {String} format (optional)
* @param {Function} callback (optional)
* @return {Stream}
*/
proto.stream = function stream (format, callback) {
if (!callback && typeof format === 'function') {
callback = format;
format = null;
}
var throughStream;
if ("function" !== typeof callback) {
throughStream = new PassThrough();
callback = function (err, stdout, stderr) {
if (err) throughStream.emit('error', err);
else stdout.pipe(throughStream);
}
}
if (format) {
format = format.split('.').pop();
this.outname = format + ":-";
}
var self = this;
this._preprocess(function (err) {
if (err) return callback(err);
return self._spawn(self.args(), false, callback);
});
return throughStream || this;
}
/**
* Convenience function for `proto.stream`.
* Simply returns the buffer instead of the stream.
*
* @param {String} format (optional)
* @param {Function} callback
* @return {null}
*/
proto.toBuffer = function toBuffer (format, callback) {
if (!callback) callback = format, format = null;
if ("function" !== typeof callback) {
throw new Error('gm().toBuffer() expects a callback.');
}
return this.stream(format, function (err, stdout) {
if (err) return callback(err);
streamToUnemptyBuffer(stdout, callback);
})
}
/**
* Run any preProcessor functions in series. Used by autoOrient.
*
* @param {Function} callback
* @return {Object} gm
*/
proto._preprocess = function _preprocess (callback) {
series(this._preprocessor, this, callback);
}
/**
* Execute the command, buffer input and output, return stdout and stderr buffers.
*
* @param {String} bin
* @param {Array} args
* @param {Function} callback
* @return {Object} gm
*/
proto._exec = function _exec (args, callback) {
return this._spawn(args, true, callback);
}
/**
* Execute the command with stdin, returning stdout and stderr streams or buffers.
* @param {String} bin
* @param {Array} args
* @param {ReadableStream} stream
* @param {Boolean} shouldBuffer
* @param {Function} callback, signature (err, stdout, stderr) -> *
* @return {Object} gm
* @TODO refactor this mess
*/
proto._spawn = function _spawn (args, bufferOutput, callback) {
var appPath = this._options.appPath || '';
var bin = this._options.imageMagick
? appPath + args.shift()
: appPath + 'gm'
var cmd = bin + ' ' + args.map(utils.escape).join(' ')
, self = this
, proc, err
, timeout = parseInt(this._options.timeout)
, disposers = this._options.disposers
, timeoutId;
debug(cmd);
//imageMagick does not support minify (https://github.com/aheckmann/gm/issues/385)
if(args.indexOf("-minify") > -1 && this._options.imageMagick){
err = new Error("imageMagick does not support minify, use -scale or -sample. Alternatively, use graphicsMagick");
return cb(err);
}
try {
proc = spawn(bin, args);
} catch (e) {
return cb(e);
}
proc.stdin.once('error', cb);
proc.on('error', function(err){
if (err.code === 'ENOENT') {
cb(new Error('Could not execute GraphicsMagick/ImageMagick: '+cmd+" this most likely means the gm/convert binaries can't be found"));
} else {
cb(err);
}
});
if (timeout) {
timeoutId = setTimeout(function(){
dispose('gm() resulted in a timeout.');
}, timeout);
}
if (disposers) {
disposers.forEach(function(disposer) {
disposer.events.forEach(function(event) {
disposer.emitter.on(event, dispose);
});
});
}
if (self.sourceBuffer) {
proc.stdin.write(this.sourceBuffer);
proc.stdin.end();
} else if (self.sourceStream) {
if (!self.sourceStream.readable) {
err = new Error("gm().stream() or gm().write() with a non-readable stream.");
return cb(err);
}
self.sourceStream.pipe(proc.stdin);
// bufferStream
// We convert the input source from a stream to a buffer.
if (self.bufferStream && !this._buffering) {
if (!Buffer.concat) {
throw new Error(noBufferConcat);
}
// Incase there are multiple processes in parallel,
// we only need one
self._buffering = true;
streamToUnemptyBuffer(self.sourceStream, function (err, buffer) {
self.sourceBuffer = buffer;
self.sourceStream = null; // The stream is now dead
})
}
}
// for _exec operations (identify() mostly), we also
// need to buffer the output stream before returning
if (bufferOutput) {
var stdout = ''
, stderr = ''
, onOut
, onErr
, onExit
proc.stdout.on('data', onOut = function (data) {
stdout += data;
});
proc.stderr.on('data', onErr = function (data) {
stderr += data;
});
proc.on('close', onExit = function (code, signal) {
if (code !== 0 || signal !== null) {
err = new Error('Command failed: ' + stderr);
err.code = code;
err.signal = signal;
};
cb(err, stdout, stderr, cmd);
stdout = stderr = onOut = onErr = onExit = null;
});
} else {
cb(null, proc.stdout, proc.stderr, cmd);
}
return self;
function cb (err, stdout, stderr, cmd) {
if (cb.called) return;
if (timeoutId) clearTimeout(timeoutId);
cb.called = 1;
if (args[0] !== 'identify' && bin !== 'identify') {
self._in = [];
self._out = [];
}
callback.call(self, err, stdout, stderr, cmd);
}
function dispose (msg) {
var message = msg ? msg : 'gm() was disposed';
err = new Error(message);
cb(err);
if (proc.exitCode === null) {
proc.stdin.pause();
proc.kill();
}
}
}
/**
* Returns arguments to be used in the command.
*
* @return {Array}
*/
proto.args = function args () {
var outname = this.outname || "-";
if (this._outputFormat) outname = this._outputFormat + ':' + outname;
return [].concat(
this._subCommand
, this._in
, this.src()
, this._out
, outname
).filter(Boolean); // remove falsey
}
/**
* Adds an img source formatter.
*
* `formatters` are passed an array of images which will be
* used as 'input' images for the command. Useful for methods
* like `.append()` where multiple source images may be used.
*
* @param {Function} formatter
* @return {gm} this
*/
proto.addSrcFormatter = function addSrcFormatter (formatter) {
if ('function' != typeof formatter)
throw new TypeError('sourceFormatter must be a function');
this._sourceFormatters || (this._sourceFormatters = []);
this._sourceFormatters.push(formatter);
return this;
}
/**
* Applies all _sourceFormatters
*
* @return {Array}
*/
proto.src = function src () {
var arr = [];
for (var i = 0; i < this._sourceFormatters.length; ++i) {
this._sourceFormatters[i].call(this, arr);
}
return arr;
}
/**
* Image types.
*/
var types = {
'jpg': /\.jpe?g$/i
, 'png' : /\.png$/i
, 'gif' : /\.gif$/i
, 'tiff': /\.tif?f$/i
, 'bmp' : /(?:\.bmp|\.dib)$/i
, 'webp': /\.webp$/i
};
types.jpeg = types.jpg;
types.tif = types.tiff;
types.dib = types.bmp;
/**
* Determine the type of source image.
*
* @param {String} type
* @return {Boolean}
* @example
* if (this.inputIs('png')) ...
*/
proto.inputIs = function inputIs (type) {
if (!type) return false;
var rgx = types[type];
if (!rgx) {
if ('.' !== type[0]) type = '.' + type;
rgx = new RegExp('\\' + type + '$', 'i');
}
return rgx.test(this.source);
}
/**
* add disposer (like 'close' of http.IncomingMessage) in order to dispose gm() with any event
*
* @param {EventEmitter} emitter
* @param {Array} events
* @return {Object} gm
* @example
* command.addDisposer(req, ['close', 'end', 'finish']);
*/
proto.addDisposer = function addDisposer (emitter, events) {
if (!this._options.disposers) {
this._options.disposers = [];
}
this._options.disposers.push({
emitter: emitter,
events: events
});
return this;
};
}
+120
View File
@@ -0,0 +1,120 @@
// compare
var spawn = require('cross-spawn');
/**
* Compare two images uses graphicsmagicks `compare` command.
*
* gm.compare(img1, img2, 0.4, function (err, equal, equality) {
* if (err) return handle(err);
* console.log('The images are equal: %s', equal);
* console.log('There equality was %d', equality);
* });
*
* @param {String} orig Path to an image.
* @param {String} compareTo Path to another image to compare to `orig`.
* @param {Number|Object} [options] Options object or the amount of difference to tolerate before failing - defaults to 0.4
* @param {Function} cb(err, Boolean, equality, rawOutput)
*/
module.exports = exports = function (proto) {
function compare(orig, compareTo, options, cb) {
var isImageMagick = this._options && this._options.imageMagick;
var appPath = this._options && this._options.appPath || '';
var bin = isImageMagick
? appPath + 'compare'
: appPath + 'gm'
var args = ['-metric', 'mse', orig, compareTo]
if (!isImageMagick) {
args.unshift('compare');
}
var tolerance = 0.4;
// outputting the diff image
if (typeof options === 'object') {
if (options.highlightColor && options.highlightColor.indexOf('"') < 0) {
options.highlightColor = '"' + options.highlightColor + '"';
}
if (options.file) {
if (typeof options.file !== 'string') {
throw new TypeError('The path for the diff output is invalid');
}
// graphicsmagick defaults to red
if (options.highlightColor) {
args.push('-highlight-color');
args.push(options.highlightColor);
}
if (options.highlightStyle) {
args.push('-highlight-style')
args.push(options.highlightStyle)
}
// For IM, filename is the last argument. For GM it's `-file <filename>`
if (!isImageMagick) {
args.push('-file');
}
args.push(options.file);
}
if (typeof options.tolerance != 'undefined') {
if (typeof options.tolerance !== 'number') {
throw new TypeError('The tolerance value should be a number');
}
tolerance = options.tolerance;
}
} else {
// For ImageMagick diff file is required but we don't care about it, so null it out
if (isImageMagick) {
args.push('null:');
}
if (typeof options == 'function') {
cb = options; // tolerance value not provided, flip the cb place
} else {
tolerance = options
}
}
var proc = spawn(bin, args);
var stdout = '';
var stderr = '';
proc.stdout.on('data',function(data) { stdout+=data });
proc.stderr.on('data',function(data) { stderr+=data });
proc.on('close', function (code) {
// ImageMagick returns err code 2 if err, 0 if similar, 1 if dissimilar
if (isImageMagick) {
if (code === 0) {
return cb(null, 0 <= tolerance, 0, stdout);
}
else if (code === 1) {
err = null;
stdout = stderr;
} else {
return cb(stderr);
}
} else {
if(code !== 0) {
return cb(stderr);
}
}
// Since ImageMagick similar gives err code 0 and no stdout, there's really no matching
// Otherwise, output format for IM is `12.00 (0.123)` and for GM it's `Total: 0.123`
var regex = isImageMagick ? /\((\d+\.?[\d\-\+e]*)\)/m : /Total: (\d+\.?\d*)/m;
var match = regex.exec(stdout);
if (!match) {
err = new Error('Unable to parse output.\nGot ' + stdout);
return cb(err);
}
var equality = parseFloat(match[1]);
cb(null, equality <= tolerance, equality, stdout, orig, compareTo);
});
}
if (proto) {
proto.compare = compare;
}
return compare;
};
+29
View File
@@ -0,0 +1,29 @@
// composite
/**
* Composite images together using the `composite` command in graphicsmagick.
*
* gm('/path/to/image.jpg')
* .composite('/path/to/second_image.jpg')
* .geometry('+100+150')
* .write('/path/to/composite.png', function(err) {
* if(!err) console.log("Written composite image.");
* });
*
* @param {String} other Path to the image that contains the changes.
* @param {String} [mask] Path to the image with opacity informtion. Grayscale.
*/
module.exports = exports = function(proto) {
proto.composite = function(other, mask) {
this.in(other);
// If the mask is defined, add it to the output.
if(typeof mask !== "undefined")
this.out(mask);
this.subCommand("composite");
return this;
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Extend proto
*/
module.exports = function (proto) {
require("./convenience/thumb")(proto);
require("./convenience/morph")(proto);
require("./convenience/sepia")(proto);
require("./convenience/autoOrient")(proto);
}
+57
View File
@@ -0,0 +1,57 @@
/**
* Extend proto.
*/
module.exports = function (proto) {
var exifTransforms = {
topleft: ''
, topright: ['-flop']
, bottomright: ['-rotate', 180]
, bottomleft: ['-flip']
, lefttop: ['-flip', '-rotate', 90]
, righttop: ['-rotate', 90]
, rightbottom: ['-flop', '-rotate', 90]
, leftbottom: ['-rotate', 270]
}
proto.autoOrient = function autoOrient () {
// Always strip EXIF data since we can't
// change/edit it.
// imagemagick has a native -auto-orient option
// so does graphicsmagick, but in 1.3.18.
// nativeAutoOrient option enables this if you know you have >= 1.3.18
if (this._options.nativeAutoOrient || this._options.imageMagick) {
this.out('-auto-orient');
this.strip();
return this;
}
this.preprocessor(function (callback) {
this.orientation({bufferStream: true}, function (err, orientation) {
if (err) return callback(err);
var transforms = exifTransforms[orientation.toLowerCase()];
if (transforms) {
// remove any existing transforms that might conflict
var index = this._out.indexOf(transforms[0]);
if (~index) {
this._out.splice(index, transforms.length);
}
// repage to fix coordinates
this._out.unshift.apply(this._out, transforms.concat('-page', '+0+0'));
}
this.strip();
callback();
});
});
return this;
}
}
+61
View File
@@ -0,0 +1,61 @@
/**
* Module dependencies.
*/
var fs = require('fs');
var parallel = require('array-parallel');
/**
* Extend proto.
*/
module.exports = function (proto) {
/**
* Do nothing.
*/
function noop () {}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-morph
proto.morph = function morph (other, outname, callback) {
if (!outname) {
throw new Error("an output filename is required");
}
callback = (callback || noop).bind(this)
var self = this;
if (Array.isArray(other)) {
other.forEach(function (img) {
self.out(img);
});
self.out("-morph", other.length);
} else {
self.out(other, "-morph", 1);
}
self.write(outname, function (err, stdout, stderr, cmd) {
if (err) return callback(err, stdout, stderr, cmd);
// Apparently some platforms create the following temporary files.
// Check if the output file exists, if it doesn't, then
// work with temporary files.
fs.exists(outname, function (exists) {
if (exists) return callback(null, stdout, stderr, cmd);
parallel([
fs.unlink.bind(fs, outname + '.0'),
fs.unlink.bind(fs, outname + '.2'),
fs.rename.bind(fs, outname + '.1', outname)
], function (err) {
callback(err, stdout, stderr, cmd);
})
})
});
return self;
}
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Extend proto.
*/
module.exports = function (proto) {
proto.sepia = function sepia () {
return this.modulate(115, 0, 100).colorize(7, 21, 50);
}
}
+92
View File
@@ -0,0 +1,92 @@
/**
* Extend proto.
*/
module.exports = function (proto) {
proto.thumb = function thumb (w, h, name, quality, align, progressive, callback, opts) {
var self = this,
args = Array.prototype.slice.call(arguments);
opts = args.pop();
if (typeof opts === 'function') {
callback = opts;
opts = '';
} else {
callback = args.pop();
}
w = args.shift();
h = args.shift();
name = args.shift();
quality = args.shift() || 63;
align = args.shift() || 'topleft';
var interlace = args.shift() ? 'Line' : 'None';
self.size(function (err, size) {
if (err) {
return callback.apply(self, arguments);
}
w = parseInt(w, 10);
h = parseInt(h, 10);
var w1, h1;
var xoffset = 0;
var yoffset = 0;
if (size.width < size.height) {
w1 = w;
h1 = Math.floor(size.height * (w/size.width));
if (h1 < h) {
w1 = Math.floor(w1 * (((h-h1)/h) + 1));
h1 = h;
}
} else if (size.width > size.height) {
h1 = h;
w1 = Math.floor(size.width * (h/size.height));
if (w1 < w) {
h1 = Math.floor(h1 * (((w-w1)/w) + 1));
w1 = w;
}
} else if (size.width == size.height) {
var bigger = (w>h?w:h);
w1 = bigger;
h1 = bigger;
}
if (align == 'center') {
if (w < w1) {
xoffset = (w1-w)/2;
}
if (h < h1) {
yoffset = (h1-h)/2;
}
}
self
.quality(quality)
.in("-size", w1+"x"+h1)
.scale(w1, h1, opts)
.crop(w, h, xoffset, yoffset)
.interlace(interlace)
.noProfile()
.write(name, function () {
callback.apply(self, arguments);
});
});
return self;
};
proto.thumbExact = function () {
var self = this,
args = Array.prototype.slice.call(arguments);
args.push('!');
self.thumb.apply(self, args);
};
};
+160
View File
@@ -0,0 +1,160 @@
/**
* Module dependencies.
*/
var escape = require('./utils').escape;
/**
* Extend proto.
*/
module.exports = function (proto) {
// http://www.graphicsmagick.org/GraphicsMagick.html#details-fill
proto.fill = function fill (color) {
return this.out("-fill", color || "none");
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-stroke
proto.stroke = function stroke (color, width) {
if (width) {
this.strokeWidth(width);
}
return this.out("-stroke", color || "none");
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-strokewidth
proto.strokeWidth = function strokeWidth (width) {
return this.out("-strokewidth", width);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-font
proto.font = function font (font, size) {
if (size) {
this.fontSize(size);
}
return this.out("-font", font);
}
// http://www.graphicsmagick.org/GraphicsMagick.html
proto.fontSize = function fontSize (size) {
return this.out("-pointsize", size);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.draw = function draw (args) {
return this.out("-draw", [].slice.call(arguments).join(" "));
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawPoint = function drawPoint (x, y) {
return this.draw("point", x +","+ y);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawLine = function drawLine (x0, y0, x1, y1) {
return this.draw("line", x0+","+y0, x1+","+y1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawRectangle = function drawRectangle (x0, y0, x1, y1, wc, hc) {
var shape = "rectangle"
, lastarg;
if ("undefined" !== typeof wc) {
shape = "roundRectangle";
if ("undefined" === typeof hc) {
hc = wc;
}
lastarg = wc+","+hc;
}
return this.draw(shape, x0+","+y0, x1+","+y1, lastarg);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawArc = function drawArc (x0, y0, x1, y1, a0, a1) {
return this.draw("arc", x0+","+y0, x1+","+y1, a0+","+a1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawEllipse = function drawEllipse (x0, y0, rx, ry, a0, a1) {
if (a0 == undefined) a0 = 0;
if (a1 == undefined) a1 = 360;
return this.draw("ellipse", x0+","+y0, rx+","+ry, a0+","+a1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawCircle = function drawCircle (x0, y0, x1, y1) {
return this.draw("circle", x0+","+y0, x1+","+y1);
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawPolyline = function drawPolyline () {
return this.draw("polyline", formatPoints(arguments));
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawPolygon = function drawPolygon () {
return this.draw("polygon", formatPoints(arguments));
}
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawBezier = function drawBezier () {
return this.draw("bezier", formatPoints(arguments));
}
proto._gravities = [
"northwest"
, "north"
, "northeast"
, "west"
, "center"
, "east"
, "southwest"
, "south"
, "southeast"];
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.drawText = function drawText (x0, y0, text, gravity) {
var gravity = String(gravity || "").toLowerCase()
, arg = ["text " + x0 + "," + y0 + " " + escape(text)];
if (~this._gravities.indexOf(gravity)) {
arg.unshift("gravity", gravity);
}
return this.draw.apply(this, arg);
}
proto._drawProps = ["color", "matte"];
// http://www.graphicsmagick.org/GraphicsMagick.html#details-draw
proto.setDraw = function setDraw (prop, x, y, method) {
prop = String(prop || "").toLowerCase();
if (!~this._drawProps.indexOf(prop)) {
return this;
}
return this.draw(prop, x+","+y, method);
}
}
function formatPoints (points) {
var len = points.length
, result = []
, i = 0;
for (; i < len; ++i) {
result.push(points[i].join(","));
}
return result;
}
Generated Vendored Executable
+346
View File
@@ -0,0 +1,346 @@
/**
* 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', format: '%[EXIF:Orientation]', helper: 'Orientation' }
, '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;
// weird format, key/val pair with nested children. discard the val
o = o[lastkey] = {};
}
level = indent;
if (val) {
// if previous key was exist and we got the same key
// cast it to an array.
if(o.hasOwnProperty(key)){
// cast it to an array and dont forget the previous value
if(!Array.isArray(o[key])){
var tmp = o[key];
o[key] = [tmp];
}
// set value
o[key].push(val);
} else {
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;
}
/**
* Map exif orientation codes to orientation names.
*/
var orientations = {
'1': 'TopLeft'
, '2': 'TopRight'
, '3': 'BottomRight'
, '4': 'BottomLeft'
, '5': 'LeftTop'
, '6': 'RightTop'
, '7': 'RightBottom'
, '8': 'LeftBottom'
}
/**
* 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");
var width = parseInt(split[0], 10);
var height = parseInt(split[1], 10);
if (o.size && o.size.width && o.size.height) {
if (width > o.size.width) o.size.width = width;
if (height > o.size.height) o.size.height = height;
} else {
o.size = {
width: width,
height: height
}
}
};
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);
};
helper.Orientation = function Orientation (o, val) {
if (val in orientations) {
o['Profile-EXIF'] || (o['Profile-EXIF'] = {});
o['Profile-EXIF'].Orientation = val;
o.Orientation = orientations[val];
} else {
o.Orientation = val || 'Unknown';
}
};
}
+24
View File
@@ -0,0 +1,24 @@
// montage
/**
* Montage images next to each other using the `montage` command in graphicsmagick.
*
* gm('/path/to/image.jpg')
* .montage('/path/to/second_image.jpg')
* .geometry('+100+150')
* .write('/path/to/montage.png', function(err) {
* if(!err) console.log("Written montage image.");
* });
*
* @param {String} other Path to the image that contains the changes.
*/
module.exports = exports = function(proto) {
proto.montage = function(other) {
this.in(other);
this.subCommand("montage");
return this;
}
}
+17
View File
@@ -0,0 +1,17 @@
module.exports = exports = function (proto) {
proto._options = {};
proto.options = function setOptions (options) {
var keys = Object.keys(options)
, i = keys.length
, key
while (i--) {
key = keys[i];
this._options[key] = options[key];
}
return this;
}
}
+43
View File
@@ -0,0 +1,43 @@
/**
* Escape the given shell `arg`.
*
* @param {String} arg
* @return {String}
* @api public
*/
exports.escape = function escape (arg) {
return '"' + String(arg).trim().replace(/"/g, '\\"') + '"';
};
exports.unescape = function escape (arg) {
return String(arg).trim().replace(/"/g, "");
};
exports.argsToArray = function (args) {
var arr = [];
for (var i = 0; i <= arguments.length; i++) {
if ('undefined' != typeof arguments[i])
arr.push(arguments[i]);
}
return arr;
};
exports.isUtil = function (v) {
var ty = 'object';
switch (Object.prototype.toString.call(v)) {
case '[object String]':
ty = 'String';
break;
case '[object Array]':
ty = 'Array';
break;
case '[object Boolean]':
ty = 'Boolean';
break;
}
return ty;
}