Search code examples
imagesvgutf-8base64escaping

Alternative to unescape() and saving images as base64


I am trying to convert svg to png, using the code here as basis of my conversion.

Please note the following code (it has been shortened to only what's relevant to the error that I will describe later):

let svgData = new XMLSerializer().serializeToString(target); // 'target' is an svg element that is passed to us

let canvas = document.createElement('canvas');

let ctxt = canvas.getContext('2d');

let img = document.createElement('img');

img.setAttribute(
    'src',
    'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgData)))
);

ctxt.drawImage(img, 0, 0);

The above code works fine. However there is a problem with it. Notice that here is this line of code:

'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgData)))

In this line of code, the unescape() function is used, however it is deprecated, and I have to use an alternative. According to documentation, I should use decodeURIComponent(), but this not a viable solution, because when I update the code above with decodeURIComponent(), the result is:

'data:image/svg+xml;base64,' + window.btoa(decodeURIComponent(encodeURIComponent(svgData)))

So basically I would be encoding then decoding. Which is the same as doing this:

'data:image/svg+xml;base64,' + window.btoa(svgData)

Now, with the new updated code, if the source svg contains only English characters, everything is OK. However, if an svg contains characters other than English (e.g., contains the Japanese word トマト), then the next line of code, ctxt.drawImage(img, 0, 0); , throws an error. The error message is DOMException: String contains an invalid character.

So, to summarise so far:

  1. the original code works fine with both English and non-English characters, but uses the unescape() function which is depracated. So I should use something else.

  2. The updated code, which does not encode or decode, causes an exception when using non-English characters.

Now, question 1, what do I do to be able to create an image from an svg that contains non-English characters using base64?

As an alternative solution, I did this

'data:image/svg+xml;utf8,' + svgData

Basically, I did not use base64, but rather utf8. This worked just fine, but I am not sure if it is the correct solution.

So, question 2, is using utf8 a common practice? Is there any issue with it as opposed to base64?

Thank you.


According to the comment below from @RobertLongson , I updated the code to:

'data:image/svg+xml;utf8,' + encodeURIComponent(svgData)

This worked in svg elements containg either English and non-English characters.

Now, question 3, is this correct?

Apologies about the basic question. I am not familiar with this. Thanks.


Solution

  • With this example I'm trying to avoid using the mentioned functions for converting to base64 and instead use a FileReader and particular the readAsDataURL function for returning the data URI. I know the code is a bit more complicated with the callbacks, but it works.

    As I understand Google Translate トマト translates into 🍅.

    let svg01 = document.getElementById('svg01');
    let canvas = document.getElementById('canvas01');
    let img = document.getElementById('img01');
    let ctx = canvas.getContext('2d');
    
    canvas01.width = svg01.getAttribute('width');
    canvas01.height = svg01.getAttribute('height');
    
    let svgData = new XMLSerializer().serializeToString(svg01);
    
    // create a File object
    let file = new File([svgData], 'svg.svg', {
      type: "image/svg+xml"
    });
    
    // and a reader
    let reader = new FileReader();
    
    reader.addEventListener('load', e => {
      let img = new Image();
      // wait for it to got load
      img.addEventListener('load', e => {
        // update canvas with new image
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(e.target, 0, 0);
        // create PNG image based on canvas
        img01.src = canvas.toDataURL("image/png");
      });
      img.src = e.target.result;
    });
    // read the file as a data URL
    reader.readAsDataURL(file);
    <p>SVG embedded:</p>
    <svg id="svg01" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 20" width="300" height="60">
      <rect width="100" height="20" fill="silver" rx="5"/>
      <text font-size="7" x="50" y="10" text-anchor="middle"
      dominant-baseline="middle">Japanese word: トマト 🍅</text>
    </svg>
    <p>Canvas image:</p>
    <canvas id="canvas01"></canvas>
    <p>PNG image:</p>
    <p><img id="img01" /></p>