Last active
April 30, 2024 06:05
-
-
Save concatime/ae8775bd350e7117a848b73e768b8718 to your computer and use it in GitHub Desktop.
SVG to image to canvas to PNG/JPEG/... (URI/URL and Blob)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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