/** * @see https://stackoverflow.com/a/28226736 * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL * * @created 25.09.2023 * @author smiley * @copyright 2023 smiley * @license MIT */ export default class SVGConvert{ /** * Converts the SVG image from the source element into a raster image with the given size and format * and inserts the resulting data URI into the src attribute of the given destination image element. * * @param {SVGElement|HTMLImageElement} source * @param {HTMLImageElement} destination * @param {Number} width * @param {Number} height * @param {String} format * @return {void} */ static toDataURI(source, destination, width, height, format){ // format is whatever the fuck the specification allows: // @see https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl-dev format = (format || 'image/png'); if(!destination instanceof HTMLImageElement){ throw new Error('destination is not an instance of HTMLImageElement'); } if(source instanceof HTMLImageElement){ source = SVGConvert.base64Decode(source); } SVGConvert.toCanvas(source, width, height) .then(canvas => destination.src = canvas.toDataURL(format)) .catch(error => console.log('(╯°□°)╯彡┻━┻ ', error)); } /** * Draws the given SVG source on a canvas of the given size and returns the canvas element * * @param {SVGElement} source * @param {Number} width * @param {Number} height * @return {Promise} */ static toCanvas(source, width, height){ return new Promise((resolve, reject) => { if(!source instanceof SVGElement){ throw new Error('source is not an instance of SVGElement'); } // the source SVG element needs to be visible source.style.display = 'inline-block'; source.style.visibility = 'visible'; // add/fix the width/height of the SVG element // @see https://bugzilla.mozilla.org/show_bug.cgi?id=700533#c39 source.width.baseVal.valueAsString = width; source.height.baseVal.valueAsString = height; try{ // stringify the SVG element and create an object URL for it let svgString = new XMLSerializer().serializeToString(source); let svgBlob = new Blob([svgString], {type: 'image/svg+xml;charset=utf-8'}); let tempUrl = URL.createObjectURL(svgBlob); // create a temporary image let tempImg = new Image(width, height); // trigger the onLoad event with the temporary blob URL tempImg.src = tempUrl; // process the conversion in the onLoad event tempImg.onload = function(){ // create a canvas element to draw the SVG on let canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; canvas.getContext('2d').drawImage(tempImg, 0, 0); // revoke the temporary blob URL.revokeObjectURL(tempUrl); // return the PNG image as data URI resolve(canvas); }; } catch(error){ reject(error.message); } }); } /** * Converts the given SVG base64 data URI into a DOM element * * @param {HTMLImageElement} image * @return {SVGElement} */ static base64Decode(image){ if(!image instanceof HTMLImageElement){ throw new Error('image is not an instance of HTMLImageElement'); } if(!image.src || !image.src.includes('base64,')){ throw new Error('invalid image source'); } return new DOMParser().parseFromString(atob(image.src.split(',')[1]), 'image/svg+xml').firstChild; } }