Skip to content

Optimising icon with SVGO

This function is part of icon manipulation functions in Iconify Tools.

Function runSVGO() optimises icon using popular SVG optimisation tool SVGO.

It is meant to be used on icons that have already been processed with cleanupSVG(), which does most of the cleanup.

This function is used to do advanced stuff that SVGO is good at: converting transformations, cleaning up numbers, removing unused SVG elements, and so on.

Plugins

By default, function uses a pre-configured list of SVGO plugins, which excludes some bugged plugins.

If an icon contains SVG animations, plugins that modify shapes are excluded.

Usage

Function has the following parameters:

  • svg, SVG. Icon instance.
  • options, object. Options (optional).

Options

There are two ways to set options:

  • Using a custom list of SVGO plugins.
  • Toggle groups of plugins using several options.

Custom plugins list

You can set custom plugins using plugins property of options. Value is array of plugins, passed directly to SVGO (see SVGO documentation).

Example:

tsrunSVGO(svg, {
   plugins: ['convertStyleToAttrs', 'inlineStyles'],
   multipass: true,
});

Plugin options

You can also pick from a preset list of plugins by setting these options:

  • animated, boolean. If true, SVGO plugins that are known to bug out with animated icons are not used.
  • keepShapes, boolean. If true, plugins that modify shapes are not used. This is useful if you need to keep shapes as-is, for example, when animating shapes, but it is not as strict as setting animated option.
  • cleanupIDs, string|false|function. Custom prefix for rewriting IDs, false to disable plugins that change IDs. Can be a callback that returns new ID based on old ID.

These options cannot be used together with plugins option.

Other options

Options that can be used with any options listed above:

  • multipass, boolean. If true, plugins are ran multiple times for better optimisation. Enabled by default.

Example

svgo.ts
tsimport { SVG, runSVGO } from '@iconify/tools';

const reallyBadIcon = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:cc="http://creativecommons.org/ns#"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:svg="http://www.w3.org/2000/svg"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  width="2048"
  height="2048"
  id="svg3891"
  version="1.1"
  inkscape:version="0.91 r13725"
  sodipodi:docname="trash.svg"
  inkscape:export-filename="/home/nikku/camunda/projects/bpmn.io/bpmn-font/raw/trash.png"
  inkscape:export-xdpi="0.88"
  inkscape:export-ydpi="0.88">
 <defs
    id="defs3893">
   <inkscape:path-effect
      effect="spiro"
      id="path-effect4094"
      is_visible="true" />
   <inkscape:path-effect
      effect="spiro"
      id="path-effect4094-0"
      is_visible="true" />
 </defs>
 <sodipodi:namedview
    id="base"
    pagecolor="#ffffff"
    bordercolor="#666666"
    borderopacity="1.0"
    inkscape:pageopacity="0.0"
    inkscape:pageshadow="2"
    inkscape:zoom="0.175"
    inkscape:cx="307.67263"
    inkscape:cy="1030.7415"
    inkscape:document-units="px"
    inkscape:current-layer="layer1-6"
    showgrid="false"
    inkscape:window-width="1596"
    inkscape:window-height="807"
    inkscape:window-x="0"
    inkscape:window-y="91"
    inkscape:window-maximized="0"
    inkscape:snap-page="false"
    inkscape:snap-object-midpoints="false"
    inkscape:snap-nodes="false"
    inkscape:snap-to-guides="false"
    inkscape:snap-grids="false" />
 <metadata
    id="metadata3896">
   <rdf:RDF>
     <cc:Work
        rdf:about="">
       <dc:format>image/svg+xml</dc:format>
       <dc:type
          rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
       <dc:title />
     </cc:Work>
   </rdf:RDF>
 </metadata>
 <g
    inkscape:label="Layer 1"
    inkscape:groupmode="layer"
    id="layer1"
    transform="translate(0,995.63783)">
   <g
      transform="matrix(96.752895,0,0,96.752895,55.328158,-100816.34)"
      id="layer1-6"
      inkscape:label="Layer 1"
      style="display:inline">
     <path
        style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.343629;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        d="m 3.4296875,1038.3672 1.3325877,12.7308 10.5912408,0 1.228186,-12.7284 -13.1520736,0 z m 1.4921875,1.3437 10.185547,0 -0.972656,10.0411 -8.1582035,0 z"
        id="rect4089"
        inkscape:connector-curvature="0"
        sodipodi:nodetypes="ccccccccccc" />
     <g
        id="g4275"
        transform="matrix(1,0,0,0.90111263,0,103.41515)">
       <path
          sodipodi:nodetypes="cc"
          inkscape:connector-curvature="0"
          inkscape:original-d="m 7.0333918,1040.9794 0.9432241,7.504"
          inkscape:path-effect="#path-effect4094"
          id="path4092"
          d="m 7.0333918,1040.9794 0.9432241,7.504"
          style="fill:none;stroke:#000000;stroke-width:1.343629;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
       <path
          sodipodi:nodetypes="cc"
          inkscape:connector-curvature="0"
          inkscape:original-d="m 12.990235,1040.9794 -0.943224,7.504"
          inkscape:path-effect="#path-effect4094-0"
          id="path4092-2"
          d="m 12.990235,1040.9794 -0.943224,7.504"
          style="fill:none;stroke:#000000;stroke-width:1.343629;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
     </g>
     <path
        style="fill:#000000;fill-opacity:1;stroke:none"
        d="m 7.2638322,1035.194 -4.2854023,1.2542 0,0.6276 14.0667651,0 0,-0.6276 -4.337726,-1.2542 z"
        id="rect4121"
        inkscape:connector-curvature="0"
        sodipodi:nodetypes="ccccccc" />
     <path
        style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.72291225;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
        d="m 7.6269598,1033.8929 4.7697062,0 0,1.737 -4.7697062,0 z"
        id="rect4121-6" />
   </g>
 </g>
