whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.13k stars 2.67k forks source link

Allow SVGSVGElement as image parameter in CanvasRenderingContext2D.drawImage() #10289

Open hfmanson opened 6 months ago

hfmanson commented 6 months ago

What is the issue with the DOM Standard?

Currently a SVGSVGElement can only be rendered in a HTML canvas by

addEventListener("load", () => { function drawSVGSVGElement(svgsvgElement) { // serializing the containing document to a string const s = new XMLSerializer(); const str = s.serializeToString(svgsvgElement.ownerDocument); // creating a blob out of it
let blob = new Blob([str], {type: 'image/svg+xml'}); // convert the blob to an url let url = URL.createObjectURL(blob); // create an image element and assign the src property
let image = document.createElement('img'); image.src = url; // adding an EventListener which draws the loaded image.*/
image.addEventListener('load', function () { const canvas = document.getElementById('canvas'); const context = canvas.getContext("2d"); context.drawImage(image, 0, 0); URL.revokeObjectURL(url); }); } function getSVGSVGElement() { const parser = new DOMParser(); const doc = parser.parseFromString(svgSource, "image/svg+xml"); return doc.documentElement; } const svgsvgElement = getSVGSVGElement(); drawSVGSVGElement(svgsvgElement); }, false);


Demo in [jsfiddle](https://jsfiddle.net/hfmanson/vzf9g4sa/17/)

If CanvasRenderingContext2D.drawImage() also allows to have an image parameter of type `SVGSVGElement` the code would be
```js
function drawSVGSVGElement(svgsvgElement) {
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext("2d");
  context.drawImage(svgsvgElement, 0, 0);
}
Kaiido commented 6 months ago

One of the advantages of having to go through an <img> element is that this allows for easier sandboxing of the SVG image. For instance here I modified the original fiddle to show both the in DOM <svg> and the one drawn on the canvas. We wouldn't want the :visited selector to be leaked in the canvas where it can be read back.

So this means that the browser can't go directly from the image rendered into the doc to the canvas, it will have to recompute it there, or to taint the canvas which puts quite a limit on the feature.

I'd personally propose that if we do this for <svg>, why not do it for any element? IIRC there was already a proposal for such an "any element as source" idea, though I can't find it right now.

hfmanson commented 6 months ago

Indeed, CanvasRenderingContext2D.drawImage() does not render the image, but its bitmap which is a bit confusing (better name drawBitmap?). So passing a SVGSVGElement is not appropriate for drawImage(). bitmap only is demonstrating the above. When drawing the image on the canvas opacity is not applied, just the image bitmap is drawn.

I could change the proposal to "add CanvasRenderingContext2D.drawDocument() method", which has the same signature a drawImage apart for its first parameter which would be a document instead of an image-like parameter with a bitmap. The semantics can be the same as when a SVG image src is supplied, which also converts a SVG to a bitmap.

I think having a drawImage with any element as parameter is not possible, e.g. it's not possible to draw a SVG circle without it's svg document. Is that correct?

annevk commented 6 months ago

@Kaiido makes a good point that it needs the same restrictions as <img> SVG, so perhaps this isn't worth doing. Supporting arbitrary (styled) node trees without revealing anything about the end user is rather involved and not something anyone has been willing to take on thus far.

hfmanson commented 6 months ago

having read @Kaiido's comment I think the semantics of drawImage() having a SVGSVGElement as parameter should be as if it would be rendered through an <img> element.

schenney-chromium commented 3 months ago

There is an active proposal for converting any DOM content into a canvas image, and even more so render HTML content directly to the canvas. This is in early discussions pending discussions among browser vendors, though there is a prototype in Chromium (not fully functional). See the Explainer.

Incubation will happen at WICG.