Search code examples
javascriptcsscanvassvgcanvg

Canvg not supporting css


I have inline svg with is loaded dynamically from server. I want that svg to be manupulated or modifed using inline css. I have searched for converting that modified svg into png or base64 image. After long search i have decided to stick with Canvg.

Now the problem is that it renders the inline style attributes like background and border radius.

This is my svg.

<svg height="550" width="550" viewBox="0 0 512 512" id="svg">
<path d="m309 139c0-111-32-139-53-139-21 0-53 28-53 139l-173 122c-2 1-4 4-4 7l0 46c0 6 5 10 11 9l166-37c6 49 16 93 23 139l-63 49c-2 1-4 4-4 6l0 23c0 7 6 11 12 8l85-38 85 38c6 3 12-1 12-8l0-23c0-2-2-5-4-6l-63-49c7-46 17-90 23-139l166 37c6 1 11-3 11-9l0-46c0-3-2-6-4-7z m-53-88c-11 0-21 3-30 7 3-13 15-22 30-22 15 0 27 9 30 22-9-4-19-7-30-7z"></path>
</svg>

Which looks like this:

Raw Svg Preivew

Remember the path is dynamically loaded from server. So cannot modify it.

Now i want to manupulate it something like this.

<svg style="background: grey none repeat scroll 0% 0%; fill: white; padding: 70px; border-radius: 98px;" height="550" width="550" viewBox="0 0 512 512" id="svg">
      <path d="m309 139c0-111-32-139-53-139-21 0-53 28-53 139l-173 122c-2 1-4 4-4 7l0 46c0 6 5 10 11 9l166-37c6 49 16 93 23 139l-63 49c-2 1-4 4-4 6l0 23c0 7 6 11 12 8l85-38 85 38c6 3 12-1 12-8l0-23c0-2-2-5-4-6l-63-49c7-46 17-90 23-139l166 37c6 1 11-3 11-9l0-46c0-3-2-6-4-7z m-53-88c-11 0-21 3-30 7 3-13 15-22 30-22 15 0 27 9 30 22-9-4-19-7-30-7z"></path>      </svg>

Which would look like this:

Manupulated Image

But after rendering using canvg script. It renders as raw file. It just includes the fill color. Nothing else.

Is there any other way to do that. Using canvas or svg elements.

Please Help!!! Thanks in advance


