使用 foreignObject 从 svg 获取画布 dataURI 的问题 [英] Problems with getting canvas dataURI from svg with foreignObject

查看:30
本文介绍了使用 foreignObject 从 svg 获取画布 dataURI 的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下问题.

我正在生成 SVG 图像(

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

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

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

注意你可以获取数据URI,用

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

有什么想法吗?

提前致谢!

问候

毛罗

解决方案

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

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

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

所以他们所做的是污染画布,锁定其任何导出方法.(请注意,IE < Edge 也会在出于类似安全原因将任何 svg 绘制到画布时污染画布).

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

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

<小时>

注意:chrome 的问题可以解决,但我不确定这是个好主意,它仍然无法在 Safari 中工作,也无法在 IE 中使用 <Edge 和 chrome 可能只是忘记阻止它,并且会在下一个版本中阻止它,无论如何,方法如下:

var canvas = document.getElementById('c');var ctx = canvas.getContext('2d');var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">'+''+'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">'+'<em>我</em>像'+'<span style="color:white; text-shadow:0 0 2px blue;">'+'啤酒</span>'+'</div>'+'</foreignObject>'+'</svg>';var img = new Image();//而不是 blobURL,如果我们使用 dataURL,chrome 似乎很高兴...var url = '数据:图像/svg+xml;charset=utf8, ' + encodeURIComponent(data);img.onload = 函数(){c.width = this.width;c.height = this.height;ctx.drawImage(img, 0, 0);var dataURL = canvas.toDataURL();控制台日志(数据URL);};img.src = url;

<canvas id="c"></canvas>

Hi I have the following problem.

I'm generating a SVG image (https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas).

The image is generated correctly and looks ok. Now I need to get the data URI, but everytime I try to get that from canvas.toDataURL() this message appears Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.(…)

I've created some sample code to illustrate the situation.

</!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>

This code generates the following image

This is correct, but then when these lines are executed

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

It throws the error. I'm doing this inside the image onload method to allow the image finish drawing to the canvas. If I try to get the dataURL from outside the onload method, the canvas hasn't finished loading, so it would give me an empty image. I've been searching a lot but I couldn't find the answer yet. This is something related to CORS. I've installed the chrome plugin CORS and added the img, the thing is that this is just an example, and a CORS based solution would not be useful, because I'm working on an application that's running over a crippled chromium browser (I mean the browser just shows the web app, you can't do anything there except interact with the app).

To notice you can obtain the data URI, with

inspect --> network --> select the image and open in sources panel and there it is just copy image as data uri. I get the base64 image that way, and if I use some decoder like this (http://base64online.org/decode/) it's showing the image correctly. So definitely the problem is that I'm getting the data URI before the canvas is drawn.

Any ideas?

Thanks in advance!

Regards

Mauro

解决方案

This has nothing to do with CORS. You are not doing any request to an external resource.

The issue is that drawing an svg (moreover when it contains html elements) is a really sensitive action for browsers.

That's why there are a lot of limitations on this technique (you can't load external resources, scripts are ignored, you can't style the content from external CSS, all default browser and OS styles are disabled to avoid fingerprinting etc.).
I don't work for safari nor chrome, so I can't tell for sure, but I think they aren't able to provide enough security on one of these points (safari realised it in v9, and it's a known issue in chrome too).

So what they do, is that they taint the canvas, locking any of its export methods. (Note that IE < Edge did also taint the canvas whenever any svg had been drawn to the canvas for similar security reasons).

If what you want is to rasterize DOM elements, then you should parse the DOM and all its applied styles, and reproduce it with canvas drawings methods.
Doing so, you can bypass most security issues, and no UA will taint the canvas. Some library out there do exactly this, the most famous being html2canvas.

If what you want is to draw this image, then you can rewrite it without using a foreignObject (svg text has more options than canvas one), then use a library like canvg to render it on your canvas (because otherwise IE will taint the canvas as said previously).


Note : chrome's issue can be workedaround, but I'm not sure it's a good idea, it will still not work in Safari, nor in IE < Edge, and chrome may just have forgotten to block it too and will in next releases, anyway, here is how :

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;

<canvas id="c"></canvas>

这篇关于使用 foreignObject 从 svg 获取画布 dataURI 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