0

I'm trying to convert the following SVG string to image with JS:

<svg xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="100%" height="100%" class="ct-chart-line" id="test-chart" style="width: 100%; height: 100%;"><g class="ct-grids"><line y1="165" y2="165" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line><line y1="115" y2="115" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line><line y1="65" y2="65" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line><line y1="15" y2="15" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line></g><g><g series-name="performance" class="ct-series ct-series-a"><path d="M50,165C53.57,165,403.415,155,406.984,155" class="ct-line"></path></g><g series-name="bemchmark" class="ct-series ct-series-b"><path d="M50,165C53.57,165,403.415,45,406.984,45" class="ct-line"></path></g></g><g class="ct-labels"><foreignObject style="overflow: visible;" x="40" y="170" width="356.984375" height="20"><span class="lineChartLabel ct-horizontal ct-end" style="width: 357px; height: 20px" xmlns="http://www.w3.org/2000/xmlns/">Jan</span></foreignObject><foreignObject style="overflow: visible;" x="396.984375" y="170" width="30" height="20"><span class="lineChartLabel ct-horizontal ct-end" style="width: 30px; height: 20px" xmlns="http://www.w3.org/2000/xmlns/">Feb</span></foreignObject><foreignObject style="overflow: visible;" y="115" x="10" height="50" width="30"><span class="lineChartLabel ct-vertical ct-start" style="height: 50px; width: 30px" xmlns="http://www.w3.org/2000/xmlns/">0%</span></foreignObject><foreignObject style="overflow: visible;" y="65" x="10" height="50" width="30"><span class="lineChartLabel ct-vertical ct-start" style="height: 50px; width: 30px" xmlns="http://www.w3.org/2000/xmlns/">0.5%</span></foreignObject><foreignObject style="overflow: visible;" y="15" x="10" height="50" width="30"><span class="lineChartLabel ct-vertical ct-start" style="height: 50px; width: 30px" xmlns="http://www.w3.org/2000/xmlns/">1%</span></foreignObject><foreignObject style="overflow: visible;" y="-15" x="10" height="30" width="30"><span class="lineChartLabel ct-vertical ct-start" style="height: 30px; width: 30px" xmlns="http://www.w3.org/2000/xmlns/">1.5%</span></foreignObject></g></svg>  

I tried everything I could find, like simg.js or this script, for example:

var svg  = document.getElementById('test-chart'),
xml  = new XMLSerializer().serializeToString(svg),
data = "data:image/svg+xml;base64," + btoa(xml),
img  = new Image();
img.setAttribute('src', data)
document.body.appendChild(img)

But the generated image is always "broken", i.e. doesn't appear at all. The same script with a simpler SVG string works just fine.

I don't understand exactly what's wrong with my string, which is generated by chartist.js and displays nicely in the browser, but it seems the culprit are the <foreignObject> elements, as if I remove them, things work fine.

Is there a way to convert that SVG string into an image with JS?

chris
  • 649
  • 7
  • 26
  • Maybe the relativ width and height is the problem. Do you checked this? http://stackoverflow.com/questions/3975499/convert-svg-to-image-jpeg-png-etc-in-the-browser – Citrullin Apr 10 '16 at 19:39
  • What about using canvas instead of xml. Have a look at [this](http://stackoverflow.com/questions/27230293/how-to-convert-svg-to-png-using-html5-canvas-javascript-jquery-and-save-on-serve) – Ghita Apr 10 '16 at 19:41
  • @Ghita before drawing to a canvas, you have to pass your svg into an `` tag. Have a look at the post you linked to ;-) – Kaiido Apr 11 '16 at 01:23

2 Answers2

2

Your problem is that the xmlns attribute set on the <span> elements contained in the <foreignObjects> are wrong. Currently they are set to http://www.w3.org/2000/xmlns/ which is the default XML namespace. It should be http://www.w3.org/1999/xhtml since the element you are including belong to the xhtml namespace.

I do believe that you didn't set that yourself and that the library you do use did this.