Solution

  • Most svg to canvas libraries don't support external resources (xlink attributes, images, and CSS).

    The only way I've found to do it is to append those external resources into the svg node, then use the canvas.toDataURL() method to draw your svg onto the canvas.

    I am writing a small script that do handle this and here is a dump of the CSS parser I made which will loop through all document's stylesheets and only append the ones that do have an influence on inner elements.

    var parseStyles = function() {
        var styleSheets = [],
            i;
        // get the stylesheets of the document (ownerDocument in case svg is in <iframe> or <object>)
        var docStyles = svg.ownerDocument.styleSheets;
        // transform the live StyleSheetList to an array to avoid endless loop
        for (i = 0; i < docStyles.length; i++) {
            styleSheets.push(docStyles[i]);
        }
        if (styleSheets.length) {
            // getDef() will return an svg <defs> element if already into the node, or will create it otherwise
            getDef();
            svg.matches = svg.matches || svg.webkitMatchesSelector || svg.mozMatchesSelector || svg.msMatchesSelector || svg.oMatchesSelector;
        }
    
        // iterate through all document's stylesheets
        for (i = 0; i < styleSheets.length; i++) {
            // create a new style element
            var style = document.createElement('style');
            // some stylesheets can't be accessed and will throw a security error
            try {
                var rules = styleSheets[i].cssRules,
                    l = rules.length;
                // iterate through each cssRules of this stylesheet
                for (var j = 0; j < l; j++) {
                    // get the selector of this cssRules
                    var selector = rules[j].selectorText;
                    // is it our svg node or one of its children ?
                    if ((svg.matches && svg.matches(selector)) || svg.querySelector(selector)) {
                        // append it to our <style> node
                        style.innerHTML += rules[j].cssText + '\n';
                    }
                }
                // if we got some rules
                if (style.innerHTML) {
                    // append the style node to the clone's defs
                    defs.appendChild(style);
                }
            } catch (e) {
                console.warn('unable to get some cssRules : ', e);
            }
        }
        // small hack to avoid border and margins being applied inside the <img>
        var s = clone.style;
        s.border = s.padding = s.margin = 0;
        s.transform = 'initial';
    };
    

    var svg = document.querySelector('svg');
    var doSomethingWith = function(canvas) {
      document.body.appendChild(canvas)
    };
    
    
    
    var parseStyles = function() {
      var styleSheets = [],
        i;
      // get the stylesheets of the document (ownerDocument in case svg is in <iframe> or <object>)
      var docStyles = svg.ownerDocument.styleSheets;
      // transform the live StyleSheetList to an array to avoid endless loop
      for (i = 0; i < docStyles.length; i++) {
        styleSheets.push(docStyles[i]);
      }
      if (styleSheets.length) {
        // getDef() will return an svg <defs> element if already into the node, or will create it otherwise
        getDef();
        svg.matches = svg.matches || svg.webkitMatchesSelector || svg.mozMatchesSelector || svg.msMatchesSelector || svg.oMatchesSelector;
      }
    
      // iterate through all document's stylesheets
      for (i = 0; i < styleSheets.length; i++) {
        // create a new style element
        var style = document.createElement('style');
        // some stylesheets can't be accessed and will throw a security error
        try {
          var rules = styleSheets[i].cssRules,
            l = rules.length;
          // iterate through each cssRules of this stylesheet
          for (var j = 0; j < l; j++) {
            // get the selector of this cssRules
            var selector = rules[j].selectorText;
            // is it our svg node or one of its children ?
            if ((svg.matches && svg.matches(selector)) || svg.querySelector(selector)) {
              // append it to our <style> node
              style.innerHTML += rules[j].cssText + '\n';
            }
          }
          // if we got some rules
          if (style.innerHTML) {
            // append the style node to the clone's defs
            defs.appendChild(style);
          }
        } catch (e) {
          console.warn('unable to get some cssRules : ', e);
        }
      }
      // small hack to avoid border and margins being applied inside the <img>
      var s = svg.style;
      s.border = s.padding = s.margin = 0;
      s.transform = 'initial';
      exportDoc();
    };
    
    var defs;
    var getDef = function() {
      // Do we have a `<defs>` element already ?
      defs = svg.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
      if (!defs.parentNode) {
        svg.insertBefore(defs, svg.firstElementChild);
      }
    };
    
    var exportDoc = function() {
      // check if our svgNode has width and height properties set to absolute values
      // otherwise, canvas won't be able to draw it
      var bbox = svg.getBoundingClientRect();
    
      if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width);
      if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height);
    
      // serialize our node
      var svgData = (new XMLSerializer()).serializeToString(svg);
      // remember to encode special chars
      var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
    
      var svgImg = new Image();
    
      svgImg.onload = function() {
        var canvas = document.createElement('canvas');
        // IE11 doesn't set a width on svg images...
        canvas.width = this.width || bbox.width;
        canvas.height = this.height || bbox.height;
    
        canvas.getContext('2d').drawImage(svgImg, 0, 0, canvas.width, canvas.height);
        doSomethingWith(canvas)
      };
    
      svgImg.src = svgURL;
    };
    
    parseStyles();
    rect {
      fill: red;
      padding: 25em;
      border: 25px solid yellow;
    }
    canvas {
      border: 1px solid green;
    }
    svg{
      background-color: skyblue;
    }
    <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1">
      <rect x="0" y="0" width="100" height="100" />
    </svg>