fabricjs / fabric.js

Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser
http://fabricjs.com
Other
28.52k stars 3.47k forks source link

Allow exporting self-contained SVG files #519

Open raichu opened 11 years ago

raichu commented 11 years ago

Currently, the image elements are exported with their "ordinary" URLs. Since SVG supports data URIs, it should be possible to embed these images and make the SVG output of the canvas self-contained.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

kangax commented 11 years ago

You can do this manually at the moment and it should work, as far as I can tell. Marking as possible feature.

kangax commented 11 years ago

By the way, if we're talking about fully self-contained files, we'd need to at least also take care of fonts by embedding glyph definitions. Don't remember offhand, but I think SVG supports this.

raichu commented 11 years ago

You mean by editing toSVG() of Canvas? It seems to be supported indeed: http://www.w3.org/TR/SVG/fonts.html

raichu commented 11 years ago

So far, I found that the xlink:href part of an Image is written by getSvgSrc(), which is set to fabric.Image.prototype.getSrc, a function that simply returns this.getElement().src || this.getElement()._src;. Is there really a way of getting a data URI out of this object?

As far as I have found, toDataURL is a function on the whole canvas, and is not defined for a single image, so the only way appears to be using a (hidden) ancillary canvas, paint the image on it, and run toDataURL on the ancillary canvas, and then destroy it.

Or am I missing something?

Edit: An alternative could be using using XMLHttpRequest + FileReader for remote files and FileReader for local files, and run readAsDataURL on load event, but this is asynchronous whereas toSVG is not.

kangax commented 11 years ago

Yeah, you're missing fabric.Image.prototype.toDataURL which does pretty much what you described in 2nd paragraph :)

kangax

On Tue, Apr 2, 2013 at 8:46 AM, raichu notifications@github.com wrote:

So far, I found that the xlink:href part of an Image is written by getSvgSrc(), which is set to fabric.Image.prototype.getSrc, a function that simply returns this.getElement().src || this.getElement()._src;. Is there really a way of getting a data URI out of this object?

As far as I have found, toDataURL is a function on the whole canvas, and is not defined for a single image, so the only way appears to be using a (hidden) ancillary canvas, paint the image on it, and run toDataURL on the ancillary canvas, and then destroy it.

Or am I missing something?

— Reply to this email directly or view it on GitHubhttps://github.com/kangax/fabric.js/issues/519#issuecomment-15758468 .

raichu commented 11 years ago

Yay :) One less thing to worry about. Thanks!

raichu commented 11 years ago

I just tried to change the function

    getSrc: function() {
      return this.getElement().src || this.getElement()._src;
    }

to return this.toDataURL('png'), but it doesn't work. I tried several other things such as this.callSuper('toDataURL', 'png') or this.prototype.toDataURL('png'), but none of them work. I'm sorry that I'm a bit new to Javascript and all that magic that emulates the true OOP; could you give me some pointers as to how and where I can use fabric.Image.prototype.toDataURL?

kangax commented 11 years ago

This is because toDataURL is asynchronous — http://fabricjs.com/docs/symbols/fabric.Object.html#toDataURL

raichu commented 11 years ago

Then it's no different than doing XMLHttpRequest + FileReader. So eventually, it can't be done? I don't see why what I describe in the 2nd paragraph has to be asynchronous though since it all could be done synchronously.

kangax commented 11 years ago

It's async because we're cloning an image before rendering it on "placeholder" canvas. Image is cloned so that original wouldn't be affected by transformations required to render it on a "placeholder" canvas. And cloning an image is an async process.

Now that I look at it, I think we might be able to remove clone step and just modify the same object, saving its original values (left, top, etc.) before rendering it onto "placeholder" canvas. I think that could work just as well (even be quicker) and would make toDataURL sync.

I'll tinker with it when I get a chance.

raichu commented 11 years ago

You'd be my savior :)

tiptronic commented 4 years ago

Is there any interest in adding this option today?

asturur commented 4 years ago

I remember we did something for it. Did you give a look at image toSVG implementation?

tiptronic commented 4 years ago

Yes - I saw the modifications, but these seem only to affect the situation, if I want to get out a single layer/object.

In my case, I have a canvas contiaining some (layered) objects. E.g. Rect, Image (img.src="..") and an Oval. When exporting to SVG using canvas.toSVG I want to embed the image as base64. So what I do is: canvas.toSVG({suppressPreamble:true}, function(svg, a, b) { return svg; } But here I get only the full svg-element containing the xlink:href as src. Could it be, that I MUST overwrite the getSvgSrcprototype to get the dataUrl or an image?

tiptronic commented 4 years ago

It seems this is everything required:

fabric.Image.prototype.getSvgSrc = function() {
       return this.toDataURL();
}
canvas.toSVG({ suppressPreamble: true });
asturur commented 4 years ago

Well i remember we plan for this to be an option. But to do so, we need to convert the image at export time. Not a big deal, since if this is what the dev need, at some point he has to do it too.

I have not time to prioritize this over the other open things.

You can override the method as you pointed out, using the toDataUrl without any transformation ( or if the object is rotated, you will get a rotated dataUrl that is what you do NOT want )

tiptronic commented 4 years ago

OK - understood. Thanks for the explanation - so to keep the original image I have to convert the originalElement toDataURL if I intend to get the 'raw' (unmodified) image, right?

asturur commented 4 years ago

the Object.toDataUrl offers you the ability to skip transformations.

http://fabricjs.com/docs/fabric.Object.html#toDataURL

The withoutTransform option.

tiptronic commented 4 years ago

@asturur Bummer! I didn't see that in the docs - sorry! That's perfect! Grazie.