How to Include CSS style when converting svg to png
Asked Answered
S

1

5

i've created a simple SVG elment that get dowbloaded to png when clicking on a button, my solution is similiar to here


The basic idea is:
1.svg to canvas
2.canvas to dataUrl
3.trigger download from dataUrl

the problem is that when downloading the png file it doesnt include the css style applied on the svg my solution result

NOTICE - I know there is a workingaround solution by moving the styles "inline" on the elements like here or the recursive solution by digging the DOM tree and using getComputedStyle(element,null);

Questions:
1.what's the real reason and the solution for this issue.
(is the GPU acceleration related in anyway?)
2.how i still overcome this issue when using a custom font with Fontface

 <button id="btn">svg to png</button>

  <svg id="svg" width="200" height="200">
    <circle cx="50" cy="50" r="30" />
    <text class="svgTxt" x="0" y="100">Hen's SVG Image</text>
  </svg>
  <canvas id="canvas"  width="200" height="200"></canvas>

my CSS:

  /*adding exo2 font*/
    @font-face {
    font-family: 'exo_2black';
    src: url('./exo2font/Exo2-Black-webfont.eot');
    src: url('./exo2font/Exo2-Black-webfont.eot?#iefix') format('embedded-opentype'),
         url('./exo2font/Exo2-Black-webfont.woff') format('woff'),
         url('./exo2font/Exo2-Black-webfont.ttf') format('truetype'),
         url('./exo2font/Exo2-Black-webfont.svg#exo_2black') format('svg');
    font-weight: normal;
    font-style: normal;

}
/*change circle color depends on window size*/
@media screen and (min-width: 480px) {
    svg circle {
        fill: lightgreen;
    }
}
/*style on the svg text*/
    .svgTxt{
      font-family: 'exo_2black';
      font-size: 30px;
      fill: red;
    }

my code:

  //reference to elements
    var btn = document.querySelector('#btn');
    var svg = document.getElementById('svg');
    var svgTexts = svg.getElementsByTagName('text');
    var canvas = document.getElementById('canvas');
    //Style definitions for svg elements defined in stylesheets are not applied to the generated canvas. This can be patched by adding style definitions to the svg elements before calling canvg.
  //3.trigger download from dataUrl
    function triggerDownload(imgURI) {
      var evt = new MouseEvent('click', {
        view: window,
        bubbles: false,
        cancelable: true
      });

      var a = document.createElement('a');
      a.setAttribute('download', 'hen_saved_image.png');
      a.setAttribute('href', imgURI);
      a.setAttribute('target', '_blank');
      a.dispatchEvent(evt);
    }
    //btn click event
    btn.addEventListener('click', function () {
      // 1.svg to canvas
      var ctx = canvas.getContext('2d');
      var data = (new XMLSerializer()).serializeToString(svg);//serialize the svg element to string
      var DOMURL = window.URL || window.webkitURL || window;
      var img = new Image();
      var svgBlob = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });//A blob object represents a chuck of bytes that holds data of a file.
      var url = DOMURL.createObjectURL(svgBlob);//creates a DOMString containing an URL representing the object given in paramete
      $('svg').append(deletedSVGText);
      img.onload = function () {
        ctx.drawImage(img, 0, 0);
        DOMURL.revokeObjectURL(url);
        // 2.canvas to dataUrl
        var imgURI = canvas
          .toDataURL('image/png')
          .replace('image/png', 'image/octet-stream');// returns a data URI containing a representation of the image in the format specified by the type parameter

        triggerDownload(imgURI);
      };
      img.src = url;
    });
Sylviesylvite answered 10/1, 2017 at 14:49 Comment(0)
D
7

Question 1 (first half): what's the real reason (is the GPU acceleration related in anyway?)

No, GPU acceleration has nothing to do with it.
The broadest reason is privacy.

To draw your svg with drawImage you have to load your svg as an external document inside an <img> tag. SVG can be quite a complex image format for resources loading (it can literally require any kind of resource that could be required by any HTML document). Thus, it has been stated in specs that the same security as the one for <iframe> elements or <object> or similar ones should apply to <img> content and even stricter :

<img> content can not require any external resources, nor access to the main document.

Question 1 (second half): and the solution for this issue

You pointed to some SO questions already answering it, you could also just include all the stylesheets from the main document inside a <style> tag inside your parsed svg Node, before you do create the Blob from it. dumb implementation here

Question 2 : "how i still overcome this issue when using a custom font with Fontface"

For external resources, you have to encode it as dataURI, and include it in your svg node before you create the Blob. For font in particular, you'd set a font-face property in a <style> element.

So at the end, your svg would have something like

<defs>
  <style>
    /* all your parsed styles in here */
    @font-face {
     font-family: foo;
     src: url('data:application/font-woff;charset=utf-8;base64,...')
    }
  </style>
</defs>

in itself before you do extract its markup.

Dagny answered 10/1, 2017 at 15:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.