I am trying to write a script to allow users to download an SVG as a PNG. The SVGs are generated via d3.js and the below script is used to take the generated SVG, put it into a canvas element, and then trigger a download through an anchor element.
Everything works perfectly on Chrome and Safari, however when I test on Firefox I get mixed results.
Sometimes the canvas element ends up blank and I get a png of the right dimensions but no contents, and sometimes the download works perfectly. Sometimes it works every other click of the download button, and sometimes it breaks after I resize the page, only for it to start working a couple clicks later.
The 'await' statement for canvas.getContext is in there as without it I was getting the same behavior in Chrome and Safari, which also confuses me as I do not believe the getContext method is asynchronous.
My question is why is the behavior different in Firefox and how can I adjust the code to allow for the same kind of downloads across these browsers?
For additional context, I am using ReactJS and the function is being called inside of a useEffect block. Also if I output the img
that is created in the code, it renders correctly 100% of the time. The SVG also has fixed width and height attributes that are not percentages.
enum FileFormat {
SVG = 'svg',
PNG = 'png',
}
interface Input {
svg: Node | null;
width: number | undefined;
height: number | undefined;
fileFormat?: FileFormat;
}
export default async (input: Input) => {
const {svg, fileFormat} = input;
const width = input.width ? input.width : 1500;
const height = input.height ? input.height : 1500;
const canvas = document.createElement('canvas');
if (canvas && svg) {
const img = new Image();
const serializer = new XMLSerializer();
const svgStr = serializer.serializeToString(svg);
img.src = 'data:image/svg+xml;base64,' + window.btoa(svgStr);
img.width = width;
img.height = height;
canvas.width = width;
canvas.height = height;
const canvasContext = await canvas.getContext('2d');
let url = '';
if (canvasContext) {
canvasContext.drawImage(img, 0, 0, width, height);
url = canvas.toDataURL('image/png');
}
const a = document.createElement('a');
if (fileFormat === FileFormat.SVG) {
a.href = img.src;
a.download = 'chart.svg';
} else {
a.href = url;
a.download = 'chart-new.png';
}
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
};