Skip to content

Icon cleanup and validation

Cleanup functions are used in Iconify Tools to validate and clean up imported icons.

Unfortunately, many editors leave lots of junk in SVG files, sometimes multiplying icon file size several times.

SVG files might also contain scripts and links to external resources.

Usage

To clean up and validate icon, run cleanupSVG().

Function has one required parameter:

  • svg, SVG. Icon instance.

and one optional parameter:

  • options, object. Options, see below.

Function does not return anything, it applies changes to SVG instance.

On error function will throw an exception.

Options

The options parameter has the following properties:

  • keepTitles, boolean. If set to true, titles are not removed.

Titles are removed by default because almost all icons can represent many things, therefore, hardcoded titles are not usable for most users. The option to keep titles is intended for working with custom icon sets that are specific to one website.

Clean up process

Clean up process runs several functions that do various tasks:

  • cleanupInlineStyle() checks inline styles and removes unneeded styles.
  • convertStyleToAttrs() converts style to attributes.
  • cleanupSVGRoot() cleans up <svg> element.
  • checkBadTags() checks icon for bad tags.
  • removeBadAttributes() removes bad attributes.

If you want to, you can run functions listed above, in order listed above. It will be identical to running cleanupSVG().

Optimisation

Clean up functions do not optimise icon data, they do not rewrite any shapes. Functions only remove most dead code, making it easier to process icon.

Optimisation should be done separately. See icon manipulation functions.

Opinionated validation

Validation is opinionated. It is intended to be used to produce icons that are available to anyone, therefore, it is rather strict.

Icon validation fails if icon:

  • Contains any scripts. Untrusted scripts are dangerous.
  • Contains any text. This is heavily opinionated. Reasoning is usually icons that use text are exported by designers not realising that they are using fonts that are not installed on every computer, therefore, icon will look different than intended. Convert text to shapes before exporting it from your editor.
  • Contains any raster images. Raster images in vector shapes are unacceptable because they do not scale. Icons are meant to scale without limitations.

Example

cleanup.ts
tsimport { SVG, 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);
cleanupSVG(svg);
console.log(svg.toMinifiedString());
Result:
svg<svg xmlns="http://www.w3.org/2000/svg" width="2048" height="2048" viewBox="0 0 2048 2048"><defs id="defs3893"></defs><g id="layer1" transform="translate(0,995.63783)"><g transform="matrix(96.752895,0,0,96.752895,55.328158,-100816.34)" id="layer1-6" display="inline"><path 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" color="#000000" display="inline" visibility="visible" opacity="1" color-interpolation="sRGB" 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"/><g id="g4275" transform="matrix(1,0,0,0.90111263,0,103.41515)"><path id="path4092" d="m 7.0333918,1040.9794 0.9432241,7.504" 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" 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 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" fill="#000000" fill-opacity="1" stroke="none"/><path d="m 7.6269598,1033.8929

Released under the Apache 2.0 License.