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 only one parameter:
- svg, SVG. Icon instance.
Function does not return anything, it applies changes to SVG instance.
Clean up function is asynchronous. That means you need to handle it as Promise instance, usually by adding await before function call.
On error function will throw an exception.
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(). All functions are asynchronous.
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 uncceptable because they do not scale. Icons are meant to scale without limitations.
Example
import { 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>`;
(async () => {
const svg = new SVG(reallyBadIcon);
await cleanupSVG(svg);
console.log(svg.toMinifiedString());
})();
<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