</svg>`
;

const svg = new SVG(reallyBadIcon);
runSVGO(svg);
console.log(svg.toMinifiedString());
Result:
svg<svg xmlns="http://www.w3.org/2000/svg" width="2048" height="2048" viewBox="0 0 2048 2048"><metadata/><path d="m3.43 1038.367 1.332 12.731h10.592l1.228-12.728H3.43zm1.492 1.344h10.185l-.972 10.041H5.977z" color="#000" enable-background="accumulate" font-family="sans-serif" overflow="visible" style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;block-progression:tb;white-space:normal;isolation:auto;mix-blend-mode:normal" transform="translate(55.328 -99820.702) scale(96.7529)"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.344" d="m7.033 1040.98.944 7.503m5.013-7.503-.943 7.503" transform="matrix(96.7529 0 0 87.185 55.328 -89815)"/><path d="M758.141 337.32 343.458 458.648v60.76h1361.023v-60.76L1284.767 337.32z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="69.952" d="M793.262 211.444h461.512v168.06H793.262z"/></svg>

That example shows running SVGO on icon that has not been cleaned up and validated. Not all useless attributes have been removed, and SVGO doesn't check for some content that should not be in icon, such as text, raster images and events.

Therefore, all icons must be cleaned up after loading.

Same code with clean up:

svgo.ts
tsimport { SVG, runSVGO, cleanupSVG } from '@iconify/tools';

const reallyBadIcon = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:cc="http://creativecommons.org/ns#"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:svg="http://www.w3.org/2000/svg"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  width="2048"
  height="2048"
  id="svg3891"
  version="1.1"
  inkscape:version="0.91 r13725"
  sodipodi:docname="trash.svg"
  inkscape:export-filename="/home/nikku/camunda/projects/bpmn.io/bpmn-font/raw/trash.png"
  inkscape:export-xdpi="0.88"
  inkscape:export-ydpi="0.88">
 <defs
    id="defs3893">
   <inkscape:path-effect
      effect="spiro"
      id="path-effect4094"
      is_visible="true" />
   <inkscape:path-effect
      effect="spiro"
      id="path-effect4094-0"
      is_visible="true" />
 </defs>
 <sodipodi:namedview
    id="base"
    pagecolor="#ffffff"
    bordercolor="#666666"
    borderopacity="1.0"
    inkscape:pageopacity="0.0"
    inkscape:pageshadow="2"
    inkscape:zoom="0.175"
    inkscape:cx="307.67263"
    inkscape:cy="1030.7415"
    inkscape:document-units="px"
    inkscape:current-layer="layer1-6"
    showgrid="false"
    inkscape:window-width="1596"
    inkscape:window-height="807"
    inkscape:window-x="0"
    inkscape:window-y="91"
    inkscape:window-maximized="0"
    inkscape:snap-page="false"
    inkscape:snap-object-midpoints="false"
    inkscape:snap-nodes="false"
    inkscape:snap-to-guides="false"
    inkscape:snap-grids="false" />
 <metadata
    id="metadata3896">
   <rdf:RDF>
     <cc:Work
        rdf:about="">
       <dc:format>image/svg+xml</dc:format>
       <dc:type
          rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
       <dc:title />
     </cc:Work>
   </rdf:RDF>
 </metadata>
 <g
    inkscape:label="Layer 1"
    inkscape:groupmode="layer"
    id="layer1"
    transform="translate(0,995.63783)">
   <g
      transform="matrix(96.752895,0,0,96.752895,55.328158,-100816.34)"
      id="layer1-6"
      inkscape:label="Layer 1"
      style="display:inline">
     <path
        style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.343629;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        d="m 3.4296875,1038.3672 1.3325877,12.7308 10.5912408,0 1.228186,-12.7284 -13.1520736,0 z m 1.4921875,1.3437 10.185547,0 -0.972656,10.0411 -8.1582035,0 z"
        id="rect4089"
        inkscape:connector-curvature="0"
        sodipodi:nodetypes="ccccccccccc" />
     <g
        id="g4275"
        transform="matrix(1,0,0,0.90111263,0,103.41515)">
       <path
          sodipodi:nodetypes="cc"
          inkscape:connector-curvature="0"
          inkscape:original-d="m 7.0333918,1040.9794 0.9432241,7.504"
          inkscape:path-effect="#path-effect4094"
          id="path4092"
          d="m 7.0333918,1040.9794 0.9432241,7.504"
          style="fill:none;stroke:#000000;stroke-width:1.343629;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
       <path
          sodipodi:nodetypes="cc"
          inkscape:connector-curvature="0"
          inkscape:original-d="m 12.990235,1040.9794 -0.943224,7.504"
          inkscape:path-effect="#path-effect4094-0"
          id="path4092-2"
          d="m 12.990235,1040.9794 -0.943224,7.504"
          style="fill:none;stroke:#000000;stroke-width:1.343629;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
     </g>
     <path
        style="fill:#000000;fill-opacity:1;stroke:none"
        d="m 7.2638322,1035.194 -4.2854023,1.2542 0,0.6276 14.0667651,0 0,-0.6276 -4.337726,-1.2542 z"
        id="rect4121"
        inkscape:connector-curvature="0"
        sodipodi:nodetypes="ccccccc" />
     <path
        style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.72291225;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
        d="m 7.6269598,1033.8929 4.7697062,0 0,1.737 -4.7697062,0 z"
        id="rect4121-6" />
   </g>
 </g>
</svg>`
;

const svg = new SVG(reallyBadIcon);

// Clean up and validate icon
cleanupSVG(svg);

// Optimise icon
runSVGO(svg);

console.log(svg.toMinifiedString());
Result:
svg<svg xmlns="http://www.w3.org/2000/svg" width="2048" height="2048" viewBox="0 0 2048 2048"><path d="m387.19 644.317 128.875 1231.76h1024.807l118.813-1231.47H387.19zm144.356 130.035h985.428l-94.044 971.496H633.62z" color="#000"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.344" d="m7.033 1040.98.944 7.503m5.013-7.503-.943 7.503" transform="matrix(96.7529 0 0 87.185 55.328 -89815)"/><path d="M758.141 337.32 343.458 458.648v60.76h1361.023v-60.76L1284.767 337.32z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="69.952" d="M793.262 211.444h461.512v168.06H793.262z"/></svg>

Released under the Apache 2.0 License.