corrade-nucleus-nucleons – Blame information for rev 36

Subversion Repositories:
Rev:
Rev Author Line No. Line
36 office 1 /**
2 * Extend proto.
3 */
4  
5 module.exports = function (gm) {
6  
7 var proto = gm.prototype;
8  
9 /**
10 * `identify` states
11 */
12  
13 const IDENTIFYING = 1;
14 const IDENTIFIED = 2;
15  
16 /**
17 * Map getter functions to output names.
18 *
19 * - format: specifying the -format argument (see man gm)
20 * - verbose: use -verbose instead of -format (only if necessary b/c its slow)
21 * - helper: use the conversion helper
22 */
23  
24 var map = {
25 'format': { key: 'format', format: '%m ', helper: 'Format' }
26 , 'depth': { key: 'depth', format: '%q' }
27 , 'filesize': { key: 'Filesize', format: '%b' }
28 , 'size': { key: 'size', format: '%wx%h ', helper: 'Geometry' }
29 , 'color': { key: 'color', format: '%k', helper: 'Colors' }
30 , 'orientation': { key: 'Orientation', format: '%[EXIF:Orientation]', helper: 'Orientation' }
31 , 'res': { key: 'Resolution', verbose: true }
32 }
33  
34 /**
35 * Getter functions
36 */
37  
38 Object.keys(map).forEach(function (getter) {
39 proto[getter] = function (opts, callback) {
40 if (!callback) callback = opts, opts = {};
41 if (!callback) return this;
42  
43 var val = map[getter]
44 , key = val.key
45 , self = this;
46  
47 if (self.data[key]) {
48 callback.call(self, null, self.data[key]);
49 return self;
50 }
51  
52 self.on(getter, callback);
53  
54 self.bufferStream = !!opts.bufferStream;
55  
56 if (val.verbose) {
57 self.identify(opts, function (err, stdout, stderr, cmd) {
58 if (err) {
59 self.emit(getter, err, self.data[key], stdout, stderr, cmd);
60 } else {
61 self.emit(getter, err, self.data[key]);
62 }
63 });
64 return self;
65 }
66  
67 var args = makeArgs(self, val);
68 self._exec(args, function (err, stdout, stderr, cmd) {
69 if (err) {
70 self.emit(getter, err, self.data[key], stdout, stderr, cmd);
71 return;
72 }
73  
74 var result = (stdout||'').trim();
75  
76 if (val.helper in helper) {
77 helper[val.helper](self.data, result);
78 } else {
79 self.data[key] = result;
80 }
81  
82 self.emit(getter, err, self.data[key]);
83 });
84  
85 return self;
86 }
87 });
88  
89 /**
90 * identify command
91 *
92 * Overwrites all internal data with the parsed output
93 * which is more accurate than the fast shortcut
94 * getters.
95 */
96  
97 proto.identify = function identify (opts, callback) {
98 // identify with pattern
99 if (typeof(opts) === 'string') {
100 opts = {
101 format: opts
102 }
103 }
104 if (!callback) callback = opts, opts = {};
105 if (!callback) return this;
106 if (opts && opts.format) return identifyPattern.call(this, opts, callback);
107  
108 var self = this;
109  
110 if (IDENTIFIED === self._identifyState) {
111 callback.call(self, null, self.data);
112 return self;
113 }
114  
115 self.on('identify', callback);
116  
117 if (IDENTIFYING === self._identifyState) {
118 return self;
119 }
120  
121 self._identifyState = IDENTIFYING;
122  
123 self.bufferStream = !!opts.bufferStream;
124  
125 var args = makeArgs(self, { verbose: true });
126  
127 self._exec(args, function (err, stdout, stderr, cmd) {
128 if (err) {
129 self.emit('identify', err, self.data, stdout, stderr, cmd);
130 return;
131 }
132  
133 err = parse(stdout, self);
134  
135 if (err) {
136 self.emit('identify', err, self.data, stdout, stderr, cmd);
137 return;
138 }
139  
140 self.data.path = self.source;
141  
142 self.emit('identify', null, self.data);
143 self._identifyState = IDENTIFIED;
144 });
145  
146 return self;
147 }
148  
149  
150 /**
151 * identify with pattern
152 *
153 * Execute `identify -format` with custom pattern
154 */
155  
156 function identifyPattern (opts, callback) {
157 var self = this;
158  
159 self.bufferStream = !!opts.bufferStream;
160  
161 var args = makeArgs(self, opts);
162 self._exec(args, function (err, stdout, stderr, cmd) {
163 if (err) {
164 return callback.call(self, err, undefined, stdout, stderr, cmd);
165 }
166  
167 callback.call(self, err, (stdout||'').trim());
168 });
169  
170 return self;
171 }
172  
173  
174 /**
175 * Parses `identify` responses.
176 *
177 * @param {String} stdout
178 * @param {Gm} self
179 * @return {Error} [optionally]
180 */
181  
182 function parse (stdout, self) {
183 // normalize
184 var parts = (stdout||"").trim().replace(/\r\n|\r/g, "\n").split("\n");
185  
186 // skip the first line (its just the filename)
187 parts.shift();
188  
189 try {
190 var len = parts.length
191 , rgx1 = /^( *)(.+?): (.*)$/ // key: val
192 , rgx2 = /^( *)(.+?):$/ // key: begin nested object
193 , out = { indent: {} }
194 , level = null
195 , lastkey
196 , i = 0
197 , res
198 , o
199  
200 for (; i < len; ++i) {
201 res = rgx1.exec(parts[i]) || rgx2.exec(parts[i]);
202 if (!res) continue;
203  
204 var indent = res[1].length
205 , key = res[2] ? res[2].trim() : '';
206  
207 if ('Image' == key || 'Warning' == key) continue;
208  
209 var val = res[3] ? res[3].trim() : null;
210  
211 // first iteration?
212 if (null === level) {
213 level = indent;
214 o = out.root = out.indent[level] = self.data;
215 } else if (indent < level) {
216 // outdent
217 if (!(indent in out.indent)) {
218 continue;
219 }
220 o = out.indent[indent];
221 } else if (indent > level) {
222 // dropping into a nested object
223 out.indent[level] = o;
224 // weird format, key/val pair with nested children. discard the val
225 o = o[lastkey] = {};
226 }
227  
228 level = indent;
229  
230 if (val) {
231 // if previous key was exist and we got the same key
232 // cast it to an array.
233 if(o.hasOwnProperty(key)){
234 // cast it to an array and dont forget the previous value
235 if(!Array.isArray(o[key])){
236 var tmp = o[key];
237 o[key] = [tmp];
238 }
239  
240 // set value
241 o[key].push(val);
242 } else {
243 o[key] = val;
244 }
245  
246 if (key in helper) {
247 helper[key](o, val);
248 }
249 }
250  
251 lastkey = key;
252 }
253  
254 } catch (err) {
255 err.message = err.message + "\n\n Identify stdout:\n " + stdout;
256 return err;
257 }
258 }
259  
260 /**
261 * Create an argument array for the identify command.
262 *
263 * @param {gm} self
264 * @param {Object} val
265 * @return {Array}
266 */
267  
268 function makeArgs (self, val) {
269 var args = [
270 'identify'
271 , '-ping'
272 ];
273  
274 if (val.format) {
275 args.push('-format', val.format);
276 }
277  
278 if (val.verbose) {
279 args.push('-verbose');
280 }
281  
282 args = args.concat(self.src());
283 return args;
284 }
285  
286 /**
287 * Map exif orientation codes to orientation names.
288 */
289  
290 var orientations = {
291 '1': 'TopLeft'
292 , '2': 'TopRight'
293 , '3': 'BottomRight'
294 , '4': 'BottomLeft'
295 , '5': 'LeftTop'
296 , '6': 'RightTop'
297 , '7': 'RightBottom'
298 , '8': 'LeftBottom'
299 }
300  
301 /**
302 * identify -verbose helpers
303 */
304  
305 var helper = gm.identifyHelpers = {};
306  
307 helper.Geometry = function Geometry (o, val) {
308 // We only want the size of the first frame.
309 // Each frame is separated by a space.
310 var split = val.split(" ").shift().split("x");
311 var width = parseInt(split[0], 10);
312 var height = parseInt(split[1], 10);
313 if (o.size && o.size.width && o.size.height) {
314 if (width > o.size.width) o.size.width = width;
315 if (height > o.size.height) o.size.height = height;
316 } else {
317 o.size = {
318 width: width,
319 height: height
320 }
321 }
322 };
323  
324 helper.Format = function Format (o, val) {
325 o.format = val.split(" ")[0];
326 };
327  
328 helper.Depth = function Depth (o, val) {
329 o.depth = parseInt(val, 10);
330 };
331  
332 helper.Colors = function Colors (o, val) {
333 o.color = parseInt(val, 10);
334 };
335  
336 helper.Orientation = function Orientation (o, val) {
337 if (val in orientations) {
338 o['Profile-EXIF'] || (o['Profile-EXIF'] = {});
339 o['Profile-EXIF'].Orientation = val;
340 o.Orientation = orientations[val];
341 } else {
342 o.Orientation = val || 'Unknown';
343 }
344 };
345 }
346