Socket
Socket
Sign inDemoInstall

svgo

Package Overview
Dependencies
3
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.8 to 0.0.9

plugins/README.md

16

CHANGELOG.md

@@ -0,1 +1,17 @@

### [ [>](//github.com/svg/svgo/tree/v0.0.9) ] 0.0.9 / 29.10.2012
* [plugins how-to](https://github.com/svg/svgo/tree/master/plugins#readme) (close [#27](https://github.com/svg/svgo/issues/27))
* allow any plugin of any type to go in any order (close [#14](https://github.com/svg/svgo/issues/14))
* allow to do a multiple optimizations with one init (close [#25](https://github.com/svg/svgo/issues/25))
* plugins/convertPathData: global refactoring
* plugins/convertPathData: do all the tricks with absolute coords too (fix [#22](https://github.com/svg/svgo/issues/22))
* plugins/convertPathData: accumulation of rounding errors (fix [#23](https://github.com/svg/svgo/issues/23))
* plugins/convertPathData: prevent an infinity loop on invalid path data (fix [#26](https://github.com/svg/svgo/issues/26))
* plugins/convertPathData: do not remove very first M from the path data (fix [#24](https://github.com/svg/svgo/issues/24))
* plugins/convertPathData: optimize path data in <glyph> and <missing-glyph> (close [#20](https://github.com/svg/svgo/issues/20))
* plugins/convertTransform: add patternTransform attribute to the process (close [#15](https://github.com/svg/svgo/issues/15))
* plugins/convertTransform: Firefox: removing extra space in front of negative number is alowed only in path data, but not in transform (fix [#12](https://github.com/svg/svgo/issues/12))
* plugins/removeXMLProcInst: remove only 'xml' but not 'xml-stylesheet' (fix [#21](https://github.com/svg/svgo/issues/15))
* plugins/collapseGroups: merge split-level transforms (fix [#13](https://github.com/svg/svgo/issues/13))
* jsdoc corrections
### [ [>](//github.com/svg/svgo/tree/v0.0.8) ] 0.0.8 / 20.10.2012

@@ -2,0 +18,0 @@ * new plugin [convertTransform](plugins/convertTransform.js) (close [#5](https://github.com/svg/svgo/issues/5))

265

config.json

@@ -32,130 +32,145 @@ {

},
"plugins": {
"directPass": [
{
"name": "removeDoctype",
"active": true
},{
"name": "removeXMLProcInst",
"active": true
},{
"name": "removeComments",
"active": true
},{
"name": "removeMetadata",
"active": true
},{
"name": "removeEditorsNSData",
"active": true
},{
"name": "convertStyleToAttrs",
"active": true
},{
"name": "cleanupAttrs",
"active": true,
"params": {
"newlines": true,
"trim": true,
"spaces": true
}
},{
"name": "removeDefaultPx",
"active": true
},{
"name": "cleanupSVGElem",
"active": true,
"params": {
"id": true,
"version": true,
"xmlspace": true
}
},{
"name": "removeViewBox",
"active": true
},{
"name": "cleanupEnableBackground",
"active": true
},{
"name": "removeHiddenElems",
"active": true,
"params": {
"displayNone": true,
"opacity0": true,
"circleR0": true,
"ellipseRX0": true,
"ellipseRY0": true,
"rectWidth0": true,
"rectHeight0": true,
"patternWidth0": true,
"patternHeight0": true,
"imageWidth0": true,
"imageHeight0": true,
"pathEmptyD": true,
"polylineEmptyPoints": true,
"polygonEmptyPoints": true
}
},{
"name": "removeEmptyText",
"active": true,
"params": {
"text": true,
"tspan": true,
"tref": true
}
},{
"name": "convertColors",
"active": true,
"params": {
"names2hex": true,
"rgb2hex": true,
"shorthex": true
}
},{
"name": "convertPathData",
"active": true,
"params": {
"convertToRelative": true,
"lineShorthands": true,
"floatPrecision": 3,
"removeUseless": true,
"collapseRepeated": true,
"leadingZero": true
}
},{
"name": "convertTransform",
"active": true,
"params": {
"convertToShorts": true,
"floatPrecision": 3,
"matrixToTransform": true,
"shortTranslateScale": true,
"shortRotate": true,
"removeUseless": true,
"collapseIntoOne": true,
"leadingZero": true
}
},{
"name": "removeEmptyAttrs",
"active": true
"plugins": [
{
"name": "removeDoctype",
"active": true,
"type": "perItem"
},{
"name": "removeXMLProcInst",
"active": true,
"type": "perItem"
},{
"name": "removeComments",
"active": true,
"type": "perItem"
},{
"name": "removeMetadata",
"active": true,
"type": "perItem"
},{
"name": "removeEditorsNSData",
"active": true,
"type": "perItem"
},{
"name": "convertStyleToAttrs",
"active": true,
"type": "perItem"
},{
"name": "cleanupAttrs",
"active": true,
"type": "perItem",
"params": {
"newlines": true,
"trim": true,
"spaces": true
}
],
"reversePass": [
{
"name": "removeEmptyContainers",
"active": true
},{
"name": "moveElemsAttrsToGroup",
"active": true
},{
"name": "collapseGroups",
"active": true
},{
"name": "removeDefaultPx",
"active": true,
"type": "perItem"
},{
"name": "cleanupSVGElem",
"active": true,
"type": "perItem",
"params": {
"id": true,
"version": true,
"xmlspace": true
}
],
"full": [
{
"name": "removeUnusedNS",
"active": true
},{
"name": "removeViewBox",
"active": true,
"type": "perItem"
},{
"name": "cleanupEnableBackground",
"active": true,
"type": "perItem"
},{
"name": "removeHiddenElems",
"active": true,
"type": "perItem",
"params": {
"displayNone": true,
"opacity0": true,
"circleR0": true,
"ellipseRX0": true,
"ellipseRY0": true,
"rectWidth0": true,
"rectHeight0": true,
"patternWidth0": true,
"patternHeight0": true,
"imageWidth0": true,
"imageHeight0": true,
"pathEmptyD": true,
"polylineEmptyPoints": true,
"polygonEmptyPoints": true
}
]
}
},{
"name": "removeEmptyText",
"active": true,
"type": "perItem",
"params": {
"text": true,
"tspan": true,
"tref": true
}
},{
"name": "convertColors",
"active": true,
"type": "perItem",
"params": {
"names2hex": true,
"rgb2hex": true,
"shorthex": true
}
},{
"name": "convertPathData",
"active": true,
"type": "perItem",
"params": {
"convertToRelative": true,
"lineShorthands": true,
"floatPrecision": 3,
"removeUseless": true,
"collapseRepeated": true,
"leadingZero": true,
"negativeExtraSpace": true
}
},{
"name": "moveElemsAttrsToGroup",
"active": true,
"type": "perItemReverse"
},{
"name": "convertTransform",
"active": true,
"type": "perItem",
"params": {
"convertToShorts": true,
"floatPrecision": 3,
"matrixToTransform": true,
"shortTranslateScale": true,
"shortRotate": true,
"removeUseless": true,
"collapseIntoOne": true,
"leadingZero": true,
"negativeExtraSpace": false
}
},{
"name": "removeEmptyAttrs",
"active": true,
"type": "perItem"
},{
"name": "removeEmptyContainers",
"active": true,
"type": "perItemReverse"
},{
"name": "collapseGroups",
"active": true,
"type": "perItemReverse"
},{
"name": "removeUnusedNS",
"active": true,
"type": "full"
}
]
}

@@ -99,3 +99,3 @@ var Q = require('q'),

deferred = Q.defer(),
startTime = new Date(),
startTime = Date.now(),
endTime,

@@ -127,6 +127,6 @@ startBytes,

return SVGO(svg, { coa: options });
return new SVGO({ coa: options }).optimize(svg);
})
.then(function(svgmin) {
endTime = new Date();
endTime = Date.now();
endBytes = Buffer.byteLength(svgmin.data, 'utf-8');

@@ -171,3 +171,4 @@

});
})
.end();

@@ -174,0 +175,0 @@ });

@@ -6,20 +6,44 @@ var QFS = require('q-fs'),

/**
* Read and/or extend default config file.
* Read and/or extend default config file,
* prepare and optimize plugins array.
*
* @module config
*
* @param {Object} [options] options
* @param {Object} [params] config object to extend or coa params
*
* @return {Object} config deferred promise
*/
module.exports = function(options) {
module.exports = function(params) {
return _getConfig(params).then(function(config) {
config.plugins = preparePluginsArray(config.plugins);
config.plugins = optimizePluginsArray(config.plugins);
return config;
});
};
/**
* Get default or extended config.
*
* @param {Object} [params] config object to extend or coa params
*
* @return {Object} default or extended config
*
* @private
*/
function _getConfig(params) {
var defaultConfigPath = PATH.resolve(__dirname, '../config.json');
// if there are no any options then return default config
if (!options) return readConfig(defaultConfigPath);
// if there are no any params then return default config
if (!params) return readConfig(defaultConfigPath);
// COA options
if (options.coa) {
// COA params
if (params.coa) {
options = options.coa;
params = params.coa;

@@ -30,14 +54,14 @@ return readConfig(defaultConfigPath)

// --pretty
if (options.pretty) defaultConfig.js2svg.pretty = true;
if (params.pretty) defaultConfig.js2svg.pretty = true;
// --disable
if (options.disable) return changePluginsState(options.disable, false, defaultConfig);
if (params.disable) return changePluginsState(params.disable, false, defaultConfig);
// --enable
if (options.enable) return changePluginsState(options.enable, true, defaultConfig);
if (params.enable) return changePluginsState(params.enable, true, defaultConfig);
// --config
if (options.config) {
if (params.config) {
var localConfigPath = PATH.resolve(process.cwd, options.config);
var localConfigPath = PATH.resolve(process.cwd, params.config);

@@ -64,3 +88,3 @@ // check for the local config file

// inline {} options
// inline {} params
} else {

@@ -72,4 +96,8 @@

return extend(true, defaultConfig, options);
if (params) {
return extend(true, defaultConfig, params);
}
return defaultConfig;
});

@@ -79,3 +107,3 @@

};
}

@@ -86,6 +114,6 @@ /**

* @param {String} path config path
*
* @return {Object} read config deferred promise
* @private
*/
var readConfig = exports.readConfig = function(path) {
function readConfig(path) {

@@ -97,41 +125,65 @@ return QFS.read(path)

};
}
/**
* Change plugins state by names array.
* Require() all plugins in array and convert it to array of arrays.
*
* @param {Array} names plugins names
* @param {Boolean} state active state
* @param {Object} config original config
* @return {Object} changed config
* @param {Array} plugins input plugins array
*
* @return {Array} input plugins array of arrays
*/
var changePluginsState = exports.changePluginsState = function(names, state, config) {
function preparePluginsArray(plugins) {
getPluginsByNames(names, config).forEach(function(plugin) {
plugin.active = state;
return plugins.map(function(plugin) {
plugin.fn = require('../plugins/' + plugin.name)[plugin.name];
return [plugin];
});
return config;
}
};
/**
* Try to group sequential elements of plugins array.
*
* @param {Object} plugins input plugins array
*
* @return {Array} output plugins array
*/
function optimizePluginsArray(plugins) {
var prev;
plugins = plugins.filter(function(item) {
if (prev && item[0].type === prev[0].type) {
prev.push(item[0]);
return false;
}
prev = item;
return true;
});
return plugins;
}
/**
* Get plugins by names array.
* Change plugins state by names array.
*
* @param {Array} names plugins names
* @param {Object} config config
* @return {Array} plugins array
* @param {Boolean} state active state
* @param {Object} config original config
*
* @return {Object} changed config
*/
var getPluginsByNames = exports.getPluginsByNames = function(names, config) {
function changePluginsState(names, state, config) {
var plugins = [];
config.plugins.forEach(function(plugin) {
if (names.indexOf(plugin.name) > -1) {
plugin.active = state;
}
});
for (var type in config.plugins) {
config.plugins[type].forEach(function(plugin) {
if (names.indexOf(plugin.name) > -1) plugins.push(plugin);
});
}
return config;
return plugins;
};
}

@@ -1,2 +0,3 @@

var INHERIT = require('inherit');
var INHERIT = require('inherit'),
extend = require('./tools').extend;

@@ -10,2 +11,3 @@ /**

* @param {Object} config config
*
* @return {Object} output data

@@ -26,2 +28,3 @@ */

* @constructs
*
* @private

@@ -32,7 +35,7 @@ */

/**
* Converter config.
* Shallow copy of converter config.
*
* @type {Object}
*/
this.config = config;
this.config = extend({}, config);

@@ -64,2 +67,3 @@ /**

* @param {Object} svg-as-js data object
*
* @return {String}

@@ -130,2 +134,3 @@ */

* @param {String} doctype doctype body string
*
* @return {String}

@@ -145,2 +150,3 @@ */

* @param {Object} instruction instruction object
*
* @return {String}

@@ -162,2 +168,3 @@ */

* @param {String} comment comment body
*
* @return {String}

@@ -177,2 +184,3 @@ */

* @param {String} cdata CDATA body
*
* @return {String}

@@ -192,2 +200,3 @@ */

* @param {Object} data element object
*
* @return {String}

@@ -238,2 +247,3 @@ */

* @param {Object} elem attributes object
*
* @return {String}

@@ -264,2 +274,3 @@ */

* @param {String} text text
*
* @return {String}

@@ -277,11 +288,1 @@ */

});
/*
var MyConv = INHERIT(Converter, {
__constructor: function(options) {
this.__base();
}
});
*/

@@ -13,2 +13,3 @@ var INHERIT = require('inherit'),

* @constructs
*
* @private

@@ -27,2 +28,3 @@ */

* @param {String|Array} [param] element name or names arrays
*
* @return {Boolean}

@@ -55,3 +57,4 @@ */

* @param {Function} callback
* @return {Boolean} false if the are no any attributes
*
* @return {Boolean} false if there are no any attributes
*/

@@ -72,7 +75,8 @@ eachAttr: function(callback) {

*
* @param {String|Object} [name] attribute name or object
* @param {String} [name] attribute name
* @param {String} [val] attribute value (will be toString()'ed)
*
* @return {Boolean}
*/
hasAttr: function(attr, val) {
hasAttr: function(name, val) {

@@ -83,11 +87,6 @@ if (!this.attrs || !Object.keys(this.attrs).length) return false;

if (typeof attr === 'object') {
val = attr.value;
attr = attr.name;
}
if (val !== undefined) return !!this.attrs[name] && this.attrs[name].value === val.toString();
if (val !== undefined) return !!this.attrs[attr] && this.attrs[attr].value === val.toString();
return !!this.attrs[name];
return !!this.attrs[attr];
},

@@ -99,5 +98,6 @@

*
* @param {String} [name] attribute name
* @param {String} name attribute name
* @param {String} [val] attribute value (will be toString()'ed)
* @return {Object}
*
* @return {Object|Undefined}
*/

@@ -110,3 +110,3 @@ attr: function(name, val) {

return this.hasAttr(name) && this.attrs[name];
return this.attrs[name];

@@ -118,9 +118,10 @@ },

*
* @param {String|Object} attr attribute name or object
* @param {String} name attribute name
* @param {String} [val] attribute value
*
* @return {Boolean}
*/
removeAttr: function(attr, val) {
removeAttr: function(name, val) {
if (!this.hasAttr(attr)) return false;
if (!this.hasAttr(name, val)) return false;

@@ -132,11 +133,6 @@ if (!arguments.length) {

if (typeof attr === 'object') {
val = attr.value;
attr = attr.name;
}
if (val && this.attrs[name].value !== val) return false;
if (val && this.attrs[attr].value !== val) return false;
delete this.attrs[name];
delete this.attrs[attr];
if (!Object.keys(this.attrs).length) delete this.attrs;

@@ -152,2 +148,4 @@

* @param {Object} attr attribute object
*
* @return {Object} created attribute
*/

@@ -158,3 +156,3 @@ addAttr: function(attr) {

this.attrs[attr.name] = attr;
return (this.attrs[attr.name] = attr);

@@ -161,0 +159,0 @@ }

@@ -15,2 +15,4 @@ var PATH = require('path'),

* @param {Number} height viewport height
*
* @return {Object} deferred promise with exit code
*/

@@ -17,0 +19,0 @@ exports.test = function(file1, file2, width, height) {

@@ -6,4 +6,5 @@ /**

*
* @param {Object} jsdata input data
* @param {Object} data input data
* @param {Object} plugins plugins object from config
*
* @return {Object} output data

@@ -13,6 +14,18 @@ */

data = pass(data, plugins.directPass);
data = pass(data, plugins.reversePass, true);
data = full(data, plugins.full);
plugins.forEach(function(group) {
switch(group[0].type) {
case 'perItem':
data = perItem(data, group);
break;
case 'perItemReverse':
data = perItem(data, group, true);
break;
case 'full':
data = full(data, group);
break;
}
});
return data;

@@ -23,30 +36,12 @@

/**
* Require() all plugins in list.
* Direct or reverse per-item loop.
*
* @param {Array} arr original plugins list
* @return {Array} require'ed plugins list
* @private
*/
function _makePluginsList(plugins) {
return plugins.map(function(plugin) {
plugin.fn = require('../plugins/' + plugin.name)[plugin.name];
return plugin;
});
}
/**
* Direct or reverse pass.
* @param {Object} data input data
* @param {Array} plugins plugins list to process
* @param {Boolean} [reverse] reverse pass?
*
* @param {Object} jsdata input data
* @param {Array} plugins list of the current plugins type
* @param {Boolean} [reverse] reverse pass?
* @return {Object} output data
*/
function pass(data, plugins, reverse) {
function perItem(data, plugins, reverse) {
plugins = _makePluginsList(plugins);
function monkeys(items) {

@@ -96,8 +91,9 @@

*
* @return {[type]} [description]
* @param {Object} data input data
* @param {Array} plugins plugins list to process
*
* @return {Object} output data
*/
function full(data, plugins) {
plugins = _makePluginsList(plugins);
plugins.forEach(function(plugin) {

@@ -104,0 +100,0 @@ if (plugin.active) {

var UTIL = require('util'),
Q = require('q'),
SAX = require('sax'),
JSAPI = require('./jsAPI');
JSAPI = require('./jsAPI'),
extend = require('./tools').extend;

@@ -13,2 +14,3 @@ /**

* @param {Object} config sax xml parser config
*
* @return {Object} output data deferred promise

@@ -18,2 +20,5 @@ */

// shallow copy
config = extend({}, config);
var strict = config.strict;

@@ -20,0 +25,0 @@ delete config.strict;

@@ -1,6 +0,1 @@

var CONFIG = require('./config'),
SVG2JS = require('./svg2js'),
PLUGINS = require('./plugins'),
JS2SVG = require('./js2svg');
/**

@@ -13,6 +8,2 @@ * SVGO is a Nodejs-based tool for optimizing SVG vector graphics files.

*
* @param {String} svgdata input data
* @param {Object} [options] options
* @return {String} output data deferred promise
*
* @author Kir Belevich <kir@soulshine.in> (https://github.com/deepsweet)

@@ -22,16 +13,50 @@ * @copyright © 2012 Kir Belevich

*/
module.exports = function(svgdata, options) {
return CONFIG(options)
.then(function(config) {
var INHERIT = require('inherit'),
CONFIG = require('./config'),
SVG2JS = require('./svg2js'),
PLUGINS = require('./plugins'),
JS2SVG = require('./js2svg');
return SVG2JS(svgdata, config.svg2js)
.then(function(jsdata) {
/**
* @class SVGO.
*/
module.exports = INHERIT(/** @lends SVGO.prototype */{
return JS2SVG(PLUGINS(jsdata, config.plugins), config.js2svg);
/**
* @param {Object} [config] config to extend
*
* @constructs
*
* @private
*/
__constructor: function(config) {
});
this.config = CONFIG(config);
});
},
};
/**
* Main optimize function.
*
* @param {String} svgdata input data
*
* @return {String} output data deferred promise
*/
optimize: function(svgdata) {
return this.config
.then(function(config) {
return SVG2JS(svgdata, config.svg2js)
.then(function(jsdata) {
return JS2SVG(PLUGINS(jsdata, config.plugins), config.js2svg);
});
});
}
});

@@ -43,14 +43,21 @@ exports.extend = require('node.extend');

exports.cleanupOutData = function(data, params) {
var str = '';
var str = '',
delimiter;
data.forEach(function(item, i) {
// there is no delimiter by default
var delimiter = '';
// but if item >= 0 and item index > 0
// then must be a delimiter (space) between items
if (item >= 0 && i > 0) {
delimiter = ' ';
// space delimiter by default
delimiter = ' ';
// no extra space in front of first number
if (i === 0) {
delimiter = '';
}
// no extra space in front of negative number
if (params.negativeExtraSpace && item < 0) {
delimiter = '';
}
// remove floating-point numbers leading zeros

@@ -74,2 +81,3 @@ // 0.5 → .5

return str;
};
{
"name": "svgo",
"version": "0.0.8",
"version": "0.0.9",
"description": "Nodejs-based tool for optimizing SVG vector graphics files",

@@ -5,0 +5,0 @@ "keywords": [

@@ -47,4 +47,6 @@ var flattenOneLevel = require('../lib/tools').flattenOneLevel;

inner.addAttr(attr);
} else if (attr.name === 'transform') {
inner.attr(attr.name).value = attr.value + ' ' + inner.attr(attr.name).value;
}
g.removeAttr(attr);
g.removeAttr(attr.name);
});

@@ -51,0 +53,0 @@ }

var cleanupOutData = require('../lib/tools').cleanupOutData,
regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
regPathData = /(?=-)|[\s,]+/;
regPathData = /(?=-)|[\s,]+/,
pathElems = ['path', 'glyph', 'missing-glyph'];

@@ -23,20 +24,14 @@ /**

if (item.isElem('path') && item.hasAttr('d')) {
if (item.isElem(pathElems) && item.hasAttr('d')) {
var data = path2js(item.attr('d').value);
if (params.convertToRelative) {
if (data.length) {
data = convertToRelative(data, params);
}
if (params.removeUseless) {
data = removeUseless(data);
}
data = filters(data, params);
if (params.collapseRepeated) {
data = collapseRepeated(data);
item.attr('d').value = js2path(data, params);
}
item.attr('d').value = js2path(data, params);
}

@@ -82,21 +77,26 @@

var pair = 0;
// very stupid defense strategy
if (!isNaN(data[0])) {
if ('HhVv'.indexOf(instruction) > -1) {
pair = 1;
} else if ('MmLlTt'.indexOf(instruction) > -1) {
pair = 2;
} else if ('QqSs'.indexOf(instruction) > -1) {
pair = 4;
} else if ('Cc'.indexOf(instruction) > -1) {
pair = 6;
} else if ('Aa'.indexOf(instruction) > -1) {
pair = 7;
}
var pair = 0;
while(data.length) {
path.push({
instruction: instruction,
data: data.splice(0, pair)
});
if ('HhVv'.indexOf(instruction) > -1) {
pair = 1;
} else if ('MmLlTt'.indexOf(instruction) > -1) {
pair = 2;
} else if ('QqSs'.indexOf(instruction) > -1) {
pair = 4;
} else if ('Cc'.indexOf(instruction) > -1) {
pair = 6;
} else if ('Aa'.indexOf(instruction) > -1) {
pair = 7;
}
while(data.length) {
path.push({
instruction: instruction,
data: data.splice(0, pair)
});
}
}

@@ -115,14 +115,18 @@

*
* @param {Object} item input array
* @param {Array} path input path data
* @param {Object} params plugin params
*
* @return {Object} output array
* @return {Array} output path data
*/
function convertToRelative(path, params) {
var point = [0, 0];
var instruction,
data,
newPoint,
point = [0, 0];
path.forEach(function(item) {
var instruction = item.instruction,
data = item.data;
instruction = item.instruction;
data = item.data;

@@ -134,29 +138,30 @@ if (data) {

if ('mcslqta'.indexOf(instruction) > -1) {
var newPoint = data.slice(-2);
// x
newPoint = data.slice(-2);
point[0] += newPoint[0];
// y
point[1] += newPoint[1];
}
if (instruction === 'h') {
// x
point[0] += data[data.length - 1];
}
} else if (instruction === 'h') {
if (instruction === 'v') {
// y
point[1] += data[data.length - 1];
point[0] += data[0];
} else if (instruction === 'v') {
point[1] += data[0];
}
// absolute
// M → m
// L → l
// T → t
if ('MLT'.indexOf(instruction) > -1) {
instruction = instruction.toLowerCase();
// convert absolute path data coordinates to relative
if (params.convertToRelative) {
// x y
// 0 1
// for(i = 0; i < data.length; i += 2) {
// M → m
// L → l
// T → t
if ('MLT'.indexOf(instruction) > -1) {
instruction = instruction.toLowerCase();
// x y
// 0 1
data[0] -= point[0];

@@ -167,12 +172,10 @@ data[1] -= point[1];

point[1] += data[1];
// }
}
// C → c
if (instruction === 'C') {
instruction = 'c';
// C → c
} else if (instruction === 'C') {
// x1 y1 x2 y2 x y
// 0 1 2 3 4 5
// for(i = 0; i < data.length; i += 6) {
instruction = 'c';
// x1 y1 x2 y2 x y
// 0 1 2 3 4 5
data[0] -= point[0];

@@ -187,13 +190,11 @@ data[1] -= point[1];

point[1] += data[5];
// }
}
// S → s
// Q → q
if ('SQ'.indexOf(instruction) > -1) {
instruction = instruction.toLowerCase();
// S → s
// Q → q
} else if ('SQ'.indexOf(instruction) > -1) {
// x1 y1 x y
// 0 1 2 3
// for(i = 0; i < data.length; i += 4) {
instruction = instruction.toLowerCase();
// x1 y1 x y
// 0 1 2 3
data[0] -= point[0];

@@ -206,12 +207,10 @@ data[1] -= point[1];

point[1] += data[3];
// }
}
// A → a
if (instruction === 'A') {
instruction = 'a';
// A → a
} else if (instruction === 'A') {
// rx ry x-axis-rotation large-arc-flag sweep-flag x y
// 0 1 2 3 4 5 6
// for(i = 0; i < data.length; i += 7) {
instruction = 'a';
// rx ry x-axis-rotation large-arc-flag sweep-flag x y
// 0 1 2 3 4 5 6
data[0] -= point[0];

@@ -224,46 +223,43 @@ data[1] -= point[1];

point[1] += data[6];
// }
}
// H → h
if (instruction === 'H') {
instruction = 'h';
// H → h
} else if (instruction === 'H') {
// for(i = 0; i < data.length; i++) {
instruction = 'h';
data[0] -= point[0];
point[0] += data[0];
// }
}
// V → v
if (instruction === 'V') {
instruction = 'v';
// V → v
} else if (instruction === 'V') {
// for(i = 0; i < data.length; i++) {
instruction = 'v';
data[0] -= point[1];
point[1] += data[0];
// }
}
// horizontal and vertical line shorthands
// l 50 0 → h 50
// l 0 50 → v 50
if (params.lineShorthands && instruction === 'l') {
if (data[1] === 0) {
instruction = 'h';
data = [data[0]];
} else if (data[0] === 0) {
instruction = 'v';
data = [data[1]];
}
}
// fixed-point numbers
// 12.754997 → 12.755
if (params.floatPrecision) {
data = data.map(function(num) {
return +num.toFixed(params.floatPrecision);
});
// calculate new current point
} else {
if ('MCSLQTA'.indexOf(instruction) > -1) {
newPoint = data.slice(-2);
point[0] = newPoint[0];
point[1] = newPoint[1];
} else if (instruction === 'H') {
point[0] = data[0];
} else if (instruction === 'V') {
point[1] = data[0];
}
}

@@ -273,2 +269,3 @@

item.data = data;
item.point = point.slice(0);

@@ -284,61 +281,122 @@ }

/**
* Remove useless path segments.
* Main filters loop.
*
* @param {Array} path input array
* @param {Array} path input path data
* @param {Object} params plugin params
*
* @return {Array} output array
* @return {Array} output path data
*/
function removeUseless(path) {
function filters(path, params) {
return path.filter(function(item) {
var instruction,
data,
point = [0, 0],
prev = {
point: [0, 0]
},
index = 0;
// m 0,0 / l 0,0 / h 0 / v 0 / q 0,0 0,0 / t 0,0 / c 0,0 0,0 0,0 / s 0,0 0,0
if (
'mMlLhHvVqQtTcCsS'.indexOf(item.instruction) > -1 &&
item.data.every(function(i) { return i === 0; })
) {
return false;
// a 25,25 -30 0,1 0,0
} else if (
'aA'.indexOf(item.instruction) > -1 &&
item.data[5] === 0 &&
item.data[6] === 0
) {
return false;
}
path = path.filter(function(item) {
return true;
instruction = item.instruction;
data = item.data;
point = item.point;
});
index++;
}
if (data) {
/**
* Collapse repeated instructions data.
*
* @param {Array} items input array
*
* @return {Array} output array
*/
function collapseRepeated(items) {
if (params.floatPrecision) {
data = roundData(data, params.floatPrecision);
}
var prev;
// horizontal and vertical line shorthands
// l 50 0 → h 50
// l 0 50 → v 50
if (
params.lineShorthands &&
'Ll'.indexOf(instruction) > -1
) {
var lowerCase = instruction === instruction.toLowerCase();
return items.filter(function(item) {
if (point[1] - prev.point[1] === 0) {
instruction = lowerCase ? 'h' : 'H';
data = [data[0]];
} else if (point[0] - prev.point[0] === 0) {
instruction = lowerCase ? 'v' : 'V';
data = [data[1]];
}
}
if (prev && item.instruction === prev.instruction) {
// increase previous h or v data with current
if (item.instruction === 'h' || item.instruction === 'v') {
prev.data[0] += item.data[0];
// concat previous data with current
} else {
prev.data = prev.data.concat(item.data);
// remove useless non-first path segments
if (params.removeUseless) {
// m 0,0 / l 0,0 / h 0 / v 0 / q 0,0 0,0 / t 0,0 / c 0,0 0,0 0,0 / s 0,0 0,0
if (
(
'lhvqtcs'.indexOf(instruction) > -1 ||
(instruction === 'm' && index > 1)
) &&
data.every(function(i) { return i === 0; })
) {
return false;
}
// M25,25 L25,25 C 25,25 25,25 25,25
if (
'LHVQTCS'.indexOf(instruction) > -1 ||
(instruction === 'M' && index > 1)
) {
var i = -1,
every = data.every(function(d) {
return d - prev.point[++i % 2] === 0;
});
if (every) {
return false;
}
}
// a 25,25 -30 0,1 0,0
if (
'aA'.indexOf(item.instruction) > -1 &&
point[0] - prev.point[0] === 0 &&
point[1] - prev.point[1] === 0
) {
return false;
}
}
// filter current item
return false;
// collapse repeated instructions data
if (
params.collapseRepeated &&
prev.item &&
instruction === prev.item.instruction
) {
// increase previous h or v data with current
if (instruction === 'h' || instruction === 'v') {
prev.item.data[0] += data[0];
// replace previous H or V data with current
} else if (instruction === 'H' || instruction === 'V') {
prev.item.data[0] = data[0];
// concat previous data with current
} else {
prev.item.data = prev.item.data.concat(data);
}
// filter current item
return false;
}
item.instruction = instruction;
item.data = data;
prev = {
item: item,
point: point.slice(0)
};
}
prev = item;
return true;

@@ -348,13 +406,32 @@

return path;
}
/**
* Convert path JS representation to string.
* Decrease accuracy of floating-point numbers
* in path data keeping a specified number of decimals.
*
* @param {Array} pathJS JS representation array
* @param {Array} data input data array
* @param {Number} fixed number of decimals
*
* @return {Array} output data array
*/
function roundData(data, fixed) {
return data.map(function(num) {
return +num.toFixed(fixed);
});
}
/**
* Convert path array to string.
*
* @param {Array} path input path data
* @param {Object} params plugin params
*
* @return {String} output string
* @return {String} output path string
*/
function js2path(pathJS, params) {
function js2path(path, params) {

@@ -364,5 +441,5 @@ // out path data string

pathJS.forEach(function(path) {
path.forEach(function(item) {
pathString += path.instruction + (path.data ? cleanupOutData(path.data, params) : '');
pathString += item.instruction + (item.data ? cleanupOutData(item.data, params) : '');

@@ -369,0 +446,0 @@ });

@@ -34,2 +34,7 @@ var cleanupOutData = require('../lib/tools').cleanupOutData,

// patternTransform
if (item.hasAttr('patternTransform')) {
convertTransform(item, 'patternTransform', params);
}
}

@@ -45,4 +50,2 @@

* @param {Object} params plugin params
*
* @return {[type]} [description]
*/

@@ -49,0 +52,0 @@ function convertTransform(item, attrName, params) {

@@ -15,4 +15,4 @@ /**

return !item.processinginstruction;
return !(item.processinginstruction && item.processinginstruction.name === 'xml');
};

@@ -9,3 +9,3 @@ ```

## SVGO [![Build Status](https://secure.travis-ci.org/svg/svgo.png)](http://travis-ci.org/svg/svgo)
## SVGO v0.0.9 [![Build Status](https://secure.travis-ci.org/svg/svgo.png)](http://travis-ci.org/svg/svgo)

@@ -16,3 +16,3 @@ **SVG** **O**ptimizer is a Nodejs-based tool for optimizing SVG vector graphics files.

SVG files, especially exported from various editors, usually contains a lot of redundant and useless information such as editor metadata, comments, hidden elements and other stuff that can be safely removed without affecting SVG rendering result.
SVG files, especially exported from various editors, usually contains a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result.

@@ -47,7 +47,5 @@ ## What it can do

But it's not only about rude removing, SVG has a strict [specification](http://www.w3.org/TR/SVG/expanded-toc.html) with a lot of opportunities for optimizations, default values, geometry hacking and more.
Want to know how it works and how to write your own plugin? [Of course you want to](https://github.com/svg/svgo/tree/master/plugins#readme).
How-to instructions and plugins API docs will coming ASAP.
## How to use

@@ -63,3 +61,2 @@

Options:

@@ -98,7 +95,7 @@ -h, --help : Help

1. documentation and "plugins how-to"
2. batch folder optimization
3. more plugins
4. more unit tests
5. online SVGO web service
1. batch folder optimization
2. more plugins
3. SVGO GUI (crossplatform?) via awesome [node-webkit](https://github.com/rogerwang/node-webkit)
4. online SVGO web service
5. more unit tests
6. …
var config = require('../../lib/config');
function getPlugin(name, config) {
var found;
config.plugins.forEach(function(group) {
group.forEach(function(plugin) {
if (plugin.name === name) {
found = plugin;
}
});
});
return found;
}
describe('config', function() {

@@ -13,3 +29,4 @@

done();
});
})
.end();
});

@@ -33,30 +50,17 @@

it('result should have property "plugins" with instance of Object', function() {
result.should.have.property('plugins').with.instanceOf(Object);
it('result should have property "plugins" with instance of Array', function() {
result.should.have.property('plugins').with.instanceOf(Array);
});
it('plugins should have property "directPass" with instance of Array', function() {
result.plugins.should.have.property('directPass').with.instanceOf(Array);
});
it('directPass should include "removeDoctype" plugin with default params', function() {
result.plugins.directPass.should.includeEql({
name: 'removeDoctype',
active: true
});
});
});
describe('extending default config with object', function() {
describe('extend default config with object', function() {
var myConfig = {
plugins: {
directPass: [
{ name: 'removeDoctype', active: false },
{ name: 'myTestPlugin', active: true }
]
}
},
result;
var result,
myConfig = {
plugins: [{
name: 'removeDoctype',
active: false
}]
};

@@ -67,22 +71,13 @@ before(function(done) {

done();
});
})
.end();
});
it('directPass should include extended "removeDoctype" plugin', function() {
result.plugins.directPass.should.includeEql({
name: 'removeDoctype',
active: false
});
it('result should exists', function() {
getPlugin('removeDoctype', result).active.should.be.false;
});
it('directPass should include new "myTestPlugin" plugin', function() {
result.plugins.directPass.should.includeEql({
name: 'myTestPlugin',
active: true
});
});
});
describe('extending default config with file', function() {
describe('extend default config with file', function() {

@@ -100,21 +95,12 @@ var myConfig = {

done();
});
})
.end();
});
it('directPass should include extended "removeDoctype" plugin', function() {
result.plugins.directPass.should.includeEql({
name: 'removeDoctype',
active: false
});
it('result should exists', function() {
getPlugin('removeDoctype', result).active.should.be.false;
});
it('directPass should include new "myTestPlugin" plugin', function() {
result.plugins.directPass.should.includeEql({
name: 'myTestPlugin',
active: true
});
});
});
});
{
"plugins": {
"directPass": [
{
"name": "removeDoctype",
"active": false
},{
"name": "myTestPlugin",
"active": true
}
]
}
}
"plugins": [{
"name": "removeDoctype",
"active": false
}]
}

@@ -1,8 +0,33 @@

var QFS = require('q-fs'),
var INHERIT = require('inherit'),
QFS = require('q-fs'),
FS = require('fs'),
PATH = require('path'),
CONFIG = require('../../lib/config'),
SVGO = require('../../lib/svgo'),
regFilename = /^(.*)\.(\d+)\.orig\.svg$/;
var MySVGO = INHERIT(require('../../lib/svgo'), {
enableOnlyOne: function(name) {
this.config = this.config.then(function(config) {
config.plugins.forEach(function(group) {
group.forEach(function(plugin) {
plugin.active = plugin.name === name;
});
});
return config;
});
}
}),
mySVGO = new MySVGO({
js2svg: {
pretty: true
}
});
describe('plugins tests', function() {

@@ -40,8 +65,7 @@

function getResult(name, index) {
return prepareConfig(name)
.then(function(config) {
return readFile(name + '.' + index + '.orig.svg')
.then(function(input) {
return SVGO(input.toString(), config);
});
return readFile(name + '.' + index + '.orig.svg')
.then(function(input) {
mySVGO.enableOnlyOne(name);
return mySVGO.optimize(input.toString());
})

@@ -56,23 +80,4 @@ .then(function(min) {

function prepareConfig(name) {
return CONFIG()
.then(function(config) {
for (var type in config.plugins) {
config.plugins[type].forEach(function(plugin) {
if (plugin.name !== name) {
plugin.active = false;
}
});
}
config.js2svg.pretty = true;
return config;
});
}
function readFile(path) {
return QFS.read(PATH.resolve(__dirname, path));
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc