0

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);
  }
};
Kyle Soeltz
  • 306
  • 1
  • 9

0 Answers0