JavaScript:toDataUrl()抛出"安全错误:可能无法导出受污染的画布。"

时间:2016-08-25 14:56:38

标签: javascript html5 canvas svg

我有一个HTML5画布,我在svg上绘制图像。

HTML

<canvas id="canvas" width="600" height="320"></canvas>

的JavaScript

var DOMURL = window.URL || window.webkitURL || window;

var data = '<svg xmlns="http://www.w3.org/2000/svg" width="600" height="320">'+
                    '<foreignObject width="100%" height="100%">'+
                        '<style>'+
                             'foreignObject {'+
                                 'background-color: #000;'+
                                 'color: #fff'+
                                 'border-radius: 10px;'+
                             '}'+
                            'h1 {'+
                                'color: #2acabd;'+
                                'font: 25px arial;'+
                                'font-weight: bold;'+
                                'text-align: center;'+
                            '}'+
                            'h2 {'+
                                'margin: 0;'+
                                'color: #2acabd;'+
                                'font: 15px arial;'+
                            '}'+
                            'p {'+
                                'color: #fff;'+
                            '}'+
                        '</style>'+
                        '<div xmlns="http://www.w3.org/1999/xhtml" style="font-size: 40px;">'+
                            '<h1>Heading</h1>'+
                            '<div>'+
                                '<div id="details-wrapper">'+
                                    '<h2>Full Name</h2>'+
                                    '<p>Alan Johnson</p>'+

                                    '<h2>Date of Birth</h2>'+
                                    '<p>7th November 1988</p>'+

                                    '<p><span id="user-id">34329483028493284093284432</span></p>'+
                                '</div>'+
                            '</div>'+
                        '</div>'+
                    '</foreignObject>'+
                '</svg>';


var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
img = new Image();
img.setAttribute("crossOrigin", "anonymous");
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);

img.onload = function() {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
  console.log(canvas.toDataURL());
}
img.src = url;

(JS Fiddle:https://jsfiddle.net/LondonAppDev/qnpcg8th/1/

当我致电canvas.toDataURL()时,我得到例外:

(index):98 Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

我在Stack Overflow上看到了与此异常相关的许多其他问题和答案。我的问题是不同的,因为(正如你所看到的)我在任何时候都不包括我svg或画布中来自另一个域的任何图像。

我猜测问题是我正在使用DOMURL.createObjectURL创建对象网址。

我知道在不同的浏览器中执行此操作存在一些兼容性问题,但此应用只​​需要在Chrome中运行。此外,将文本直接绘制到画布上不是一种选择,我必须通过svg。

来完成

关于如何解决这个问题,并成功获得我画布的PNG的任何想法?

3 个答案:

答案 0 :(得分:14)

我通过将svg转换为数据URL而不是Blob来解决这个问题。

我删除了var url = DOMURL.createObjectURL(svg);并将img.src = url;替换为:

function buildSvgImageUrl(svg) {
    b64 = window.btoa(svg);
    return "data:image/svg+xml;base64," + b64;
}

img.src = buildSvgImageUrl(data);

现在它完美无瑕。

答案 1 :(得分:1)

出于安全和用户隐私原因,此 在画布上绘制<foreignObject>可以泄漏有关用户的几个信息,在此之前,chrome团队认为他们没有足够的安全措施来防止这种情况,因此他们确实污染了画布。

以下是关于此问题的chromium bug report

但是当OP发现时,他们有一个实现错误,忘记在dataURI版本上设置这个限制 我发布了这个bug report关于他们没有使用dataURI污染画布的事实。

所有这些都会导致前一个限制的影响,因此在下一版本的chrome中,我们应该能够在画布上绘制<foreignObject>而不会污染它,即使使用BlobURI也是如此。

但请注意,Safari仍然具有相同的限制(没有实现错误,即dataURI hack在那里没有工作),并且IE&lt;当任何 SVG被绘制到它时,Edge不支持<foreignObject>元素并污染画布,并且FF确实删除了所有UserAgent和OS样式(这会导致与您不同的结果)可以期待)。

一个真正的解决方法是不使用此 hack 来绘制元素,而是使用CanvasAPI绘制方法绘制所有HTML(就像html2canvas那样)。

答案 2 :(得分:1)

我同意接受的答案,但是它对我没有用.......这种类似的方法可以做到: Tainted Canvas with canvas.toBlob

这是代码:

let img = new Image();
let svg = document.getElementById('svg-node');
let url = "data:image/svg+xml;charset=utf-8," + (new XMLSerializer()).serializeToString(svg);
img.src = url;

希望我为您节省了一些时间! :)