There are a few important things to keep in mind.
- Any CSS will be lost when converting to a Blob.
- You’ll have to convert all styles to SVG attributes.
- SVG resources must be self contained.
- Fonts will need to be converted to Base64 (this post has a handy tool)
- Images will need to be converted to Base64
Adding @font-face to an SVG in JSX (React) will look like this:
<defs>
<style type='text/css'>
{ `@font-face {
font-family: SharpSansNo2;
font-weight: 900;
font-style: normal;
src: url(data:application/font-woff;charset=utf-8;base64,${SharpSansDisplayNo2});
}` }
</style>
</defs>
</svg>
Here is my JS for converting my SVG to a Blob. Notice that I clone the element before making modifications, such as converting CSS styles to SVG attributes.
const $clonedSvgElement = $svgElement.cloneNode(true) as SVGElement;
// make any tweaks to colors (such as replacing css variables with calculated values)
const $background = $svgElement.querySelector('[data-background]') as SVGGraphicsElement;
const $clonedBackground = $clonedSvgElement.querySelector('[data-background]') as SVGGraphicsElement;
$clonedBackground.setAttribute('fill', getComputedStyle($background).fill);
const $textElements = $svgElement.querySelectorAll('text');
const $clonedTextElements = $clonedSvgElement.querySelectorAll('text');
for (let i = 0; i < $clonedTextElements.length; i++) {
const $textElement = $textElements[i];
const $clonedTextElement = $clonedTextElements[i];
const computedStyles = getComputedStyle($textElement);
const attributes = {
color: computedStyles.color,
fill: computedStyles.color,
'font-family': 'SharpSansNo2',
'font-size': computedStyles.fontSize,
'font-weight': computedStyles.fontWeight,
'letter-spacing': computedStyles.letterSpacing
};
for (const key in attributes) {
const value = key ? attributes[key] : '';
if (value) {
$clonedTextElement.setAttribute(key, value);
}
}
}
// export current state to HTML
const data = new XMLSerializer().serializeToString($clonedSvgElement);
// generate blob with base64 data of image
const blob = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
const URL = window.URL || window.webkitURL || window;
const blobURL = URL.createObjectURL(blob);
// generate image with canvas data (to convert to PNG and other formats)
const image = new Image();
document.body.appendChild(image);
const canvas = document.createElement('canvas');
canvas.width = this.currentPoster.width * scale;
canvas.height = this.currentPoster.height * scale;
const context = canvas.getContext('2d');
let png: string;
// wait for image to load
image.onload = () => {
if (context) {
context.drawImage(image, 0, 0, this.currentPoster.width * scale, this.currentPoster.height * scale);
png = canvas.toDataURL();
// trigger download
const download = function (href, name) {
const link = document.createElement('a');
link.download = name;
link.style.opacity = '0';
document.body.append(link);
link.href = href;
link.click();
link.remove();
};
download(png, 'sddw-poster.png');
}
};
image.src = blobURL;