diff --git a/examples/fromFile.js b/examples/fromFile.js
new file mode 100644
index 00000000..a8211327
--- /dev/null
+++ b/examples/fromFile.js
@@ -0,0 +1,26 @@
+var SVGO = require('../lib/svgo'),
+ svgo = new SVGO(/*{ custom config object }*/);
+
+svgo
+ // optimize SVG file
+ .fromFile('../examples/test.svg')
+ // get optimized result
+ .then(function(min) {
+
+ console.log(min);
+ // output:
+ // {
+ // // optimized SVG data string
+ // data: ''
+ // // additional info such as width/height and start/end bytes length
+ // info: {
+ // width: '10',
+ // height: '20',
+ // startBytes: 59,
+ // endBytes: 38
+ // }
+ // }
+
+ })
+ // end promises chain
+ .done();
diff --git a/examples/fromStream.js b/examples/fromStream.js
new file mode 100644
index 00000000..4872765a
--- /dev/null
+++ b/examples/fromStream.js
@@ -0,0 +1,31 @@
+var FS = require('fs'),
+ PATH = require('path'),
+ path = PATH.resolve(__dirname, '../examples/test.svg'),
+ fileStream = FS.createReadStream(path, { encoding: 'utf8' });
+
+var SVGO = require('../lib/svgo'),
+ svgo = new SVGO(/*{ custom config object }*/);
+
+svgo
+ // optimize stream
+ .fromStream(fileStream)
+ // get optimized result
+ .then(function(min) {
+
+ console.log(min);
+ // output:
+ // {
+ // // optimized SVG data string
+ // data: ''
+ // // additional info such as width/height and start/end bytes length
+ // info: {
+ // width: '10',
+ // height: '20',
+ // startBytes: 59,
+ // endBytes: 38
+ // }
+ // }
+
+ })
+ // end promises chain
+ .done();
diff --git a/examples/fromString.js b/examples/fromString.js
new file mode 100644
index 00000000..88fa42f2
--- /dev/null
+++ b/examples/fromString.js
@@ -0,0 +1,26 @@
+var SVGO = require('../lib/svgo'),
+ svgo = new SVGO(/*{ custom config object }*/);
+
+svgo
+ // optimize SVG data string
+ .fromString('')
+ // get optimized result
+ .then(function(min) {
+
+ console.log(min);
+ // output:
+ // {
+ // // optimized SVG data string
+ // data: ''
+ // // additional info such as width/height and start/end bytes length
+ // info: {
+ // width: '10',
+ // height: '20',
+ // startBytes: 52,
+ // endBytes: 38
+ // }
+ // }
+
+ })
+ // end promises chain
+ .done();
diff --git a/examples/test.svg b/examples/test.svg
new file mode 100644
index 00000000..c539cd40
--- /dev/null
+++ b/examples/test.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/svgo.js b/lib/svgo.js
index 3a84c13c..6e08469d 100644
--- a/lib/svgo.js
+++ b/lib/svgo.js
@@ -11,10 +11,14 @@
*/
var INHERIT = require('inherit'),
+ Q = require('q'),
+ FS = require('fs'),
+ PATH = require('path'),
CONFIG = require('./svgo/config'),
SVG2JS = require('./svgo/svg2js'),
PLUGINS = require('./svgo/plugins'),
- JS2SVG = require('./svgo/js2svg');
+ JS2SVG = require('./svgo/js2svg'),
+ decodeSVGDatauri = require('./svgo/tools').decodeSVGDatauri;
/**
* @class SVGO.
@@ -34,27 +38,61 @@ module.exports = INHERIT(/** @lends SVGO.prototype */{
},
- /**
- * Main optimize function.
- *
- * @param {String} svgdata input data
- *
- * @return {String} output data deferred promise
- */
- optimize: function(svgdata) {
+ fromString: function(str) {
+
+ str = decodeSVGDatauri(str);
return this.config
.then(function(config) {
- return SVG2JS(svgdata, config.svg2js)
+ return SVG2JS(str, config.svg2js)
.then(function(jsdata) {
- return JS2SVG(PLUGINS(jsdata, config.plugins), config.js2svg);
+ var out = JS2SVG(PLUGINS(jsdata, config.plugins), config.js2svg);
+
+ out.info.startBytes = Buffer.byteLength(str, 'utf-8');
+ out.info.endBytes = Buffer.byteLength(out.data, 'utf-8');
+
+ return out;
});
});
+ },
+
+ fromStream: function(stream) {
+
+ var deferred = Q.defer(),
+ inputData = [],
+ self = this;
+
+ stream.pause();
+
+ stream
+ .on('data', function(chunk) {
+ inputData.push(chunk);
+ })
+ .once('end', function() {
+ deferred.resolve(inputData.join());
+ })
+ .resume();
+
+ return deferred.promise
+ .then(function(str) {
+
+ return self.fromString(str);
+
+ });
+
+ },
+
+ fromFile: function(path) {
+
+ path = PATH.resolve(__dirname, path);
+
+ return this.fromStream(FS.createReadStream(path, { encoding: 'utf8' }));
+
}
});
diff --git a/lib/svgo/coa.js b/lib/svgo/coa.js
index 58ae41a2..c153e088 100644
--- a/lib/svgo/coa.js
+++ b/lib/svgo/coa.js
@@ -1,9 +1,8 @@
-var Q = require('q'),
- FS = require('fs'),
+var FS = require('fs'),
UTIL = require('util'),
SVGO = require('../svgo'),
info = JSON.parse(require('fs').readFileSync(__dirname + '/../../package.json')),
- datauriPrefix = 'data:image/svg+xml;base64,';
+ encodeSVGDatauri = require('./tools').encodeSVGDatauri;
/**
* Command-Option-Argument.
@@ -26,17 +25,19 @@ module.exports = require('coa').Cmd()
})
.end()
.opt()
- .name('input').title('Input: stdin (default) | filename | Data URI base64 string')
+ .name('input').title('Input file, "-" for STDIN')
.short('i').long('input')
- .def(process.stdin)
.val(function(val) {
return val || this.reject('Option --input must have a value.');
})
.end()
.opt()
- .name('output').title('Output: stdout (default) | filename')
+ .name('string').title('Input SVG data string')
+ .short('s').long('string')
+ .end()
+ .opt()
+ .name('output').title('Output file (by default the same as the input), "-" for STDOUT')
.short('o').long('output')
- .def(process.stdout)
.val(function(val) {
return val || this.reject('Option --output must have a value.');
})
@@ -74,11 +75,6 @@ module.exports = require('coa').Cmd()
.long('pretty')
.flag()
.end()
- .opt()
- .name('test').title('Make a visual comparison of two files (PhantomJS pre-required)')
- .long('test')
- .flag()
- .end()
.arg()
.name('input').title('Alias to --input')
.end()
@@ -89,123 +85,71 @@ module.exports = require('coa').Cmd()
var input = args && args.input ? args.input : opts.input,
output = args && args.output ? args.output : opts.output,
- inputData = [],
- deferred = Q.defer(),
+ string = opts.string,
startTime = Date.now(),
- endTime,
- startBytes,
- endBytes;
+ svgo;
- // https://github.com/joyent/node/issues/2130
- process.stdin.pause();
+ if (
+ (!input || input === '-') &&
+ !string &&
+ !opts.stdin &&
+ process.stdin.isTTY
+ ) return this.usage();
- // if value is a string and not a Data URI string
- if (typeof input === 'string' && !isDatauri(input)) {
- // then create stream
- input = FS.createReadStream(input, { encoding: 'utf8' });
- input.pause();
+ // --string
+ if (string) {
+ svgo = new SVGO({ coa: opts }).fromString(string);
}
- // if run as just 'svgo' display help and exit
- if (input.isTTY) return this.usage();
-
- // datauri base64 string
- if (typeof input === 'string') {
- deferred.resolve(convertDatauriInput(input));
- // stdin or file stream
- } else {
- input
- .on('data', function(chunk) {
- inputData.push(chunk);
- })
- .once('end', function() {
- deferred.resolve(convertDatauriInput(inputData.join()));
- })
- .resume();
+ // STDIN
+ else if (input === '-') {
+ svgo = new SVGO({ coa: opts }).fromStream(process.stdin);
}
- return deferred.promise
- .then(function(svg) {
- startBytes = Buffer.byteLength(svg, 'utf-8');
+ // file
+ else if (input) {
+ svgo = new SVGO({ coa: opts }).fromFile(input);
+ }
- return new SVGO({ coa: opts }).optimize(svg);
- })
- .then(function(svgmin) {
- endTime = Date.now();
- endBytes = Buffer.byteLength(svgmin.data, 'utf-8');
+ return svgo.then(function(svgmin) {
- // --datauri
- if (opts.datauri) {
- // convert to Data URI base64 string
- svgmin.data = datauriPrefix + new Buffer(svgmin.data).toString('base64');
- }
-
- if (typeof output === 'string') {
- output = FS.createWriteStream(output, { encoding: 'utf8' });
+ // --datauri
+ if (opts.datauri) {
+ // convert to Data URI base64 string
+ svgmin.data = encodeSVGDatauri(svgmin.data);
+ }
+
+ // stdout
+ if (output === '-' || (input === '-' && !output)) {
+ process.stdout.write(svgmin.data + '\n');
+ }
+
+ // file
+ else {
+
+ // if input is from file - overwrite it
+ if (!output && input) {
+ output = input;
}
+ // output file
+ output = FS.createWriteStream(output, { encoding: 'utf8' });
output.write(svgmin.data);
+ output.end();
- if (output === process.stdout) {
- output.write('\n');
- } else {
- output.end();
+ // print time info
+ printTimeInfo(startTime, Date.now());
- // print time info
- printTimeInfo(startTime, endTime);
+ // print optimization profit info
+ printProfitInfo(svgmin.info.startBytes, svgmin.info.endBytes);
- // print optimization profit info
- printProfitInfo(startBytes, endBytes);
+ }
- // --test
- if (opts.test) {
- // make a visual comparison of two files with PhantomJS and print info
- printPhantomTestInfo(
- input.path,
- output.path,
- svgmin.info.width || 500,
- svgmin.info.height || 500
- );
- }
- }
-
- })
- .done();
+ })
+ .done();
});
-/**
- * Check if string is a Data URI base64.
- *
- * @param {String} str input string
- *
- * @return {Boolean}
- */
-function isDatauri(str) {
-
- return str.substring(0, 26) === datauriPrefix;
-
-}
-
-/**
- * Convert Data URI base64 string to plain SVG.
- *
- * @param {String} str input string
- *
- * @return {String} output string
- */
-function convertDatauriInput(str) {
-
- // if datauri base64 string
- if (isDatauri(str)) {
- // then convert
- str = new Buffer(str.substring(26), 'base64').toString('utf8');
- }
-
- return str;
-
-}
-
/**
* Print time info.
*
@@ -235,40 +179,3 @@ function printProfitInfo(inBytes, outBytes) {
);
}
-
-/**
- * Make a visual comparison of two files with PhantomJS and print info.
- *
- * @param {String} file1 file1 path
- * @param {String} file2 file2 path
- * @param {Number} width width
- * @param {Number} height height
- */
-function printPhantomTestInfo(file1, file2, width, height) {
-
- var PHANTOM = require('./phantom');
-
- UTIL.print('Visual comparison: ');
-
- PHANTOM.test(file1, file2, width, height)
- .then(function(code) {
-
- var answer;
-
- if (code === 1) {
- answer = 'OK';
- } else if (code === 2) {
- answer = 'Oops, files are not visually identical!\n\nIf you do not see any visual differences with your eyes then ALL IS OK and there are only very minor floating-point numbers rounding errors in some browsers.\nBut if you see significant errors then things are bad :), please create an issue at https://github.com/svg/svgo/issues';
- } else if (code === 3) {
- answer = 'Error while rendering SVG\nPlease create an issue at https://github.com/svg/svgo/issues';
- } else if (code === 127) {
- answer = 'Error, you need to install PhantomJS first http://phantomjs.org/download.html';
- } else {
- answer = 'Error, something went wrong\nPlease create an issue at https://github.com/svg/svgo/issues';
- }
-
- UTIL.print(answer + '\n\n');
-
- });
-
-}
diff --git a/lib/svgo/phantom.js b/lib/svgo/phantom.js
deleted file mode 100644
index a6a3d6dd..00000000
--- a/lib/svgo/phantom.js
+++ /dev/null
@@ -1,30 +0,0 @@
-var PATH = require('path'),
- Q = require('Q'),
- spawn = require('child_process').spawn;
-
-/**
- * Two SVG files rendering and visually comparing with PhantomJS.
- *
- * @see http://phantomjs.org/
- * @see /lib/phantom_test.js
- *
- * @param {String} file1 file1
- * @param {String} file2 file2
- * @param {Number} width viewport width
- * @param {Number} height viewport height
- *
- * @return {Object} deferred promise with exit code
- */
-exports.test = function(file1, file2, width, height) {
-
- var phantomTestFile = PATH.resolve(__dirname, './phantom_test.js'),
- phantom = spawn('phantomjs', [phantomTestFile, file1, file2, width, height]),
- deferred = Q.defer();
-
- phantom.on('exit', function(code) {
- deferred.resolve(code);
- });
-
- return deferred.promise;
-
-};
diff --git a/lib/svgo/phantom_test.js b/lib/svgo/phantom_test.js
deleted file mode 100644
index 0a4cfb46..00000000
--- a/lib/svgo/phantom_test.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Two SVG files rendering and visually comparing with PhantomJS.
- *
- * @see http://phantomjs.org/
- * @see https://github.com/ariya/phantomjs/wiki/Screen-Capture
- */
-var page = require('webpage').create(),
- file1 = phantom.args[0],
- file2 = phantom.args[1],
- width = phantom.args[2],
- height = phantom.args[3];
-
-page.viewportSize = {
- width: width,
- height: height
-};
-
-open(file1, function(out1) {
- open(file2, function(out2) {
- if (out1.length === out2.length && out1 === out2) {
- phantom.exit(1);
- } else {
- phantom.exit(2);
- }
- });
-});
-
-function open(file, callback) {
-
- page.open(file, function(status) {
-
- if (status !== 'success') {
- phantom.exit(3);
- } else {
- callback(page.renderBase64('PNG'));
- }
-
- });
-
-}
diff --git a/lib/svgo/svg2js.js b/lib/svgo/svg2js.js
index 9fed6eee..7fd6407e 100644
--- a/lib/svgo/svg2js.js
+++ b/lib/svgo/svg2js.js
@@ -8,12 +8,12 @@ var UTIL = require('util'),
*
* @module svg2js
*
- * @param {String} svgdata input data
+ * @param {String} svg input data
* @param {Object} config sax xml parser config
*
* @return {Object} output data deferred promise
*/
-module.exports = function(svgdata, config) {
+module.exports = function(svg, config) {
var deferred = Q.defer(),
sax = SAX.parser(config.strict, config),
@@ -119,7 +119,7 @@ module.exports = function(svgdata, config) {
};
- sax.write(svgdata).close();
+ sax.write(svg).close();
return deferred.promise;
diff --git a/lib/svgo/tools.js b/lib/svgo/tools.js
index a7d0f03e..39d41517 100644
--- a/lib/svgo/tools.js
+++ b/lib/svgo/tools.js
@@ -1,3 +1,33 @@
+var datauriSVGPrefix = exports.datauriSVGPrefix = 'data:image/svg+xml;base64,';
+
+/**
+ * Encode plain SVG data string into Data URI base64 string.
+ *
+ * @param {String} str input string
+ *
+ * @return {String} output string
+ */
+exports.encodeSVGDatauri = function(str) {
+
+ return datauriSVGPrefix + new Buffer(str).toString('base64');
+
+};
+
+/**
+ * Decode SVG Data URI base64 string into plain SVG data string.
+ *
+ * @param {string} str input string
+ *
+ * @return {String} output string
+ */
+exports.decodeSVGDatauri = function(str) {
+
+ if (str.substring(0, 26) !== datauriSVGPrefix) return str;
+
+ return new Buffer(str.substring(26), 'base64').toString('utf8');
+
+};
+
exports.extend = require('node.extend');
exports.flattenOneLevel = function(array) {
diff --git a/test/plugins/_index.js b/test/plugins/_index.js
index 3f562826..6d09fc7c 100644
--- a/test/plugins/_index.js
+++ b/test/plugins/_index.js
@@ -66,7 +66,7 @@ function getResult(name, index) {
.then(function(input) {
mySVGO.enableOnlyOne(name);
- return mySVGO.optimize(input.toString());
+ return mySVGO.fromString(input.toString());
})
.then(function(min) {
return readFile(name + '.' + index +'.should.svg')