Svgo: Here is a hack for svgo to work in a browser

Created on 9 Oct 2018  Â·  9Comments  Â·  Source: svg/svgo

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.

Most helpful comment

Or use svgomg ;)

All 9 comments

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?

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

niftylettuce picture niftylettuce  Â·  4Comments

tremby picture tremby  Â·  5Comments

madysondesigns picture madysondesigns  Â·  4Comments

pixelass picture pixelass  Â·  3Comments

crybat picture crybat  Â·  6Comments