Home > Blockchain >  Converting SVG images to base64 PNG with browser
Converting SVG images to base64 PNG with browser

Time:06-30

I'm trying to convert a bunch of siblings SVG images to base64 PNG strings, using just the browser and plain Javascript, but for some reason beyond my knowledge I get the base64 PNG string of only the last SVG. Here is the HTML snippet:

<html>
<head><title>Browser SVG to PNG Converter</title></head>
<body bgcolor="#DDDDEE">
    <h1>Browser SVG to PNG Converter</h1>
    <div id="div_svg">
        <svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
            <rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
            <rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:1;fill:none;" /> </svg>
        <svg xmlns="http://www.w3.org/2000/svg" width="130" height="150">
            <rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,200,35);" />
            <rect x="30" y="30" width="70" height="90" style="stroke:#000000;stroke-width:2;fill:none;" /> </svg>
        <svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
            <rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
            <rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:3;fill:none;" /> </svg>
    </div>
    <br />
    <button id="btn_convert">Convert SVG to PNG and display it below</button>
    <br />
    <br />
    <canvas id="aux_canvas" style="display:none;"></canvas>
    <textarea id="output_png" style="width:90vw;height:40vh;display:block;">base64 PNG source will be displayed here:</textarea>
    <script>
    document.getElementById('btn_convert').addEventListener('click', function() {
        var svg_all = document.getElementById('div_svg').querySelectorAll('svg');
        var canvas = document.getElementById('aux_canvas');
        var win = window.URL || window.webkitURL || window;
        var img = new Image();
        img.addEventListener("load", function() {
            canvas.getContext('2d').drawImage(img, 0, 0);
            win.revokeObjectURL(url);
            document.getElementById('output_png').value  = "\n\n"   canvas.toDataURL("image/png");
        });
        for(let i = 0; i < svg_all.length; i  ) {
            let svg = svg_all[i];
            canvas.width = svg.getBoundingClientRect().width;
            canvas.height = svg.getBoundingClientRect().height;
            var data = new XMLSerializer().serializeToString(svg);
            var blob = new Blob([data], {
                type: 'image/svg xml'
            });
            var url = win.createObjectURL(blob);
            document.getElementById('output_png').value  = "\nGoing to load image..."
            img.src = url;
            document.getElementById('output_png').value  = "\nEnded loading image..."
        }
    });
    </script>
</body>
</html>

Input as hardcoded SVG and output to textarea are only for testing purposes, my ultimate goal is a Javascript function getting an array of SVG sources and returning the corresponding PNG images as an array of base64 strings.

CodePudding user response:

Image loading is asynchronous, you have to wait for each image to load before moving to the next, use Promise with asyn and await before each loading.

Here is a working example

const convertButton = document.querySelector("#btn_convert");
const output = document.querySelector("#output_png");
const svgs = document.querySelectorAll("svg");
const img = document.createElement("img");
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');
const URL = window.URL || window.webkitURL || window;

convertButton.onclick = async () => {        
  for(let i = 0; i < svgs.length; i  ) {
    output.value  = "\n---- Going to load image... \n\n";
    output.value  = await svgToBase64(svgs[i]);
    output.value  = "\n---- Ended loading image... \n\n";
  }
};
    
async function svgToBase64(svg) {
  const { width, height } = svg.getBoundingClientRect();
  canvas.width = width;
  canvas.height = height;

  const data = new XMLSerializer().serializeToString(svg);
  const blob = new Blob([data], {
      type: 'image/svg xml'
  });
  var url = URL.createObjectURL(blob);

  return new Promise((resolve) => {
    img.onload = () => {
      ctx.drawImage(img, 0, 0);
      URL.revokeObjectURL(url);
      resolve(canvas.toDataURL("image/png"));
    }
    img.src = url;
  });
}
body {
  background: #DDDDEE;
}
<h1>Browser SVG to PNG Converter</h1>
<div id="div_svg">
        <svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
            <rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
            <rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:1;fill:none;" /> </svg>
        <svg xmlns="http://www.w3.org/2000/svg" width="130" height="150">
            <rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,200,35);" />
            <rect x="30" y="30" width="70" height="90" style="stroke:#000000;stroke-width:2;fill:none;" /> </svg>
        <svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
            <rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
            <rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:3;fill:none;" /> </svg>
</div>
<br />
<button id="btn_convert">Convert SVG to PNG and display it below</button>
<br />
<br />
<textarea id="output_png" style="width:90vw;height:40vh;display:block;">base64 PNG source will be displayed here:</textarea>

  • Related