You could leave its authors an issue report, but note that IE<11 didn't support this <foreignObject> tag, and that current Safari have some security restrictions on <img>tag representing such a tag (if you want to draw it back to a canvas, it will taint this one). So in my personal opinion the use of this tag is still not recommended.

Also note that, as others said, you have to set absolute width and height attribute to the root <svg> so it displays correctly in <img> tag.

Finally, you don't need to base64 encode your string, and the 'data:image/svg+xml; charset=utf8, ' dataURI header is the only one supported by all browsers from IE9 to Edge.

Here is how your markup should look like :

var svgData = (new XMLSerializer()).serializeToString(test)
var dataURI = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
var img = new Image();
img.src = dataURI;
document.body.appendChild(img);
<h3>
svg version
</h3>

<svg xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="500" height="200" class="ct-chart-line" id="test" style="width: 100%; height: 100%;">
  <g class="ct-grids">
    <line y1="165" y2="165" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line>
    <line y1="115" y2="115" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line>
    <line y1="65" y2="65" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line>
    <line y1="15" y2="15" x1="50" x2="406.984375" class="ct-grid ct-vertical"></line>
  </g>
  <g>
    <g series-name="performance" class="ct-series ct-series-a">
      <path d="M50,165C53.57,165,403.415,155,406.984,155" class="ct-line"></path>
    </g>
    <g series-name="bemchmark" class="ct-series ct-series-b">
      <path d="M50,165C53.57,165,403.415,45,406.984,45" class="ct-line"></path>
    </g>
  </g>
  <g class="ct-labels">
    <foreignObject style="overflow: visible;" x="40" y="170" width="356.984375" height="20" requiredExtensions="http://www.w3.org/1999/xhtml"><span class="lineChartLabel ct-horizontal ct-end" style="width: 357px; height: 20px" xmlns="http://www.w3.org/1999/xhtml">Jan</span></foreignObject>
    <foreignObject style="overflow: visible;" x="396.984375"
    y="170" width="30" height="20"><span class="lineChartLabel ct-horizontal ct-end" style="width: 30px; height: 20px" xmlns="http://www.w3.org/1999/xhtml">Feb</span></foreignObject>
    <foreignObject style="overflow: visible;" y="115" x="10" height="50" width="30" ><span class="lineChartLabel ct-vertical ct-start" style="height: 50px; width: 30px" xmlns="http://www.w3.org/1999/xhtml">0%</span></foreignObject>
    <foreignObject style="overflow: visible;" y="65" x="10" height="50" width="30" ><span class="lineChartLabel ct-vertical ct-start" style="height: 50px; width: 30px" xmlns="http://www.w3.org/1999/xhtml">0.5%</span></foreignObject>
    <foreignObject style="overflow: visible;" y="15" x="10" height="50" width="30" ><span class="lineChartLabel ct-vertical ct-start" style="height: 50px; width: 30px" xmlns="http://www.w3.org/1999/xhtml">1%</span></foreignObject>
    <foreignObject style="overflow: visible;" y="-15" x="10" height="30" width="30" ><span class="lineChartLabel ct-vertical ct-start" style="height: 30px; width: 30px" xmlns="http://www.w3.org/1999/xhtml">1.5%</span></foreignObject>
  </g>
</svg>
<h3>
img version
</h3>

So if you can't fix the issue before, you'll have to loop through all your HTML elements contained into the <foreignObject> elements and fix that manually (HTMLelem.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml'))

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I've been trying to adapt the above to generate a PNG image. Again, I tried everything I could find but nothing works with this corrected SVG string and PhantomJS! Any ideas? – chris Apr 11 '16 at 14:55
-1

you can use fabric js: http://fabricjs.com/

var addShape = function(shapeName) {

fabric.loadSVGFromURL('../assets/' + shapeName + '.svg', function(objects, options) {
  var loadedObject = fabric.util.groupSVGElements(objects, options);
   loadedObject.set({
     left: 0,
     top:0,
     angle:0
   }).setCoords();
  canvas.add(loadedObject);
  window.open(canvas.toDataURL('png'));
  });
};
Ajit kohir
  • 481
  • 5
  • 14