corrade-nucleus-nucleons – Blame information for rev 36

Subversion Repositories:
Rev:
Rev Author Line No. Line
36 office 1  
2 /**
3 * Module dependencies.
4 */
5  
6 var spawn = require('cross-spawn');
7 var utils = require('./utils');
8 var debug = require('debug')('gm');
9 var series = require('array-series');
10 var PassThrough = require('stream').PassThrough;
11  
12 /**
13 * Error messaging.
14 */
15  
16 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`.';
17  
18 /**
19 * Extend proto
20 */
21  
22 module.exports = function (proto) {
23  
24 function args (prop) {
25 return function args () {
26 var len = arguments.length;
27 var a = [];
28 var i = 0;
29  
30 for (; i < len; ++i) {
31 a.push(arguments[i]);
32 }
33  
34 this[prop] = this[prop].concat(a);
35 return this;
36 }
37 }
38  
39 function streamToUnemptyBuffer(stream, callback) {
40 var done = false
41 var buffers = []
42  
43 stream.on('data', function (data) {
44 buffers.push(data)
45 })
46  
47 stream.on('end', function () {
48 var result, err;
49 if (done)
50 return
51  
52 done = true
53 result = Buffer.concat(buffers)
54 buffers = null
55 if (result.length==0)
56 {
57 err = new Error("Stream yields empty buffer");
58 callback(err, null);
59 } else {
60 callback(null, result);
61 }
62 })
63  
64 stream.on('error', function (err) {
65 done = true
66 buffers = null
67 callback(err)
68 })
69 }
70  
71 proto.in = args('_in');
72 proto.out = args('_out');
73  
74 proto._preprocessor = [];
75 proto.preprocessor = args('_preprocessor');
76  
77 /**
78 * Execute the command and write the image to the specified file name.
79 *
80 * @param {String} name
81 * @param {Function} callback
82 * @return {Object} gm
83 */
84  
85 proto.write = function write (name, callback) {
86 if (!callback) callback = name, name = null;
87  
88 if ("function" !== typeof callback) {
89 throw new TypeError("gm().write() expects a callback function")
90 }
91  
92 if (!name) {
93 return callback(TypeError("gm().write() expects a filename when writing new files"));
94 }
95  
96 this.outname = name;
97  
98 var self = this;
99 this._preprocess(function (err) {
100 if (err) return callback(err);
101 self._spawn(self.args(), true, callback);
102 });
103 }
104  
105 /**
106 * Execute the command and return stdin and stderr
107 * ReadableStreams providing the image data.
108 * If no callback is passed, a "through" stream will be returned,
109 * and stdout will be piped through, otherwise the error will be passed.
110 *
111 * @param {String} format (optional)
112 * @param {Function} callback (optional)
113 * @return {Stream}
114 */
115  
116 proto.stream = function stream (format, callback) {
117 if (!callback && typeof format === 'function') {
118 callback = format;
119 format = null;
120 }
121  
122 var throughStream;
123  
124 if ("function" !== typeof callback) {
125 throughStream = new PassThrough();
126 callback = function (err, stdout, stderr) {
127 if (err) throughStream.emit('error', err);
128 else stdout.pipe(throughStream);
129 }
130 }
131  
132 if (format) {
133 format = format.split('.').pop();
134 this.outname = format + ":-";
135 }
136  
137 var self = this;
138 this._preprocess(function (err) {
139 if (err) return callback(err);
140 return self._spawn(self.args(), false, callback);
141 });
142  
143 return throughStream || this;
144 }
145  
146 /**
147 * Convenience function for `proto.stream`.
148 * Simply returns the buffer instead of the stream.
149 *
150 * @param {String} format (optional)
151 * @param {Function} callback
152 * @return {null}
153 */
154  
155 proto.toBuffer = function toBuffer (format, callback) {
156 if (!callback) callback = format, format = null;
157  
158 if ("function" !== typeof callback) {
159 throw new Error('gm().toBuffer() expects a callback.');
160 }
161  
162 return this.stream(format, function (err, stdout) {
163 if (err) return callback(err);
164  
165 streamToUnemptyBuffer(stdout, callback);
166 })
167 }
168  
169 /**
170 * Run any preProcessor functions in series. Used by autoOrient.
171 *
172 * @param {Function} callback
173 * @return {Object} gm
174 */
175  
176 proto._preprocess = function _preprocess (callback) {
177 series(this._preprocessor, this, callback);
178 }
179  
180 /**
181 * Execute the command, buffer input and output, return stdout and stderr buffers.
182 *
183 * @param {String} bin
184 * @param {Array} args
185 * @param {Function} callback
186 * @return {Object} gm
187 */
188  
189 proto._exec = function _exec (args, callback) {
190 return this._spawn(args, true, callback);
191 }
192  
193 /**
194 * Execute the command with stdin, returning stdout and stderr streams or buffers.
195 * @param {String} bin
196 * @param {Array} args
197 * @param {ReadableStream} stream
198 * @param {Boolean} shouldBuffer
199 * @param {Function} callback, signature (err, stdout, stderr) -> *
200 * @return {Object} gm
201 * @TODO refactor this mess
202 */
203  
204 proto._spawn = function _spawn (args, bufferOutput, callback) {
205 var appPath = this._options.appPath || '';
206 var bin = this._options.imageMagick
207 ? appPath + args.shift()
208 : appPath + 'gm'
209  
210 var cmd = bin + ' ' + args.map(utils.escape).join(' ')
211 , self = this
212 , proc, err
213 , timeout = parseInt(this._options.timeout)
214 , disposers = this._options.disposers
215 , timeoutId;
216  
217 debug(cmd);
218 //imageMagick does not support minify (https://github.com/aheckmann/gm/issues/385)
219 if(args.indexOf("-minify") > -1 && this._options.imageMagick){
220 err = new Error("imageMagick does not support minify, use -scale or -sample. Alternatively, use graphicsMagick");
221 return cb(err);
222 }
223 try {
224 proc = spawn(bin, args);
225 } catch (e) {
226 return cb(e);
227 }
228 proc.stdin.once('error', cb);
229  
230 proc.on('error', function(err){
231 if (err.code === 'ENOENT') {
232 cb(new Error('Could not execute GraphicsMagick/ImageMagick: '+cmd+" this most likely means the gm/convert binaries can't be found"));
233 } else {
234 cb(err);
235 }
236 });
237  
238 if (timeout) {
239 timeoutId = setTimeout(function(){
240 dispose('gm() resulted in a timeout.');
241 }, timeout);
242 }
243  
244 if (disposers) {
245 disposers.forEach(function(disposer) {
246 disposer.events.forEach(function(event) {
247 disposer.emitter.on(event, dispose);
248 });
249 });
250 }
251  
252 if (self.sourceBuffer) {
253 proc.stdin.write(this.sourceBuffer);
254 proc.stdin.end();
255 } else if (self.sourceStream) {
256  
257 if (!self.sourceStream.readable) {
258 err = new Error("gm().stream() or gm().write() with a non-readable stream.");
259 return cb(err);
260 }
261  
262 self.sourceStream.pipe(proc.stdin);
263  
264 // bufferStream
265 // We convert the input source from a stream to a buffer.
266 if (self.bufferStream && !this._buffering) {
267 if (!Buffer.concat) {
268 throw new Error(noBufferConcat);
269 }
270  
271 // Incase there are multiple processes in parallel,
272 // we only need one
273 self._buffering = true;
274  
275 streamToUnemptyBuffer(self.sourceStream, function (err, buffer) {
276 self.sourceBuffer = buffer;
277 self.sourceStream = null; // The stream is now dead
278 })
279 }
280 }
281  
282 // for _exec operations (identify() mostly), we also
283 // need to buffer the output stream before returning
284 if (bufferOutput) {
285 var stdout = ''
286 , stderr = ''
287 , onOut
288 , onErr
289 , onExit
290  
291 proc.stdout.on('data', onOut = function (data) {
292 stdout += data;
293 });
294  
295 proc.stderr.on('data', onErr = function (data) {
296 stderr += data;
297 });
298  
299 proc.on('close', onExit = function (code, signal) {
300 if (code !== 0 || signal !== null) {
301 err = new Error('Command failed: ' + stderr);
302 err.code = code;
303 err.signal = signal;
304 };
305 cb(err, stdout, stderr, cmd);
306 stdout = stderr = onOut = onErr = onExit = null;
307 });
308 } else {
309 cb(null, proc.stdout, proc.stderr, cmd);
310 }
311  
312 return self;
313  
314 function cb (err, stdout, stderr, cmd) {
315 if (cb.called) return;
316 if (timeoutId) clearTimeout(timeoutId);
317 cb.called = 1;
318 if (args[0] !== 'identify' && bin !== 'identify') {
319 self._in = [];
320 self._out = [];
321 }
322 callback.call(self, err, stdout, stderr, cmd);
323 }
324  
325 function dispose (msg) {
326 var message = msg ? msg : 'gm() was disposed';
327 err = new Error(message);
328 cb(err);
329 if (proc.exitCode === null) {
330 proc.stdin.pause();
331 proc.kill();
332 }
333 }
334 }
335  
336 /**
337 * Returns arguments to be used in the command.
338 *
339 * @return {Array}
340 */
341  
342 proto.args = function args () {
343 var outname = this.outname || "-";
344 if (this._outputFormat) outname = this._outputFormat + ':' + outname;
345  
346 return [].concat(
347 this._subCommand
348 , this._in
349 , this.src()
350 , this._out
351 , outname
352 ).filter(Boolean); // remove falsey
353 }
354  
355 /**
356 * Adds an img source formatter.
357 *
358 * `formatters` are passed an array of images which will be
359 * used as 'input' images for the command. Useful for methods
360 * like `.append()` where multiple source images may be used.
361 *
362 * @param {Function} formatter
363 * @return {gm} this
364 */
365  
366 proto.addSrcFormatter = function addSrcFormatter (formatter) {
367 if ('function' != typeof formatter)
368 throw new TypeError('sourceFormatter must be a function');
369 this._sourceFormatters || (this._sourceFormatters = []);
370 this._sourceFormatters.push(formatter);
371 return this;
372 }
373  
374 /**
375 * Applies all _sourceFormatters
376 *
377 * @return {Array}
378 */
379  
380 proto.src = function src () {
381 var arr = [];
382 for (var i = 0; i < this._sourceFormatters.length; ++i) {
383 this._sourceFormatters[i].call(this, arr);
384 }
385 return arr;
386 }
387  
388 /**
389 * Image types.
390 */
391  
392 var types = {
393 'jpg': /\.jpe?g$/i
394 , 'png' : /\.png$/i
395 , 'gif' : /\.gif$/i
396 , 'tiff': /\.tif?f$/i
397 , 'bmp' : /(?:\.bmp|\.dib)$/i
398 , 'webp': /\.webp$/i
399 };
400  
401 types.jpeg = types.jpg;
402 types.tif = types.tiff;
403 types.dib = types.bmp;
404  
405 /**
406 * Determine the type of source image.
407 *
408 * @param {String} type
409 * @return {Boolean}
410 * @example
411 * if (this.inputIs('png')) ...
412 */
413  
414 proto.inputIs = function inputIs (type) {
415 if (!type) return false;
416  
417 var rgx = types[type];
418 if (!rgx) {
419 if ('.' !== type[0]) type = '.' + type;
420 rgx = new RegExp('\\' + type + '$', 'i');
421 }
422  
423 return rgx.test(this.source);
424 }
425  
426 /**
427 * add disposer (like 'close' of http.IncomingMessage) in order to dispose gm() with any event
428 *
429 * @param {EventEmitter} emitter
430 * @param {Array} events
431 * @return {Object} gm
432 * @example
433 * command.addDisposer(req, ['close', 'end', 'finish']);
434 */
435  
436 proto.addDisposer = function addDisposer (emitter, events) {
437 if (!this._options.disposers) {
438 this._options.disposers = [];
439 }
440 this._options.disposers.push({
441 emitter: emitter,
442 events: events
443 });
444 return this;
445 };
446 }