11

I'm creating a dynamically-generated SVG from HTML text content. It works fine in Chrome but Safari consistently throws an error when the SVG data is converted to PNG for downloading.

What has me puzzled is that there is no cross-browser stuff happening; this is all being generated/downloaded from a single source. Even so, including img.crossOrigin = ‘Anonymous’ has not helped Safari to overcome its problem with (presumably) the canvas's origin-clean state.

function makeImage(canvas, pHeight, content) {

var ctx = canvas.getContext('2d');
var svgElementCode = '<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600">' + 
    '<foreignObject width="100%" height="100%">' + 
    '<div xmlns="http://www.w3.org/1999/xhtml" style="height:' + (pHeight - 100) + 'px; font-size: 20px;">' + 
    content +  
    '</div>' + 
    '</foreignObject>' + 
    '</svg>';

var data = 'data:image/svg+xml,' + encodeURIComponent(svgElementCode);
var img = new Image();
img.crossOrigin = 'Anonymous';
img.src = data;

img.onload = function() {
    ctx.drawImage(img, 0, 0);
    var canvasdata = canvas.toDataURL("image/png");
    var pngimg = '<img src="'+canvasdata+'">';
    $("#shareImage").append($('<a href="' + canvasdata + '" title="file.svg">' + pngimg + '</a>'));      
    };

}

I don't know what canvas/SVG horrors may lie in other browsers, but for now I'd just like to see this working in Safari. Any ideas?

UPDATE

It seems that with the foreignObject tag the SVG cannot be written to canvas, yet this tag might be partly responsible for Safari's security exception (a dirty canvas). As soon as canvas.toDataURL is encountered by Safari, it throws the security error. In fact, it seems that even the svg-todataurl.js plugin--apparently written TO DO JUST THIS--cannot overcome this same basic problem in Safari.

Interestingly, this problem also happens in all versions of IE up until Edge (non IE anyway?); in Edge the canvas.toDataURL function works. Hmm.

UPDATE 2

@Kaiido's suggestion: Use html2canvas. If you're not doing any cross-origin stuff then this is all you might need for rendering DOM elements as pixels. Thanks Kaiido.

UPDATE 3

html2canvas: NEGATORY. Try downloading the test image generated on their examples page, using Safari. Same problem, so not a solution.

cbmtrx
  • 591
  • 8
  • 16
  • 2
    Remove the foreignObject tag, I think that's what's making Safari throw. – Robert Longson Nov 28 '15 at 14:20
  • This caused the SVG not to draw at all. However, when I researched the `foreignObject` tag it says "The contents of foreignObject are assumed to be from a different namespace." I wonder if the foreignObject tag can be replaced with something else. – cbmtrx Nov 28 '15 at 15:11
  • depends what you want to do, an SVG rect or circle etc would work. Safari won't render html into a canvas though if that's what you're after. – Robert Longson Nov 28 '15 at 15:12
  • Actually it does--rather nicely--but it won't let you go the next step and right-click/link to SAVE it. – cbmtrx Nov 28 '15 at 15:35
  • It actually seems that Safari does taint the canvas when a foreign object is painted on it. Also, you should know that IE will taint the canvas for any svg painted on your canvas. And if there is no cross-origin needed, don't set it or your browser won't be able to load the image. – Kaiido Nov 28 '15 at 16:01
  • @RobertLongson The SVG image itself is created just fine but it's not downloadable. Only ONCE you attempt to convert it from SVG>PNG using canvas.toDataURL does Safari fail b/c of security exception. – cbmtrx Nov 28 '15 at 16:08
  • @Kaiido I hadn't been setting `img.crossOrigin` before, but as it wasn't working in Safari anyway I tried it here based on a recommendation in another SO question. Either way Safari chokes at the `canvas.toDataURL` line. – cbmtrx Nov 28 '15 at 16:10
  • No, in latest Safari and all browers that support the cross-origin attribute, you'll have a "Cross-origin image load denied by Cross-Origin Resource Sharing policy." or alike if you set the attribute while the server is unable to provide the good headers. But Yes this is not your core issue, since it is Safari's security settings that will taint the canvas if a foreignObject is painted onto the canvas (actually I think it's more the img tag containing the foreignObject which is marked as unsafe). As I said in my comment above, IE will even taint the canvas whenever you paint an svg on it. – Kaiido Nov 28 '15 at 16:16
  • the foreignObject doesn't implies at all a cross-origin source.. Just a different type of elements. You are mixing a lot of ideas... – Kaiido Nov 28 '15 at 16:21
  • @Kaiido And, as I understand from my testing, there is no way to write an SVG of a DOM element to a canvas without the foreignObject tag. Catch-22? – cbmtrx Nov 28 '15 at 16:21
  • @Kaiido See 2nd paragraph: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject: "The contents of foreignObject are _assumed_ to be from a different namespace." – cbmtrx Nov 28 '15 at 16:23
  • 1
    Why do you want to pass through the svg and foreignObject ? There are libraries to transform your html markup to pixels (e.g html2canvas) – Kaiido Nov 28 '15 at 16:23
  • Yes that's exactly what I said : "You are mixing a lot of ideas..." namespaces" are for markup. A way to know what kind of element it is. cross-origin is about communication between servers. – Kaiido Nov 28 '15 at 16:25
  • I guess because I had gone down one path already...I had only seen 1 mention of html2canvas, but maybe that's where I should be. Thx. – cbmtrx Nov 28 '15 at 16:25
  • foreignObject are not supported by IE (don't know about edge) and the rendering are really variable across browsers when painted back to a canvas. You should avoid this way. – Kaiido Nov 28 '15 at 16:27
  • @kaiido Never mind: html2canvas doesn't work in Safari either. It gets as far as I got in producing an image, but it is **STILL** not downloadable. I'll keep searching. – cbmtrx Nov 28 '15 at 17:13
  • @RobertLongson, I don't get it. `cross-origin` is an attribute you set on media elements to tell the server that you have credentials to talk with him, or that you haven't ('anonymous'). It's all about http request. What does it have to do with `` ? I do agree that the canvas will be `tainted` for security reasons but a "cross-origin" flag ? If you could point me to something about it. – Kaiido Nov 28 '15 at 17:15
  • @cbmtrx this example is also tainted on FF – Kaiido Nov 28 '15 at 17:18
  • I get html2canvas working on the same browsers my kluge was already working on. IE and Safari still fail for SVGs as downloadable files. – cbmtrx Nov 28 '15 at 17:20
  • 1
    @kaido I meant that Safari will taint the canvas if the SVG contains foreignObject, Firefox will not. – Robert Longson Nov 28 '15 at 17:40
  • @cbmtrx you will first have to clean your svg from `` you have and maybe from unclean ``or `` resources. html2canvas with a simple svg does work on Safari : http://jsfiddle.net/6z8goksq/ – Kaiido Nov 29 '15 at 03:51
  • 1
    Kaiido: I think you misunderstand the problem here: foreignObject is REQUIRED to render html to an svg. Yes, you can render a simple box or something like that without using it, but if you want to render html to an svg (which is what this question is about) then foreignObject is the only way to do it. – CpnCrunch Nov 02 '17 at 16:13
  • This appears to be fixed in the latest version of Safari. – CpnCrunch Dec 11 '18 at 16:33

0 Answers0