0

I am trying to convert an SVG to a PNG image using the canvas as the "proxy". I can get it to work in Chromium, but not in Firefox. It seams that I need a width and height defined in the SVG, but in my case the SVG does not have width and height attributes. When the SVG is displayed in the browser the size depends on the size of the container/window.

The SVGs are converted to PNG successfully, but this one shell SVG.

Here is the JSFiddle: https://jsfiddle.net/8mqtLw6p/

var img = new Image();
var svg = new Blob([svgString], {
  type: "image/svg+xml;charset=utf-8"
});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
  ctx.drawImage(img, 0, 0);
  var png = canvas.toDataURL("image/png");
  var mg = document.createElement("img");
  mg.setAttribute("src", png);
  document.body.appendChild(canvas);
  document.body.appendChild(mg);
  DOMURL.revokeObjectURL(png);
};
img.setAttribute("src", url);
chrwahl
  • 8,675
  • 2
  • 20
  • 30
Ngọc Nguyễn
  • 339
  • 4
  • 16
  • Running the JS fiddle, there is `Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.` After fixing that (I just added an "id" attribute to the svg tag, like ` – qrsngky May 12 '23 at 03:20
  • @qrsngky Thank you for noticing. It's my bad for not checking the error before posting. I have updated JSFiddle link. The issue is the canvas is empty. – Ngọc Nguyễn May 12 '23 at 03:31
  • @NgọcNguyễn It's executing correctly on the most recent version of Chrome on Windows. Where is it failing for you? – David Mulder May 12 '23 at 03:40
  • @DavidMulder It's not working on FireFox. – Ngọc Nguyễn May 12 '23 at 03:48
  • I managed to make the canvas draw something on Firefox by specifying width and height attributes for the ` – qrsngky May 12 '23 at 04:06
  • @qrsngky Just came to answer the same No idea what's going on with Firefox there. – David Mulder May 12 '23 at 04:09
  • It's a bit strange that Firefox fails to compute the width and height of an SVG for a canvas (when they are not specified as attributes) when it can get them in other contexts. To get the same effect as in Chromium, you may need to manually calculate them based on the viewBox and the dimensions of the canvas. – qrsngky May 12 '23 at 04:17
  • @qrsngky FireFox fails us this time. Adding CSS to add size to the SVG does not work. Adding the `width` and `height` attributes works as you said. – Ngọc Nguyễn May 12 '23 at 04:26
  • Did you have a look at some of the other questions like https://stackoverflow.com/questions/70309786/how-to-render-svg-and-convert-to-png-in-react-native – chrwahl May 12 '23 at 08:13

1 Answers1

1

To be able to draw a bitmap image it needs a size (width and height). If the SVG does not have a size specified and you would like to copy it as it is rendered in the browser you can use getBoundingClientRect() to get the width and height.

Now, that you SVG is rather big I just replaced all the paths with a circle and modified the viewBox a bit.

let svg01 = document.getElementById('Capa_1');
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let img01 = document.getElementById('img01');

let BCR = svg01.getBoundingClientRect();
svg01.setAttribute('width', BCR.width);
svg01.setAttribute('height',BCR.height);

var xmlSerializer = new XMLSerializer();
var svgString = xmlSerializer.serializeToString(svg01);

var svg = new Blob([svgString], {
  type: "image/svg+xml;charset=utf-8"
});
var url = URL.createObjectURL(svg);

let img = new Image();

img.addEventListener('load', e => {
  URL.revokeObjectURL(e.target.src);
  canvas.width = e.target.width;
  canvas.height = e.target.height;
  ctx.drawImage(e.target, 0, 0);
  img01.src = canvas.toDataURL('image/png');
});
img.src = url;
<h3>The SVG:</h3>
<svg id="Capa_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 60">
  <style type="text/css">.st0 { fill: rgb(222, 83, 81); }.st1 { fill: rgb(23, 89, 149); }.st2 { fill: none; stroke: rgb(222, 83, 81); stroke-width: 2; stroke-miterlimit: 10; }.st3 { fill: rgb(33, 37, 88); }.st4 { fill: rgb(177, 223, 239); }.st5 { fill: rgb(255, 255, 255); }</style>
  <circle class="st1" cx="40" cy="30" r="25" />
</svg>
<hr>
<canvas id="canvas"></canvas>
<div>
  <img id="img01" />
</div>
chrwahl
  • 8,675
  • 2
  • 20
  • 30