Skip to content

Instantly share code, notes, and snippets.

@mindon
Created May 26, 2022 12:36
Show Gist options
  • Select an option

  • Save mindon/a224958eb774a74a64f2887fe56e7ab2 to your computer and use it in GitHub Desktop.

Select an option

Save mindon/a224958eb774a74a64f2887fe56e7ab2 to your computer and use it in GitHub Desktop.
Let's say, what if your designer providing a svg file with large embedded images... how to optimize the svg file size?
(function(svgdoc, scale) {
// optimize large embed images in a svg
// usage: <html><body><svg>your own real svg with too large embeded images...</svg><script src="optimize-embed-images-size-in-svg.js">< /script></body></html>
// author: mindon@live.com
function optimize(img, cb) {
const src = img ? img.getAttribute("xlink:href") : undefined;
let c = document.createElement("canvas");
const done = () => {
cb && cb();
src && img.setAttribute('xlink:href', c.toDataURL('image/png'));
c = null;
return fin();
};
if (!src) {
return done();
}
const i = new Image();
i.onload = () => {
const x = Math.min(i.width, i.height);
const r = x <= scale ? 1 : scale / x;
const w = i.width * r,
h = i.height * r;
c.width = w;
c.height = h;
const ctx = c.getContext('2d');
ctx.globalAlpha = 1;
if (!img.getAttribute('mask')) {
ctx.drawImage(i, 0, 0, i.width, i.height, 0, 0, c.width, c.height);
return done();
}
const maskid = img.getAttribute('mask').match(/url\((#[^)]+)\)/)[1];
img.removeAttribute('mask');
const t = svgdoc.querySelector(maskid).querySelector('image'),
tmask = t.getAttribute('mask'),
tsrc = t.getAttribute('xlink:href');
const update = () => {
if (!tsrc) {
return done();
}
t.parentElement.parentElement.removeChild(t.parentElement);
const j = new Image();
j.onload = () => {
ctx.drawImage(j, 0, 0, j.width, j.height, 0, 0, w, h);
const alpha = ctx.getImageData(0, 0, w, h).data;
ctx.drawImage(i, 0, 0, i.width, i.height, 0, 0, w, h);
const contents = ctx.getImageData(0, 0, w, h),
data = contents.data;
for (let i = 3, len = data.length; i < len; i = i + 4) {
data[i] = (alpha[i-1] + alpha[i-2] + alpha[i - 3])/3;
}
ctx.clearRect(0, 0, w, h);
ctx.putImageData(contents, 0, 0);
done();
};
j.src = tsrc;
};
if (tmask) {
return optimize(t, update);
}
update();
};
i.src = src;
}
function clean() {
// remove tspan x
[].slice.call(svgdoc.querySelectorAll('tspan')).forEach(t => {
t.removeAttribute('x');
});
// remove used reference id
[].slice.call(svgdoc.querySelectorAll('defs > *[id]')).forEach(t => {
const id = t.getAttribute('id');
if (!svgdoc.querySelector(`*[mask="url(#${id})"`) &&
!svgdoc.querySelector(`*[clip-path="url(#${id})"`)) {
t.parentElement.removeChild(t);
}
});
}
const list = [].slice.call(svgdoc.querySelectorAll('image'));
let n = list.length;
const fin = () => {
n--;
if (n == 0) {
clean();
// download as svg
const a = document.createElement('a'),
now = new Date();
a.download = `optimized_${ [`0${now.getMonth() + 1}`.slice(-2), `0${now.getDate()}`.slice(-2), `0${now.getHours()}`.slice(-2), , `0${now.getMinutes()}`.slice(-2)].join('') }.svg`;
a.href = `data:image/svg+xml;base64,${ btoa(unescape(encodeURIComponent(svgdoc.outerHTML.replace(/\s+\n/g, '\n'))))}`;
a.click();
}
};
list.forEach(img => optimize(img));
})(document.querySelector('svg'), 320);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment