0

I have a file input where the selected file has to be converted into an image url so I can add it to a canvas.

I have already found out that an SVG is an exception so I need to convert it into a Blob before turning it into an image Url. Making it into a Blob seems to work fine, but converting that into a url returns an unexpected result.

The relevant code:

function initiate() {
  var canvas = $("#canvas").get(0);
  var context = canvas.getContext("2d");
  var img = new Image();
  var imageSrcUrl;
  img.onload = function() {
    URL.revokeObjectURL(imageSrcUrl), {once: true}
    context.drawImage(
      img,
      0,
      0,
      img.width,
      img.height,
      0,
      0
    );
  }
  var extension = $("#logo").get(0).files[0].name.split('.').pop().toLowerCase();
  if(extension == 'svg') {
      var reader = new FileReader();
      reader.onload = function() {
          console.log(reader.result);
          var blob = new Blob([reader.result.toString()], {type: 'image/svg+xml'});
          console.log(blob);
          imageSrcUrl = URL.createObjectURL(blob);
          console.log(imageSrcUrl);
          img.src = imageSrcUrl;                  
      };
      reader.readAsText($("#logo").get(0).files[0]);
  } else {
      img.src = URL.createObjectURL(
        $("#logo").get(0).files[0]
      );
  }
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<form id="form">
  Select file here
  <input type="file" id="logo">
</form>
<br><br>
Canvas goes here
<canvas width="400" height="200" id="canvas"></canvas>
<br><br>
<button onclick="initiate();">Get svg into canvas</button>

the console logs:

  • reader.result: '< svg version="1.1" id="Laag_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 605.3 146.4" style="enable-background:new 0 0 605.3 146.4;" xml:space="preserve">...' (seems to be correct, but had to add a space at the beginning for formatting reasons here)
  • blob: Blob { size: 50643, type: "image/svg+xml" } (seems correct?)
  • imageSrcUrl: blob:https://ourdomain.be/a64506fb-00db-459a-8e17-1aab67dadd9b (with ourdomain.be effectively being our domain)

Am I missing something or doing something wrong? I got the method from https://dev.to/benjaminblack/using-a-string-of-svg-as-an-image-source-8mo

Thanks!

Mats Raemen
  • 1,781
  • 1
  • 28
  • 41
  • @RobertLongson I have edited my post to show the entire svg tag. Seems correct to me. Regarding the cross-domain question, the svg is selected locally through a file input so I guess this question is irrelevant? – Mats Raemen Sep 28 '22 at 21:24
  • please add a running snippet and clarify what you mean by "weird result" (related to image rendering?) – herrstrietzel Sep 29 '22 at 15:10
  • @herrstrietzel I have created a snippet. As you can see when you select an svg file and click the button, the generated url doesn't appear at all to be what it should – Mats Raemen Sep 29 '22 at 17:04

1 Answers1

0

You've specified dx and dy values..
These values could be used for selecting an area of a pixel image before rendering on canvas.
These arguments won't work for svg images.

Change the drawImage arguments to:

context.drawImage(
  img,
  0,
  0,
  img.width,
  img.height
);

function initiate() {
  var canvas = $("#canvas").get(0);
  var context = canvas.getContext("2d");
  var img = new Image();
  var imageSrcUrl;
  img.onload = function() {
    URL.revokeObjectURL(imageSrcUrl), {once: true}
    context.drawImage(
      img,
      0,
      0,
      img.width,
      img.height
    );
  }
  var extension = $("#logo").get(0).files[0].name.split('.').pop().toLowerCase();
  if(extension == 'svg') {
      var reader = new FileReader();
      reader.onload = function() {
          console.log(reader.result);
          var blob = new Blob([reader.result.toString()], {type: 'image/svg+xml'});
          console.log(blob);
          imageSrcUrl = URL.createObjectURL(blob);
          console.log(imageSrcUrl);
          img.src = imageSrcUrl;                  
      };
      reader.readAsText($("#logo").get(0).files[0]);
  } else {
      img.src = URL.createObjectURL(
        $("#logo").get(0).files[0]
      );
  }
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<form id="form">
  Select file here
  <input type="file" id="logo">
</form>
<br><br>
Canvas goes here
<canvas width="400" height="200" id="canvas"></canvas>
<br><br>
<button onclick="initiate();">Get svg into canvas</button>

Sanitizing

The above snippet might not work for svgs missing namespace attributes xmlns and xmlns:xlink.

function initiate() {
  var canvas = $("#canvas").get(0);
  var context = canvas.getContext("2d");
  var img = new Image();
  var imageSrcUrl;
  img.onload = function() {
    URL.revokeObjectURL(imageSrcUrl), {
      once: true
    }

    let width = img.width;
    let height = img.height;
    //adjust canvas width and height
    canvas.setAttribute('width', width);
    canvas.setAttribute('height', height)
    context.drawImage(
      img,
      0,
      0,
      width,
      height
    );

  }
  var extension = $("#logo").get(0).files[0].name.split('.').pop().toLowerCase();
  if (extension == 'svg') {
    var reader = new FileReader();
    reader.onload = function() {

      //create svg fragment for sanitizing
      let svgCode = reader.result;
      let tmpSvgWrap = document.createElement('div');
      let fragment = document.createRange().createContextualFragment(svgCode);
      tmpSvgWrap.appendChild(fragment);
      let svg = tmpSvgWrap.querySelector('svg');

      //add xml namespace attributes 
      svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
      svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");

      var blob = new Blob([tmpSvgWrap.innerHTML], {
        type: 'image/svg+xml'
      });
      //add delay for safari
      setTimeout(function() {
        imageSrcUrl = URL.createObjectURL(blob);
        img.src = imageSrcUrl;
        console.log('imageSrcUrl', imageSrcUrl)
      }, 100)

    };
    reader.readAsText($("#logo").get(0).files[0]);
  } else {
    img.src = URL.createObjectURL(
      $("#logo").get(0).files[0]
    );
  }
}
canvas {
  max-height: 200px;
  border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<form id="form">
  Select file here
  <input type="file" id="logo" onchange="initiate();">
  <!-- 
            <button type="button" onclick="initiate();">Get svg into canvas</button>
        -->
</form>

<p>Canvas goes here</p>
<canvas width="400" height="200" id="canvas"></canvas>

The above snippet adds missing namespace attributes by creating a fragment via Range.createContextualFragment() method

  //create svg fragment for sanitizing
  let svgCode = reader.result;
  let tmpSvgWrap = document.createElement('div');
  let fragment = document.createRange().createContextualFragment(svgCode);
  tmpSvgWrap.appendChild(fragment);
  let svg = tmpSvgWrap.querySelector('svg');

  //add xml namespace attributes 
  svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
  svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
herrstrietzel
  • 11,541
  • 2
  • 12
  • 34