Hi all.
Like some others, I needed svgo to work in a browser, so, here is a hack I used.
In lib/svgo/config.js, add the following code under the 'use strict' (line 1):
var all_plugins = {
cleanupAttrs: require('../../plugins/cleanupAttrs.js'),
removeDoctype: require('../../plugins/removeDoctype.js'),
removeXMLProcInst: require('../../plugins/removeXMLProcInst.js'),
removeComments: require('../../plugins/removeComments.js'),
removeMetadata: require('../../plugins/removeMetadata.js'),
removeTitle: require('../../plugins/removeTitle.js'),
removeDesc: require('../../plugins/removeDesc.js'),
removeUselessDefs: require('../../plugins/removeUselessDefs.js'),
removeEditorsNSData: require('../../plugins/removeEditorsNSData.js'),
removeEmptyAttrs: require('../../plugins/removeEmptyAttrs.js'),
removeHiddenElems: require('../../plugins/removeHiddenElems.js'),
removeEmptyText: require('../../plugins/removeEmptyText.js'),
removeEmptyContainers: require('../../plugins/removeEmptyContainers.js'),
removeViewBox: require('../../plugins/removeViewBox.js'),
cleanupEnableBackground: require('../../plugins/cleanupEnableBackground.js'),
convertStyleToAttrs: require('../../plugins/convertStyleToAttrs.js'),
convertColors: require('../../plugins/convertColors.js'),
convertPathData: require('../../plugins/convertPathData.js'),
convertTransform: require('../../plugins/convertTransform.js'),
removeUnknownsAndDefaults: require('../../plugins/removeUnknownsAndDefaults.js'),
removeNonInheritableGroupAttrs: require('../../plugins/removeNonInheritableGroupAttrs.js'),
removeUselessStrokeAndFill: require('../../plugins/removeUselessStrokeAndFill.js'),
removeUnusedNS: require('../../plugins/removeUnusedNS.js'),
cleanupIDs: require('../../plugins/cleanupIDs.js'),
cleanupNumericValues: require('../../plugins/cleanupNumericValues.js'),
moveElemsAttrsToGroup: require('../../plugins/moveElemsAttrsToGroup.js'),
moveGroupAttrsToElems: require('../../plugins/moveGroupAttrsToElems.js'),
collapseGroups: require('../../plugins/collapseGroups.js'),
removeRasterImages: require('../../plugins/removeRasterImages.js'),
mergePaths: require('../../plugins/mergePaths.js'),
convertShapeToPath: require('../../plugins/convertShapeToPath.js'),
sortAttrs: require('../../plugins/sortAttrs.js'),
removeDimensions: require('../../plugins/removeDimensions.js'),
removeAttrs: require('../../plugins/removeAttrs.js'),
}
Then, in function preparePluginsArray, change actual line (79):
plugin = Object.assign({}, require('../../plugins/' + key));
to:
plugin = Object.assign({}, all_plugins[key]);
Install browserify in svgo directory with :
npm install browserify
and run it :
browserify lib/svgo.js > some/place/svgo.js
Finally, open the resulting file, find line:
module.exports = SVGO;
and add before or after it the following:
window.SVGO = SVGO;
Now, in your JS, you just have to call SVGO with the required synthax (don't forget to set option 'full' to true).
May this help somebody.
Or use svgomg ;)
@elrumordelaluz
Well, I meant use svgo as a library in a browser...
yes, just pinging to show that tool in case is useful for anyone. However your solution seems pretty valid, will try it!
I followed your tutorial and made a nice browserify version of SVGO, thanks !
I just have a problem when I try to add the latest SVGO plugin "removeOffCanvasPaths.js".
Whenever I try to use it, it gives me the following error: « Unhandled Promise Rejection: TypeError: Object is not a constructor (evaluating 'new SVGO()’) »
Others plugins works well. Does anyone have an idea about how to make this one works as well ?
Here's HTML part :
function SVGSVGO() {
var fichierSVG = document.getElementById("hidden").innerHTML; //MY SVG FILE
var svgo = new SVGO({
full: true,
plugins: [{
removeOffCanvasPaths: true,
}]
});
svgo.optimize(fichierSVG).then(function(result) {
for (key in result) {
if (result.hasOwnProperty(key)) {
var value = result[key];
console.log(value); //MY SVG CLEANED
}
}
})
};
And here's the JS part where the problem lies :
[function(require,module,exports){
'use strict';
exports.type = 'perItem';
exports.active = true;
exports.description = 'removes elements that are drawn outside of the viewbox (disabled by default)';
var SVGO = require('../lib/svgo.js'),
_path = require('./_path.js'),
intersects = _path.intersects,
path2js = _path.path2js,
viewBox,
viewBoxJS;
exports.fn = function(item) {
if (item.isElem('path') && item.hasAttr('d') && typeof viewBox !== 'undefined')
{
// Consider that any item with a transform attribute or a M instruction
// within the viewBox is visible
if (hasTransform(item) || pathMovesWithinViewBox(item.attr('d').value))
{
return true;
}
var pathJS = path2js(item);
if (pathJS.length === 2)
{
// Use a closed clone of the path if it's too short for intersects()
pathJS = JSON.parse(JSON.stringify(pathJS));
pathJS.push({ instruction: 'z' });
}
return intersects(viewBoxJS, pathJS);
}
if (item.isElem('svg'))
{
parseViewBox(item);
}
return true;
};
/**
* Test whether given item or any of its ancestors has a transform attribute.
*
* @param {String} path
* @return {Boolean}
*/
function hasTransform(item)
{
return item.hasAttr('transform') || (item.parentNode && hasTransform(item.parentNode));
}
/**
* Parse the viewBox coordinates and compute the JS representation of its path.
*
* @param {Object} svg svg element item
*/
function parseViewBox(svg)
{
var viewBoxData = '';
if (svg.hasAttr('viewBox'))
{
// Remove commas and plus signs, normalize and trim whitespace
viewBoxData = svg.attr('viewBox').value;
}
else if (svg.hasAttr('height') && svg.hasAttr('width'))
{
viewBoxData = '0 0 ' + svg.attr('width').value + ' ' + svg.attr('height').value;
}
viewBoxData = viewBoxData.replace(/[,+]|px/g, ' ').replace(/\s+/g, ' ').replace(/^\s*|\s*$/g, '');
var m = /^(-?\d*\.?\d+) (-?\d*\.?\d+) (\d*\.?\d+) (\d*\.?\d+)$/.exec(viewBoxData);
if (!m)
{
return;
}
viewBox = {
left: parseFloat(m[1]),
top: parseFloat(m[2]),
right: parseFloat(m[1]) + parseFloat(m[3]),
bottom: parseFloat(m[2]) + parseFloat(m[4])
};
/////////////////////////////////////////////////////////// PROBLEM HERE ?
var path = new SVGO().createContentItem
({
elem: 'path',
prefix: '',
local: 'path'
});
path.addAttr
({
name: 'd',
prefix: '',
local: 'd',
value: 'M' + m[1] + ' ' + m[2] + 'h' + m[3] + 'v' + m[4] + 'H' + m[1] + 'z'
});
viewBoxJS = path2js(path);
///////////////////////////////////////////////////////////
}
/**
* Test whether given path has a M instruction with coordinates within the viewBox.
*
* @param {String} path
* @return {Boolean}
*/
function pathMovesWithinViewBox(path)
{
var regexp = /M\s*(-?\d*\.?\d+)(?!\d)\s*(-?\d*\.?\d+)/g, m;
while (null !== (m = regexp.exec(path)))
{
if (m[1] >= viewBox.left && m[1] <= viewBox.right && m[2] >= viewBox.top && m[2] <= viewBox.bottom)
{
return true;
}
}
return false;
}
}
This could be a nice project to expose svgo as a library (JavaScript API) and better if it supports both node.js and browser. Seems https://jakearchibald.github.io/svgomg/ is not oriented to support an API but just a web application... Do you know some project exposing svgo as browser js api ? or am I missing somthing regarding svgomg ? thanks!
Do fr33z00 solution still works, I would like to use this library in a android application?
@Rskut see https://github.com/svg/svgo/pull/1122
For webpack projects (in non-Node.js environment), SVGO will also work by the following settings (without modifying the source code of the library).
package.json:
{
"devDependencies": {
"brfs": "^2.0.2",
"transform-loader": "^0.2.4",
"webpack": "^4.23.1",
"webpack-cli": "^3.1.2"
// ...
},
"dependencies": {
"svgo": "^1.3.2"
},
// ...
}
webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: 'transform-loader?brfs'
},
// ...
]
},
node: { fs: 'empty' },
// ...
}
fs: 'empty' may cause some problems… but it works fine for my use case.
Maybe try the portable webpack output https://unpkg.com/libsvgo/webpack/SVGO.js from dr-js/libsvgo, a re-formatted ES6+ fork of svgo.
Most helpful comment
Or use svgomg ;)