This repository has been archived by the owner on Jul 28, 2020. It is now read-only.
/
export-canvas.js
152 lines (137 loc) · 5.19 KB
/
export-canvas.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Load in our dependencies
var assert = require('assert');
var path = require('path');
var spawn = require('child_process').spawn;
var async = require('async');
var Tempfile = require('temporary/lib/file');
var phantomLocation = require('which').sync('phantomjs');
var ContentStream = require('contentstream');
var jpegJs = require('jpeg-js');
var ndarray = require('ndarray');
var savePixels = require('save-pixels');
var through2 = require('through2');
/**
* PhantomJS exporter
* @param {Object} options Options to export with
* @param {Number} [options.quality] Quality of the exported item (jpeg only)
*/
var defaultFormat = 'png';
var supportedFormats = ['jpg', 'jpeg', 'png'];
function exportCanvas(options) {
// Determine the export format
var format = options.format || defaultFormat;
assert(supportedFormats.indexOf(format) !== -1,
'`phantomjssmith` doesn\'t support exporting "' + format + '". Please use "jpeg" or "png"');
// Localize context for later and create common variables
var that = this;
var params, tmp, stdout, stderr, code;
// Generate a stream to return synchronously
var retStream = through2();
// In series
async.waterfall([
function createTmpFile (cb) {
// Convert over all image paths to url paths
var images = that.images;
images.forEach(function getUrlPath (img) {
img = img.img;
img._urlpath = path.relative(__dirname + '/scripts', img._filepath);
});
// Collect our parameters
params = that.params;
params.images = images;
params.options = options;
// Stringify our argument for phantomjs
var arg = JSON.stringify(params);
var encodedArg = encodeURIComponent(arg);
// Write out argument to temporary file -- streams weren't cutting it
tmp = new Tempfile();
tmp.writeFile(encodedArg, 'utf8', cb);
},
function spawnPhantomJS (cb) {
// Create a child process for phantomjs
var phantomjs = spawn(phantomLocation, [path.join(__dirname, 'scripts', 'compose.js'), tmp.path]);
phantomjs.on('error', cb);
// When there is data, save it
// DEV: encodedPixels is an array of rgba values
stdout = '';
phantomjs.stdout.on('data', function handleStdout (buffer) {
stdout += buffer.toString();
});
// When there is an error, concatenate it
stderr = '';
phantomjs.stderr.on('data', function handleData (buffer) {
// Ignore PhantomJS 1.9.2 OSX errors
// https://github.com/Ensighten/grunt-spritesmith/issues/33
var bufferStr = buffer.toString();
var isNot192OSXError = bufferStr.indexOf('WARNING: Method userSpaceScaleFactor') === -1;
var isNotPerformanceNote = bufferStr.indexOf('CoreText performance note:') === -1;
if (isNot192OSXError && isNotPerformanceNote) {
stderr += bufferStr;
}
});
// When we are done, save the code and continue
phantomjs.on('close', function handleClose (_code) {
code = _code;
cb();
});
},
function deleteTmpFile (cb) {
// Destroy the temporary file
tmp.unlink(cb);
},
function handleResult (cb) {
// If we received a non-zero exit code, complain and leave
if (code !== 0) {
var err = new Error('Received non-zero exit code "' + code + '" from PhantomJS. stdout:' + stdout);
return cb(err);
}
// Otherwise, decode the pixel values
// DEV: This used to be thinner and not need padding but Windows was messing up the image
var encodedPixels = stdout;
var decodedPixels;
try {
decodedPixels = JSON.parse(encodedPixels);
} catch (e) {
return cb(new Error('Error while parsing JSON "' + encodedPixels + '".\n' + e.message));
}
// If we are dealing with a `jpeg`, then use `jpeg-js`
// DEV: This is to support `quality` which `save-pixels` doesn't
var resultStream;
if (['jpg', 'jpeg'].indexOf(format) !== -1) {
// Encode our data via `jpeg-js` and callback with its internal buffer
var jpg;
try {
jpg = jpegJs.encode({
data: decodedPixels,
width: params.width,
height: params.height
}, options.quality);
} catch (err) {
return cb(err);
}
resultStream = new ContentStream(jpg.data);
// Otherwise, leverage `save-pixels`
} else {
// Convert the pixels into an ndarray
// Taken from https://github.com/mikolalysenko/get-pixels/blob/2ac98645119244d6e52afcef5fe52cc9300fb27b/dom-pixels.js#L14
var imgNdarray = ndarray(decodedPixels,
[params.height, params.width, 4], [4 * params.width, 4, 1], 0);
// Generate our result stream
resultStream = savePixels(imgNdarray, format);
}
// Pipe the stream into the returned result and handle errors
resultStream.on('error', function forwardError (err) {
retStream.emit('error', err);
});
resultStream.pipe(retStream);
}
], function handleError (err) {
// If there was an error, emit it
if (err) {
retStream.emit('error', err);
}
});
// Return our generated stream
return retStream;
}
module.exports = exportCanvas;