Skip to content

Instantly share code, notes, and snippets.

@concatime
Last active October 6, 2023 11:35
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save concatime/ae8775bd350e7117a848b73e768b8718 to your computer and use it in GitHub Desktop.
Save concatime/ae8775bd350e7117a848b73e768b8718 to your computer and use it in GitHub Desktop.
SVG to image to canvas to PNG/JPEG/... (URI/URL and Blob)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<svg height=500 width=500>
<circle cx=250 cy=250 r=200 stroke="black" stroke-width=3 fill="red" />
Sorry, your browser does not support inline SVG.
</svg>
<img alt="preview">
<script>
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Examples
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
const formatToMimeMap = new Map()
.set('ico', 'image/vnd.microsoft.icon')
.set('jpg', 'image/jpeg')
.set('svg', 'image/svg+xml')
.set('tif', 'image/tiff');
const formatToMime = (format) => {
format = format.toLowerCase();
return formatToMimeMap.get(format) || `image/${format}`;
};
const xs = new XMLSerializer(),
serializeSVG = (svg) => xs.serializeToString(svg);
const encodeSvgToUrl = (svg) => {
const svgData = serializeSVG(svg);
// const svgDataB64 = btoa(svgData);
// const svgDataUrl = encodeURIComponent(svgData);
return `data:image/svg+xml;utf8,${svgData}`;
// return `data:image/svg+xml;base64,${svgDataB64}`;
// return `data:image/svg+xml,${svgDataUrl}`;
}
// svg ~> image ~> canvas
const svgToCanvas = async (svg, img, strip) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (ctx === null) {
throw new Error('context of canvas should not be null');
}
// encoded svg to url
const url = encodeSvgToUrl(svg);
// write data url to image
img.setAttribute('src', url);
// wait for the image to load (or fail)
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
const box = strip ? svg.getBBox() : svg.getBoundingClientRect();
canvas.width = box.width;
canvas.height = box.height;
// draw sub-image into canvas
ctx.drawImage(img, box.x || 0, box.y || 0, box.width, box.height, 0, 0,
box.width, box.height);
return canvas;
};
// img is optional
const svgToUrl = async (svg, type, img) => {
if (type === formatToMime('svg')) {
return encodeSvgToUrl(svg);
}
const canvas = await svgToCanvas(svg, img || document.createElement('img'));
const url = canvas.toDataURL(type);
/* HTMLCanvasElement.toDataURL():
* If the height or width of the canvas is 0 or larger than
* the maximum canvas size, the string "data:," is returned.
*/
if (url === 'data:,') {
throw new Error('Either one dimension if SVG is zero or SVG is too big to fit into canvas');
}
/* HTMLCanvasElement.toDataURL():
* If the requested type is not image/png, but the returned value
* starts with data:image/png, then the requested type is not supported.
*/
const png = formatToMime('png');
if (type !== png && url.startsWith(`data:${png}`)) {
throw new Error(`Type \u201c${type}\u201d is not supported`);
}
return url;
};
// img is optional
const svgToBlob = async (svg, type, img) => {
if (type === formatToMime('svg')) {
const byteString = serializeSVG(svg),
arrayBuffer = new ArrayBuffer(byteString.length),
byteArray = new Uint8Array(arrayBuffer);
for (let i = byteString.length; i-- !== 0; ) {
byteArray[i] = byteString.charCodeAt(i);
}
return new Blob([arrayBuffer], { type });
}
const canvas = await svgToCanvas(svg, img || document.createElement('img'));
return new Promise((resolve) => canvas.toBlob(resolve, type));
}
const format = 'png', mime = formatToMime(format);
const svg = document.querySelector('svg');
const img = document.querySelector('img');
svgToUrl(svg, mime, img).then((url) => {
const anchor = document.createElement('a');
anchor.download = `name.${format}`;
anchor.href = url;
anchor.click();
}).catch((err) => console.error(err));
/*svgToBlob(svg, mime, img).then((blob) => {
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.download = `name.${format}`;
anchor.href = url;
anchor.click();
setTimeout(() => URL.revokeObjectURL(url), 5000);
}).catch((err) => console.error(err));*/
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment