1

I'm trying to save the canvas to the file from the web page. The canvas is pretty complicated and has been built from several images (few semi-transparent .png files, 2 svg elements, and 4 other canvases) and no matter how I'm trying to save it, using canvas.toDataURL() method or using domtoimage [library] (https://github.com/tsayen/dom-to-image) or canvas2image or fileSaver or canvasToBLob libraries - the result is the same: an image without svg(and <image> element inside) parts. So i'm getting only the parts, that were built from canvas. I read about the 'tainted' canvases, but the images host server is the same as webpage is.

Moreover I'm confused, because if I saving the canvas by clicking RCM and selecting "Save image as" function - it saves exactly what I need - the full picture.

The whole code is huge and the saving options are different, so I will put here only one saving option:

domtoimage.toBlob(canvasElement)
  .then(function (blob) {
    console.log(blob);
    window.saveAs(blob, 'my-node.png');
  });

The picture on the left is what im getting when using 'save image as' feature in browser, and on the right is the image saved by js through the toBlob

enter image description hereenter image description here

will be happy to get any suggestions of how to save the full image automatically :)

Darkest
  • 49
  • 8
  • The svg overlay the canvas or are actually used to draw on the canvas ? Because svg is not in the same namespace as regular HTML, this may be a bit more tricky. – Simon Dec 12 '16 at 01:04
  • it's overlaying, and thanks for the question, I forgot to add the way how i'm drawing the svg on canvas. the structure of the source dom is approx like this:
    I'm drawing on the 'exporting' canvas, getting elements one after another, canvases first and then svg's with `ctx.drawImage(sourcecanvas, 0, 0);` `ctx.drawSvg(sourcesvg, 0, 0); **drawSvg** is the [canvg](https://github.com/canvg/canvg) builtin method. The result perfectly drawn in browser
    – Darkest Dec 12 '16 at 01:50
  • You need to draw the SVG onto the canvas if you want to save the included SVG. SVG is just an image so you can render it on the canvas with `ctx.drawImage(SVGObject,0,0)` – Blindman67 Dec 12 '16 at 08:02
  • @Darkest, are you sure all your images has been painted on the canvas when you call the export method ? Otherwise, you should have the same result as 'right-click + save As..' or nothing at all (in case of tainted canvas). – Kaiido Dec 12 '16 at 09:01
  • @Blindman67, according to Op's previous comment he already does it, + No it's not just `ctx.drawImage(SVGObject,0,0)` his svg is a DOMNode, so he first needs to serialize it, then encode it to a dataURL, but since it does contain some links to external data, he even needs to encode these to dataURL too before-hand, then he can load all this in an `` tag, and use it with `drawImage` but then he will loose support for all IE browsers below Edge because they do taint the canvas when an svg has been drawn to it. So if he has to support these browsers, he has to do as he does ;-) – Kaiido Dec 12 '16 at 09:06
  • May be of some help if not a dupe : http://stackoverflow.com/questions/34042910/convert-svg-to-png-with-applied-images-as-background-to-svg-elements/34043188#34043188 – Kaiido Dec 12 '16 at 09:12
  • @Kaiido thanks for helping, you was right about the time required for painting. Updated the question with self answer. – Darkest Dec 17 '16 at 08:01
  • @Darkest just put the answer as answer and mark it as accepted. That way this will be closed as solved. –  Dec 17 '16 at 08:15
  • kudo to @K3N's comment, post it as a self-answer instead of as an edit to your question. But `drawImage` is synchronous, `drawSVG` however is just a shortcut for the global `canvg` method, with some other params, which can accept a `renderCallback` parameter in its `options` parameter. Rendering of simple svg is synchronous too, but since here you are drawing `` elements, it can not be synchronous. So you can just call it like this : `rctx.drawSvg(rightExportScreen.svg.node.outerHTML, 0, 0, null, null, {renderCallback: onrendered}); function onrendered(){ domtoimage.toBlob... }` – Kaiido Dec 17 '16 at 08:46

1 Answers1

1

I'll take the opportunity of your edit to post this as a real answer, since you based yours (which you should have posted as a self-answer b.t.w) on a comment of mines asking you for more clarification.

So, as we found out, your problem was that all the images weren't loaded/drawn when you called the export method, hence, these images were missing in the output.

The canvg function, and its shortcut ctx.drawSvg, can be asynchronous. Normally, when only shapes are being rendered it is not, but since here you are loading external content, wrapped in an SVGImage (<image>) element, it can't be synchronous.

But, there is a renderCallback option that you can add to your canvg call. Since you do use the ctx.drawSVG, and that this method has some extra parameters (dx, dy, dwidth, dheight) compared to the original canvg, you need to call it like so :

ctx.drawSvg(yourSVGMarkup, 0, 0, null, null, {renderCallback: yourCallbackFunction}); 

var str = new XMLSerializer().serializeToString(document.querySelector('svg'));
canvas.getContext('2d').drawSvg(str, 0, 0, null, null, {
  renderCallback: function() {
    console.log('done');
  }
});
console.log('doing');
<script src="https://cdn.rawgit.com/canvg/canvg/master/canvg.js"></script>
<svg width="300" height="150" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" x="0" y="0" height="100" width="100" />
</svg>
<canvas id="canvas"></canvas>
Kaiido
  • 123,334
  • 13
  • 219
  • 285