Closed Bug 700533 Opened 13 years ago Closed 1 year ago

drawImage() fails silently when drawing an SVG image without @width or @height

Categories

(Core :: SVG, defect)

defect

Tracking

()

RESOLVED FIXED
120 Branch
Webcompat Priority P2
Tracking Status
firefox120 --- fixed

People

(Reporter: sephr, Assigned: dlrobertson)

References

(Blocks 1 open bug, )

Details

(Keywords: webcompat:platform-bug)

Attachments

(7 files, 1 obsolete file)

The default size of an <img> element is 0x0, so drawing an SVG image should be the same as drawing any other 0x0 image, due to it scaling down to 0x0. Idealy, the minimum dimensions of the SVG should be used, but roc has explained to me that this is not feasible to implement.
> The default size of an <img> element is 0x0 No, the default size is its intrinsic size.... which SVG doesn't always have. That said, the HTML5 spec actually specifies what to do here: If the image has no intrinsic dimensions, the concrete object size must be used instead, as determined using the CSS "Concrete Object Size Resolution" algorithm, with the specified size having neither a definite width nor height, nor any additional contraints, the object's intrinsic properties being those of the image argument, and the default object size being the size of the canvas element. with a link to http://dev.w3.org/csswg/css3-images/#default-sizing What that means in practice is that we end up in the third bullet point there, and attempt to make the <svg> as big as we can without running out of canvas. Does that behavior actually make sense?
As noted in bug 574330 comment 58 & 59, this bug seems to be the reason that the "…drawn to <canvas>…" chunk is blank at http://phrogz.net/SVG/svg_to_png.xhtml
(In reply to Boris Zbarsky (:bz) from comment #1) > That said, the HTML5 spec actually specifies what to do here: [...] > with a link to http://dev.w3.org/csswg/css3-images/#default-sizing That refers to the "default object size", which links here: http://dev.w3.org/csswg/css3-images/#default-object-size which defines a (different) default size for various image contexts (but sadly does not mention <canvas>)
Status: UNCONFIRMED → NEW
Ever confirmed: true
Ah, nevermind - I was interpreting "default object size" as talking about the default size of the _image_ (the input), but it's talking about the default size of the _canvas_. So, disregard comment 3.
bz also points out that on <canvas>, there are two distinct sizes: - |width| & |height| attributes (which define the internal virtual surface size used by the canvas) - |width| & |height| properties (which define the actual rendered size, on-screen) So if the width/height properties are larger than the attributes, then we effectively end up scaling up the canvas, and drawing it with "large pixels". The current spec text is unclear about which of these sizes we should use. fantasai suggests that I file a spec issue about this -- I need to do that at some point.
Given: <!-- heightAndWidthAttributes.svg has width="640" height="480" --> <img id="nono" src="noHeightOrWidth.svg" /> <img id="noyes" src="noHeightOrWidth.svg" width="100" height="200" /> <img id="yesno" src="heightAndWidthAttributes.svg" /> <img id="yesyes" src="heightAndWidthAttributes.svg" width="1" height="1" /> myCanvasContext.drawImage(document.querySelector('#nono'),0,0); myCanvasContext.drawImage(document.querySelector('#noyes'),0,0); myCanvasContext.drawImage(document.querySelector('#yesno'),0,0); myCanvasContext.drawImage(document.querySelector('#yesyes'),0,0); ...what do we expect to occur in each situation? For "noyes" (the case in http://phrogz.net/SVG/svg_to_png.xhtml) and "yesyes" and others I personally expected the SVG to be rasterized into the `<img>` object at the dimensions specified in the HTML. When the image does not have a size, I expect it to use the height/width of the SVG to determine the raster size. And for "nono"...I'm not sure what I expect. However, it's not my expectations that matter, but what the specs say. However, any clarification should ensure that all four of those cases are handled.
Assignee: nobody → dholbert
I have an issue which is, I believe, related to this bug: When I load a SVG data URL as an Image, and in the image's onload handler draw it onto a canvas, Firefox 18 throws NS_ERROR_NOT_AVAILABLE. At this point the image's width & height properties are indeed zero. Anyone knows why this is happening? The SVG file does have width and height set (in px). Let me know if you need any additional information. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="98.279px" height="25.336px" viewBox="0 0 98.279 25.336" enable-background="new 0 0 98.279 25.336" xml:space="preserve">
I'm not sure, offhand. Could you file a new bug at https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=SVG with a testcase (including the canvas) that reproduces it, and CC me? (Also: unassigning myself from this one, since I haven't gotten to it for a while and it likely won't be at the top of my list in the near term)
Assignee: dholbert → nobody
Filled my issue as bug 697743 & added dholbert to CC.
Sorry, bug 826562 it is.
Also just encountered this bug, but what stumped me was that we don't imply width/height from the viewBox attribute? Seems Chrome does this, might be something worth doing as a lot of major tools seem to not put width/height.
How does Chrome size the image exactly? I think we should try to use the same sizing behaviour as if the <img> was in the document and rendered. So for no width="" or height="" attribute but with a viewBox="" present, you would size it to the width of the containing block and to a height such that it maintains the aspect ratio of the viewBox. The problem then is what to use as the containing block size to base those off if the <img> (both for when the <img> is in the document and when it isn't). At a guess I would say to use the containing block of the <img> if it is in the document (and so the image will be the same size as the one rendered in the document), or using the 300x150 dimensions if not in the document.
Is there any known workaround, other than manually adding width="" or height=""? In our case, we have users supplying their own SVGs and we can't reasonable expect them to add width/height.
(In reply to Wesley Workman from comment #14) > Is there any known workaround, other than manually adding width="" or > height=""? Not currently. > In our case, we have users supplying their own SVGs and we can't > reasonable expect them to add width/height. Sure -- but what do you even expect to happen in this scenario? In particular, if there's no width=""/height/viewport in the user-supplied images, how big do you expect the SVG image to be when you call ctx.drawImage(mySVGImage, x, y)? And how should percents in the SVG be resolved in e.g. <rect height="100%" width="100%">? This is all well-defined when SVG is viewed directly (where the browser's viewport is the SVG's viewport-size) and when SVG is included as an <img> (because CSS defines fallback sizing behavior). But it's unclear what the right thing to do is in canvas drawImage, where there's no fallback.
Actually I did find a work around. I apologize for not sharing it. Here is the JSBin: http://jsbin.com/rizivo/1/edit?html,js,output . In that case, I'm taking the viewBox data and manually setting them as the height/width of the SVG. The JSBin doesn't illustrate this, but I only do this workaround when I catch the NS_ERROR_NOT_AVAILABLE exception. I'm not at all an SVG expert. So I don't know what the "right thing to do is". But I can tell you what I would expect as a naive web developer. That is for it to mimic the same behavior as webkit, falling back to viewBox and rendering the SVG at the same ratio.
(In reply to Wesley Workman from comment #16) > I'm not at all an SVG expert. So I don't know what the "right thing to do > is". But I can tell you what I would expect as a naive web developer. That > is for it to mimic the same behavior as webkit, falling back to viewBox and > rendering the SVG at the same ratio. Two things: (1) I should've specified -- I meant to ask what happens (and/or what Chrome does) if there was no viewBox present. (I assume you have to consider that case as well, if you're having to worry about the no-width-or-height case.) (2) The SVG viewBox *might* give you a useful size, but its size doesn't necessarily represent a reasonable amount of screen real-estate. It just represents a region of the infinite SVG canvas (in the author's units) which should be scaled to fit the viewport. So you could e.g. have a SVG document with viewBox="0 0 10000 10000", but it's not actually intended to be rendered at 10,000px wide. Whatever we do, it's probably worth striving for consistency with Blink/WebKit & considering their fallback behavior. If anyone here has a chance to test that thoroughly (perhaps posting a testcase with a variety of scenarios with subsets of [width,height,viewBox] specified), to answer heycam's question from comment 13, that would definitely be useful for testing & considering different options here!
In the example given ( http://phrogz.net/SVG/svg_to_png.xhtml ) the code that draws the SVG onto the canvas uses the version that explicitly specifies target width and height. Why does that not work either? There are no open questions for Firefox in that case or are there?
It depends. I think I agree there are no open questions, if there's a viewBox and drawImage is called with a width & height provided. If there's no viewBox, though (and/or if there's a specified 'width' but no 'height' on the SVG, for example -- or an arbitrary percent value for one of them), it's less clear to me what should happen. Anyway, IIRC we fail early even in this "no open questions" case because this internal drawing path currently requires an intrinsic size, and fails if it doesn't get one, even if we *could* proceed using slightly different information.
drawImages() no longer throws per bug summary -- it simply doesn't draw the image, which is arguably worse :'(
Today i encountered same problem and opened a bug ticket. After some comment i got reference to this bug, and i paste here the information i have: Chrome, Safari, Edge have different behaviour, but all get dimensions different from 0 and can actually paint the image over the canvas. Firefox and ie11 go 0 dimensions and cannot paint anything. I tried to assign the image object width and height but this does not change any result. i tried assigning them before load and after load event but does not change the result. Those are behaviour of other browsers: Chrome ( best solution in my opinion ): take 300x150 (naturalWidth/naturalHeight) and fit the image in that area. Safari 10: display image with viewBox dimensions Edge: make a 300x150 image and put the svg with correct aspect ratio at center. What feels wrong most of all is the inability to render after displaying the svg in the dom.
platform-rel: --- → ?
Summary: drawImage() throws when drawing an SVG image without @width or @height → drawImage() fails silently when drawing an SVG image without @width or @height
Tim, did you intentionally set platform-rel?
platform-rel: ? → ---
Flags: needinfo?(timdream)
I guess I did, without really understand what it means. How do I escalate a bug on browser-parity that affects developer experiences?
Flags: needinfo?(timdream)
There is no browser parity here, everyone is different, see comment 24 for instance. I guess the first thing therefore would be to get w3c to define what should happen.
Specs refers to container. no width and no height means 100% of contanier. We do not have a proper container here. I search on w3c svg spec some kind of use case that could fit this but i did not find anything. Still the image can render in the browser alone where parent viewport is defined by browser window. I can understand painting an image on a canvas is different but i think the common sense is that something should be rendered. the `drawImage(svg, dx, dy)` and `drawImage(svg, sx, sy, sw, sh, dx, dy, dw, dh)` are problematic use cases that let think that a default fallback viewport size is a nice thing. Or at least throw something in the console, fail not silently so that if an app cannot render at least can collect errors.
(In reply to Tim Guan-tin Chien [:timdream] (please needinfo) from comment #26) > I guess I did, without really understand what it means. How do I escalate a > bug on browser-parity that affects developer experiences? Good question. platform-rel is for partner-related things ("platform relations") so it's not the best option. One option is needinfoing the triage owner of a bug but I guess emailing dev-platform?

Here is a workaround where you can use the built SVG document to set the width/heights.

const svgDocument = new DOMParser().parseFromString(atob(base64EncodedSVG), "image/svg+xml");
svgDocument.documentElement.width.baseVal.valueAsString = svgDocument.documentElement.width.baseVal.value.toString();
svgDocument.documentElement.height.baseVal.valueAsString = svgDocument.documentElement.height.baseVal.value.toString();
base64EncodedSVG = btoa(new XMLSerializer().serializeToString(svgDocument));

This works because the document knows it should be 100% (default in valueAsString) and it calculates the value correctly from the viewBox in baseVal.value.

So it's quite weird that it doesn't "just work" without this silly workaround.

Firefox own SVGDocument knows the value to use since it exposes the correct value in width.baseVal.value. So they follow the other browsers there.
So different stances within the browser...

Why doesn't this work with the overload specifying a size, i.e. drawImage(img, 0, 0, width, height)? Surely that size could then be used as the box size to display the SVG in, and then there is at least a workaround for drawing SVGs to a canvas. Is that the same as this bug or is it a different case?

This is affecting at least one site using canvas-based map software mapbox-gl in conjuction with SVG icons for markers, as can be seen in https://webcompat.com/issues/64352.

Webcompat Priority: --- → ?
Webcompat Priority: ? → P2

Both square should be the same visible.
No red should be visible.

  • Gecko (Firefox) Red
  • Blink (Edge), WebKit (Safari) Green
Severity: normal → S3

The severity field for this bug is relatively low, S3. However, the bug has 11 duplicates and 13 votes.
:jwatt, could you consider increasing the bug severity?

For more information, please visit auto_nag documentation.

Flags: needinfo?(jwatt)

The last needinfo from me was triggered in error by recent activity on the bug. I'm clearing the needinfo since this is a very old bug and I don't know if it's still relevant.

Flags: needinfo?(jwatt)
Duplicate of this bug: 1807613

Hi! I can confirm this bug is still present in Firefox 110. I've just ran into it when trying to visit the site of an official state bank from Argentina: https://github.com/webcompat/web-bugs/issues/118373
Could the severity of this bug be risen? There are several examples of it preventing the normal functioning of a site.

I made an overview of how browsers handle different SVGs with/without width and height specifiers: https://codepen.io/atjn/full/GRyMWvZ
AFAICT the only major issue is this issue in Firefox. It is a major pain point because drawImage is the best method of taking an unknown user image and sanitizing it to a known format, but there are many SVGs that only have a viewBox, making them completely impossible to import with this method.
Here is another website that this is affecting: https://github.com/webcompat/web-bugs/issues/101816

I made an overview of how browsers handle different SVGs with/without width and height specifiers: https://codepen.io/atjn/full/GRyMWvZ

That's a really helpful/thorough testcase, thanks!

Could the severity of this bug be risen? There are several examples of it preventing the normal functioning of a site.

Yeah, let's bump this to S2 at this point... It's been accumulating webcompat issues for a while. Let's try to get this prioritized and fixed/mitigated.

Severity: S3 → S2
Assignee: nobody → drobertson

Did a little debugging of this today when I had some spare cycles and it looks like this is actually due to us not handling percent values in GetHeight/GetWidth for SVG images. We currently exit early and fail here and here if the width is a percent value (we do the same for height). This comment and bug 1112533 (comment 3) shed some further light on this.

Do not return a failure for the image height or width when the intrinsic size of
an SVG is a percent value. We now return a value calculated by returning the
percent of the containing block height or width.

Attached file megatest 1

Here's a test to exercise various options with width, height, and viewBox on SVG drawn to canvas.

Attachment #9335041 - Attachment description: megatest 2 (clarified a bit, and added giant pink background to SVG) → megatest 2 (clarified/reordered a bit, and added giant pink background to SVG)

Looking at "megatest 2":
Firstly, all browsers agree on the final row. i.e. if the <svg> width and height attributes are both present and both pixel-valued (the final row of the testcase), then all browsers use those as the intrinsic size.

For the other rows (where width or height are missing or percent-valued): WebKit renders all of those rows the same (though not quite the same as they render the final row).

Summing up their behavior:

  • If the SVG image has a missing or percent-valued width or height attribute, then WebKit ignores the width and height attributes entirely, and instead looks at the viewBox, using the width and height components (both of them) from the viewBox as an intrinsic size. (as can be seen in 2nd and 3rd column of this testcase, which have two different viewBox options)
  • If the viewBox is missing (as in the first column of this testcase), WebKit just uses the fallback 300x150 replaced element intrinsic size.

[side note: megatests 1 and 2 have a stray code-comment Will draw the image as 300x227 which is just copypasta that I forgot to delete; ignore it, sorry if it adds confusion when looking at those test sources]

So comparing Chrome on megatest 2 vs. 3 is interesting. Sorry if the following is confusing; it's a little bit stream-of-consciousness as I try to make sense of things.

There are two different sizes that the browser has to fill in, for each drawImage operation in these megatests:
(1) What size region of the <canvas> should we draw the image into?
(2) What size should we use as the size for the SVG viewport, when rendering the SVG?

In megatest 3, the answer to question (1) is always 80x160 (hardcoded into the drawImage function-call as args).
In megatest 2, Chrome's answer to question (1) is the full [width,height] of the canvas, if the SVG image has a percent [width,height] -- though if there's a viewBox, Chrome instead seems to size the area-to-fill using the viewBox's aspect-ratio, scaled up to meet the edges of the canvas. (So for example, in the upper right corner of megatest 1, they don't draw any pink, i.e. nothing from outside of the viewBox).

And then as for question (2): if there's a pixel value in both axes, all browsers use that (and shrink the viewBox to fit it). Otherwise, Chrome just renders the viewBox itself. Or if there's no viewBox and a percent valued width or height, then Chrome resolves the percent value against the size that they arrived at for question (1). So for example in the first cell in the second-to-last row of megatest 2: Chrome uses the height of the canvas as the height of the area-to-be-drawn-into, and then it uses a viewport that's sized at 60% of that height, which then ends up meaning the square looks vertically-stretched when we actually draw the rendered SVG to fill the full canvas height.

The bottom line here is that things are pretty wildly non-interoperable for the edge cases.

The simplest thing to start out here might be to just use the fallback 300x150 size if the SVG image is missing either a pixel-valued width or height. I suspect that'll be easy, will make us match WebKit (so some interop improvement), and is probably good enough for common cases to work, as a quick fix to stop the pain here.

I think Chrome's more-nuanced behavior is probably what the spec actually calls for, though.

FWIW the relevant spec text here seems to be under Establish the source and destination rectangles as follows in https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-drawimage-dev (dw/dh = destination width & height [omitted in megatests 1 and 2], sw/sh = source width and height [omitted in all tests here I think]. Quoting the spec, bold section being the most-relevant bit:

If not specified, the dw and dh arguments must default to the values of sw and sh, interpreted such that one CSS pixel in the image is treated as one unit in the output bitmap's coordinate space. If the sx, sy, sw, and sh arguments are omitted, then they must default to 0, 0, the image's intrinsic width in image pixels, and the image's intrinsic height in image pixels, respectively. If the image has no intrinsic dimensions, then the concrete object size must be used instead, as determined using the CSS "Concrete Object Size Resolution" algorithm, with the specified size having neither a definite width nor height, nor any additional constraints, the object's intrinsic properties being those of the image argument, and the default object size being the size of the output bitmap.

Thanks! This helps clear things up a lot... Still trying to digest it a bit, but things are definitely more clear.

(In reply to Daniel Holbert [:dholbert] from comment #70)

[...]
The simplest thing to start out here might be to just use the fallback 300x150 size if the SVG image is missing either a pixel-valued width or height. I suspect that'll be easy, will make us match WebKit (so some interop improvement), and is probably good enough for common cases to work, as a quick fix to stop the pain here.

Yeah, I think this is probably a good target for this bug.

I think Chrome's more-nuanced behavior is probably what the spec actually calls for, though.

Agreed.

FWIW the relevant spec text here seems to be under Establish the source and destination rectangles as follows in https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-drawimage-dev (dw/dh = destination width & height [omitted in megatests 1 and 2], sw/sh = source width and height [omitted in all tests here I think].

Nice! That does seem relevant.

Attachment #9331273 - Attachment description: Bug 700533 - Handle percent sizes for SVG images. r=dholbert → Bug 700533 - Fix the canvas drawImage method to handle SVG images with unspecified or percent size on the root. r=dholbert
Attachment #9331273 - Attachment description: Bug 700533 - Fix the canvas drawImage method to handle SVG images with unspecified or percent size on the root. r=dholbert → Bug 700533 - Do not use negative values to determine percentages for svg intrinsic sizes. r=dholbert

Add tests for CanvasRenderingContext2D.drawImage() when the image to be drawn is
an SVG with no width or height attribute.

Depends on D176974

When computing the size of an image for CanvasRenderingContext2D.drawImage(),
use the default size of 300 x 150 if no width or height was given.

Depends on D182487

See Also: → 1547776
See Also: → 1846565
Attachment #9341767 - Attachment description: Bug 700533 - Use default size for canvas surface if image has an invalid size. r=dholbert → Bug 700533 - Use a fallback size for canvas drawImage if the given image has no intrinsic size (e.g. certain SVG images). r=dholbert

Comment on attachment 9331273 [details]
Bug 700533 - Do not use negative values to determine percentages for svg intrinsic sizes. r=dholbert

Revision D176974 was moved to bug 1853991. Setting attachment 9331273 [details] to obsolete.

Attachment #9331273 - Attachment is obsolete: true
Pushed by drobertson@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/88857e8df4e2 Test SVG default sizing for canvas drawImage. r=dholbert https://hg.mozilla.org/integration/autoland/rev/189a06036d6f Use a fallback size for canvas drawImage if the given image has no intrinsic size (e.g. certain SVG images). r=dholbert
Status: NEW → RESOLVED
Closed: 1 year ago
Resolution: --- → FIXED
Target Milestone: --- → 120 Branch

:dlrobertson is this something you want to mention in the Fx120 relnotes?

Flags: needinfo?(drobertson)

(In reply to Dianna Smith [:diannaS] from comment #77)

:dlrobertson is this something you want to mention in the Fx120 relnotes?

I don't think this is relnote worthy, since this is more of a bugfix, but if you think it is worthwhile, I'd be happy to add the relevant info.

Flags: needinfo?(drobertson)
See Also: → 1870502
Blocks: 1673741
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: