【问题标题】:Problems with getting canvas dataURI from svg with foreignObject使用 foreignObject 从 svg 获取画布 dataURI 的问题
【发布时间】:2023-03-26 01:27:02
【问题描述】:

您好,我有以下问题。

我正在生成 SVG 图像 (https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas)。

图像生成正确,看起来没问题。 现在我需要获取数据 URI,但每次我尝试从 canvas.toDataURL() 获取该信息时,都会出现此消息 Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': 污染的画布可能无法导出。( …)

我创建了一些示例代码来说明这种情况。

</!DOCTYPE html>
<html>
<head>
    <title>SVG to PNG</title>
</head>
<body>
    <canvas id="canvas" style="border:2px solid black;" width="200" height="200">
</canvas>


<script type="text/javascript">

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
               '<foreignObject width="100%" height="100%">' +
               '<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
                 '<em>I</em> like ' + 
                 '<span style="color:white; text-shadow:0 0 2px blue;">' +
                 'beer</span>' +
               '</div>' +
               '</foreignObject>' +
            '</svg>';

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

var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml'});
var url = domURL.createObjectURL(svg);

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

img.src = url;

</script>
</body>
</html>

此代码生成以下图像

这是正确的,但是当这些行被执行时

var dataURL = canvas.toDataURL(); console.log(dataURL);

它抛出错误。我在图像 onload 方法中执行此操作,以允许图像完成绘制到画布。如果我尝试从 onload 方法之外获取 dataURL,画布还没有完成加载,所以它会给我一个空图像。 我一直在寻找很多,但我还没有找到答案。这与 CORS 有关。我已经安装了 chrome 插件 CORS 并添加了 img,问题是这只是一个示例,基于 CORS 的解决方案没有用,因为我正在开发一个正在运行的应用程序在一个残缺不全的 chromium 浏览器上(我的意思是浏览器只显示网络应用程序,除了与应用程序交互之外,你不能在那里做任何事情)。

请注意,您可以使用

获取数据 URI

inspect --> network --> 选择图片并在源面板中打开,这里就是复制图片作为数据 uri。 我以这种方式获得 base64 图像,如果我使用这样的解码器 (http://base64online.org/decode/),它会正确显示图像。 所以问题肯定是我在绘制画布之前获取了数据 URI。

有什么想法吗?

提前致谢!

问候

毛罗

【问题讨论】:

标签: javascript image canvas svg


【解决方案1】:

这与 CORS 无关。您没有向外部资源发出任何请求。

问题在于绘制 svg(而且当它包含 html 元素时)对于浏览器来说是一个非常敏感的操作。

这就是为什么这种技术有很多限制(你不能加载外部资源,脚本被忽略,你不能从外部 CSS 设置内容的样式,所有默认浏览器和操作系统样式都被禁用以避免指纹识别等.).
我不为 safari 和 chrome 工作,所以我不能确定,但​​我认为他们无法在其中一个方面提供足够的安全性(safari 在 v9 中实现了这一点,它是 known issue in chrome too) .

所以他们所做的就是污染画布,锁定其任何导出方法。 (请注意,每当出于类似的安全原因将任何 svg 绘制到画布上时,IE 。

如果您想要栅格化 DOM 元素,那么您应该解析 DOM 及其应用的所有样式,并使用画布绘图方法重现它。
这样做,您可以绕过大多数安全问题,并且没有 UA 会污染画布。有些图书馆就是这样做的,最著名的是html2canvas

如果你想要绘制这张图片,那么你可以在不使用foreignObject的情况下重写它(svg文本比canvas有更多的选项),然后使用像canvg这样的库在你的canvas上渲染它 (因为否则 IE 会像之前所说的那样污染画布).


注意:chrome 的问题可以解决,但我不确定这是个好主意,它在 Safari 和 IE

var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');

var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
  '<foreignObject width="100%" height="100%">' +
  '<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
  '<em>I</em> like ' +
  '<span style="color:white; text-shadow:0 0 2px blue;">' +
  'beer</span>' +
  '</div>' +
  '</foreignObject>' +
  '</svg>';


var img = new Image();
// instead of a blobURL, if we use a dataURL, chrome seems happy...
var url = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(data);

img.onload = function() {
  c.width = this.width;
  c.height = this.height;
  ctx.drawImage(img, 0, 0);
  var dataURL = canvas.toDataURL();
  console.log(dataURL);
};

img.src = url;
&lt;canvas id="c"&gt;&lt;/canvas&gt;

【讨论】:

  • 您的解决方法适用于 chrome、firefox 和 safari :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-03
  • 1970-01-01
  • 2019-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多