const svg = document.querySelector("svg");
const paths = svg.querySelectorAll("path");
const ns = "http://www.w3.org/2000/svg";
// 0. add <def> if necessary
let defs = svg.querySelector("defs");
if (!defs) {
defs = document.createElementNS(ns, "defs");
svg.insertBefore(defs, svg.children[0]);
}
/**
* 1. inline styles to classes
*/
let styledEls = svg.querySelectorAll("[style]");
styleToClass(styledEls);
function styleToClass(els) {
//add <style> to parent svg if necessary
let css = svg.querySelector("style");
if (!css) {
css = document.createElementNS(ns, "style");
svg.insertBefore(css, svg.children[0]);
}
let styleObj = {};
els.forEach(function(el) {
let id = el.id;
let style = el.getAttribute("style");
style = style ? style.replaceAll(" ", "") : "";
let styleArr = style.split(";");
let stylesRounded = [];
//round nearby numeric values values
styleArr.forEach(function(prop) {
let propEl = prop.split(":");
let name = propEl[0];
let val = propEl[1];
if (parseFloat(val) == val) {
val = +parseFloat(val).toFixed(3);
}
stylesRounded.push(name + ":" + val);
});
style = removeCssProperties(stylesRounded.join(";"));
if (style) {
if (style in styleObj === false) {
styleObj[style] = {
count: 1,
ids: [id]
};
} else {
styleObj[style]["count"] += 1;
styleObj[style]["ids"].push(id);
}
}
});
let cssStr = "";
let classCount = 0;
for (style in styleObj) {
let css = style;
let className = "cl" + classCount;
cssStr += `.${className}{${style}}\n`;
classCount++;
let ids = styleObj[style]["ids"];
ids.forEach(function(id, i) {
let el = document.getElementById(id);
el.classList.add(className);
el.removeAttribute("style");
});
}
css.innerHTML = cssStr;
}
function removeCssProperties(css) {
css = css.replaceAll("; ", "");
let cssArr = css.split(";");
let cssFilter = [];
//default or propriatary properties
const remove = [
"stroke-miterlimit:10",
"stroke-dasharray:none",
"stroke-opacity:1",
"fill-opacity:1",
"-inkscape-font-specification:ArialMT",
"fill-rule:nonzero",
"fill:#000000",
"fill:black",
"stroke:none",
"writing-mode:lr-tb",
"stroke-linejoin:miter",
"font-variant:normal",
"font-weight:normal"
];
cssArr.forEach(function(prop) {
if (remove.indexOf(prop) === -1) {
cssFilter.push(prop);
}
});
cssFilter = cssFilter.join(";");
return cssFilter;
}
/**
* find repeated path "d" attributes
* replace them with <use> elements
*/
pathsToUse(paths);
function pathsToUse(paths) {
let useObj = {};
paths.forEach(function(path, i) {
let d = path.getAttribute("d").replaceAll(",", " ");
let id = path.id;
//add auto ids
if (!id) {
path.setAttribute("id", "pathID" + i);
}
//collect all d/pathdata
if (d in useObj === false) {
useObj[d] = {
count: 1,
ids: [id]
};
} else {
useObj[d]["count"] += 1;
useObj[d]["ids"].push(id);
}
});
//replace paths with <use> elements
let useDefs = "";
let useCount = 0;
for (d in useObj) {
let current = useObj[d];
let occurrences = current["ids"];
if (occurrences.length > 1) {
let useID = "p" + useCount;
//create def
useDefs += `<path id="${useID}" d="${d}" />\n`;
useCount++;
occurrences.forEach(function(id, i) {
let el = svg.getElementById(id);
let className = el.getAttribute("class");
let use = document.createElementNS(ns, "use");
use.setAttribute("href", "#" + useID);
use.setAttribute("xlink:href", "#" + useID);
use.classList.add(className);
el.replaceWith(use);
});
}
}
defs.insertAdjacentHTML("beforeend", useDefs);
}
// optimize d strings
let pathsOpt = svg.querySelectorAll("path");
pathsOpt.forEach(function(path) {
let d = path
.getAttribute("d")
.replace(/([a-zA-Z])(,)/g, "$1")
.replace(/( )([a-zA-Z])/g, "$2")
.replace(/([a-zA-Z])( )/g, "$1")
.replaceAll(" 0.", " .")
.replaceAll(",", " ")
.replaceAll(" -", "-");
path.setAttribute("d", d);
});
// optimize svg Markup
let svgMin = svg.outerHTML;
// minifying
svgMin = svgMin
.replaceAll("></path>", "/>")
.replaceAll("></use>", "/>")
.replace(/([ |\n|\r|\t])/g, " ")
.replace(/ +/g, " ")
.trim()
.replaceAll("> <", "><")
.replaceAll("><", ">\n<")
.replaceAll("} ", "}")
.replaceAll("}", "}\n");
//populate textarea
svgOpt.value = svgMin;
svg {
max-height: 90vh;
width: auto;
border: 1px solid #ccc
}
.cl0 {
stroke: green!important;
stroke-width: 10%!important;
}
<svg version="1.1" id="svg2" xml:space="preserve" viewBox="0 0 4224 3264" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="g8" transform="matrix(0,-0.04,-0.04,0,4160,3245.3333)">
<g id="g10">
<g id="g12" clip-path="url(#clipPath16)">
<g id="g18" transform="matrix(-0.04648,-0.99892,0.99892,-0.04648,16044.5,80843.5)">
<path d="M 837.5,0 C 837.5,462.53851 462.53851,837.5 0,837.5 -462.53851,837.5 -837.5,462.53851 -837.5,0 c 0,-462.53851 374.96149,-837.5 837.5,-837.5 462.53851,0 837.5,374.96149 837.5,837.5" style="fill:none;stroke:#808080;stroke-width:25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1" id="path20" />
</g>
<g id="g22" transform="matrix(-0.04715,-0.99889,0.99889,-0.04715,15943.5,78677.5)">
<path d="M 837.5,0 C 837.5,462.53851 462.53851,837.5 0,837.5 -462.53851,837.5 -837.5,462.53851 -837.5,0 c 0,-462.53851 374.96149,-837.5 837.5,-837.5 462.53851,0 837.5,374.96149 837.5,837.5" style="fill:none;stroke:#808080;stroke-width:24.9999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1" id="path24" />
</g>
<g id="g26" transform="matrix(-0.04782,-0.99886,0.99886,-0.04782,15840.5,76512.5)">
<path d="M 837.5,0 C 837.5,462.53851 462.53851,837.5 0,837.5 -462.53851,837.5 -837.5,462.53851 -837.5,0 c 0,-462.53851 374.96149,-837.5 837.5,-837.5 462.53851,0 837.5,374.96149 837.5,837.5" style="fill:none;stroke:#808080;stroke-width:24.9999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1" id="path28" />
</g>
<path d="m 41675,88799 -6933,313 -30,-649 6283,-284 -391,-8667 -6283,284 -30,-650 6934,-313 450,9966" style="fill:none;stroke:#dcdcdc;stroke-width:25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1" id="path2680" />
</g>
</g>
<g id="g2702">
<g id="g2704"><text transform="matrix(0,-1,-1,0,14155,86256)" style="font-variant:normal;font-weight:normal;font-size:2120.87px;font-family:Arial;-inkscape-font-specification:ArialMT;writing-mode:lr-tb;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none" id="text2712">
<tspan x="0 1179.2031" y="0" id="tspan2710">17</tspan>
</text>
</g>
</g>
<g id="g2714">
<g id="g2716" clip-path="url(#clipPath2720)">
<g id="g3830" transform="rotate(-90,31516,-5789.5)">
<path d="M 1507.5,0 C 1507.5,832.56927 832.56927,1507.5 0,1507.5 -832.56927,1507.5 -1507.5,832.56927 -1507.5,0 c 0,-832.56927 674.93073,-1507.5 1507.5,-1507.5 832.56927,0 1507.5,674.93073 1507.5,1507.5" style="fill:none;stroke:#000000;stroke-width:25;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1" id="path3832" />
</g><text transform="matrix(-0.08733,0.99618,0.99618,0.08733,37824,24280)" style="font-variant:normal;font-weight:normal;font-size:1211.93px;font-family:Arial;-inkscape-font-specification:ArialMT;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" id="text3836">
<tspan x="0 875.01031 1548.8411 2222.6716" y="0" id="tspan3834">C129</tspan>
</text>
</g>
</g>
</g>
</svg>
<textarea name="svgOpt" id="svgOpt" style="width:100%; min-height:30em"></textarea>