Closed Bug 737852 Opened 12 years ago Closed 6 years ago

HTML5 Canvas TextBaseline Top looks different in FF than all other browsers

Categories

(Core :: Graphics: Canvas2D, defect)

All
Windows 7
defect
Not set
normal

Tracking

()

RESOLVED INVALID

People

(Reporter: gmurray, Unassigned)

References

Details

Attachments

(1 file)

Attached image ffbug.png
User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11

Steps to reproduce:

var ctx = $("#testCanvas")[0].getContext("2d");
			ctx.font = "40px 'Segoe UI',Arial,sans-serif";
			ctx.fillStyle = "gray";
			ctx.fillRect(0,0,500,500);
			ctx.textBaseline = "top";
			ctx.fillStyle = "red";
			ctx.fillText("testing test", 0, 0);


Actual results:

The resulting text seems to be aligned flush in firefox, meanwhile there is more space between the top of the canvas and the baseline top in every other browser. I don't know if FF is not using the top of the em square as is specced, or whether it is deciding where the top of the em square is incorrectly. 

The effect is more obvious for some fonts. I guess those have a higher em square top?


Expected results:

Either firefox is wrong here, or every other browser is. If textBaseline is top I expect the text to render in the same position on every browser.
Component: Untriaged → Canvas: 2D
Product: Firefox → Core
QA Contact: untriaged → canvas.2d
I see the same effect with ctx.textBaseline = 'middle';
Safari, Webkit and Chrome seems to place the fonts differently and the offset seems to be the same as in the report.
I have the same problem with baseline top. 
Works fine with FF 8.x
The testcase is in a jsfiddle now: http://jsfiddle.net/j87bY/1/ (easier than attaching it here)

There's definitely a difference, which looks like we're using a different box for determining top/middle/bottom compared to at least IE10 (no Chrome handy at the moment).  I don't know which one is more correct, though.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Hardware: x86_64 → All
Version: 10 Branch → Trunk
Definitely appreciated if this could be brought inline with the other browsers, unless there is a very good reason not to. This issue and the Chrome aliased draws are the two largest differences in rendering in C2D currently (or at least the ones we hear the most complaints on with CreateJS/EaselJS).
Getting reports of issues with canvas rendering differences due to this bug in emscripten as well.
I discovered this bug too. Trying to overlay a span to match the position of the drawn text showed Gecko is drawing the text with |line-height: 1em| and Chrome is drawing the text with their |line-height: normal|, i.e. ~1.1em.

You can see my result here.

http://jsfiddle.net/timdream/uyCCL/

Given the fact there is no |textLineHeight| property available in CanvasRenderingContext2D, I would say a 1em default makes more sense itself for Canvas 2D API. However, it is inconsistent with default style of HTML.

(now I need to find a way to detect the difference and set browser-specific style accordingly :-/)
Noted that I have just added another test on jsfiddle and use |textBaseline = 'middle'| and line-height: 1em. It would work on both browsers.
I've also verified this issue, it seems not only the position is different, but also the color is different, that's a messy.
Here is an image which highlights some of the various discrepancies.

https://cloud.githubusercontent.com/assets/154613/6344952/cc864dfe-bbcd-11e4-86d0-ee43a1ae32a0.png

The red is rendered in a canvas with `context.textBaseline = 'top'` and the black is DOM.

In latest Chrome, Safari, and IE, nearly all of the red is eclipsed.

This image was created using part of a test suite I’m writing for an upcoming open-source typography metrics JS library. This issue is the only issue holding up release, since it renders typography metrics such as the descender position invalid when translating back to the DOM context (in Firefox).
I report the same differences with textBaseline "top" in Firefox 37 : the canvas text is really "glued" to the top but the DOM text has some space above.
In Chrome 42 and IE9/10/11, both texts have some space above.
I have a similar testcase, not sure if the same bug or not: http://bespin.cz/~ondras/ff-font-bug/

Is it a dupe, or shall I submit a new issue?
We are 3 years into this bug, which seems like it should be a pretty minor fix. It's continuing to cause significant issues, and I'm really reluctant to do browser sniffing to fix it in case it ever does get fixed.

Can we get an update? Is this likely to get fixed? Is it definitely not going to get fixed? At least if we know for sure its going to remain unfixed forever we can try to address it via browser sniffing.

Appreciate any input you can provide.
Here, here, Grant.
It's totally bogus not to give this any priority.
It's a critical positioning issue.
Another vote for fixing this!
Another vote for fixing this!
(In reply to ondra zara from comment #11)
> I have a similar testcase, not sure if the same bug or not:
> http://bespin.cz/~ondras/ff-font-bug/
> 
> Is it a dupe, or shall I submit a new issue?

your testcase proves the issue is FF only. yet, we are in 2016 and the issue isn't yet fixed!
It's been four years. I'm going put a message in my canvas application now, that Firefox is no longer supported because it's not being maintained. There are too many long-standing bugs, some nearly as old as Firefox itself, that are simply being ignored.
Peter, is this under gfx team's radar?
Flags: needinfo?(howareyou322)
Flags: needinfo?(milan)
(In reply to abduljawad.mahmoud from comment #16)
> > http://bespin.cz/~ondras/ff-font-bug/

It may be useful to note that IE doesn't align them either, so I think we should stick with the fonts that are available on all systems.  

http://jsfiddle.net/msreckovic/e7sw167g/4/ has a fork of :timdream's jsfiddle from comment 6 with font choice down to Arial/sans-serif (getting rid of Segoe UI), all HTML text top aligned and CSS changed so that the top one uses 1.1em, the middle 1em, the bottom 0.9em.

Firefox aligns to the bottom one (0.9em) on Windows and OS X, Chrome and IE on Windows, Chrome and Safari on OS X align to the top one (1.1em.)  Changing the font keeps the same alignment on Chrome/IE, while it changes things for Firefox (e.g., using Times aligns to 1em in Firefox.)

Jonathan, thoughts?
Flags: needinfo?(milan)
Flags: needinfo?(jfkthame)
Flags: needinfo?(howareyou322)
I don't suppose this is what people want to hear, but I'd say that Firefox's <canvas> text baseline-alignment behavior is more spec-conformant than Chrome's, for example.

Consider this modified testcase:
  http://jsfiddle.net/wdkf1sL0/

In each section here, we have three fragments of <canvas> text side-by-side, using the 'top', 'middle' and 'bottom' values of textBaseline. According to [1], these should align the given anchor point to the top, middle and bottom respectively of the em-square. So if we move the anchor point down by 1/2-em increments for each fragment, the effective vertical position of the em-square should stay the same.

In Firefox, this behaves exactly as expected: the "Top", "Mid" and "Bot" text is all aligned to the same level. Not so in Chrome, which seems to be interpreting the 'top' and 'bottom' textBaseline values as top and bottom of something other than the em square, perhaps the font's maxAscent and maxDescent metrics?

Now, as for how this relates to HTML/CSS line-height and baseline alignment, that's another question again.... trying to make <canvas> text (which has no concept of line-height or inter-line spacing, just absolutely-positioned runs of text) precisely match HTML text is probably an inherently fragile (or futile?). The position of the text baseline within the line box will depend on the relationship between the font's ascent and descent, and neither ascent nor descent will necessarily match the 'top' and 'bottom' of the em square.


[1] https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-textbaseline
Flags: needinfo?(jfkthame)
Should this be INVALID, and moved to Chromium issue tracker etc.?
(In reply to Tim Guan-tin Chien [:timdream] (please needinfo) from comment #21)
> Should this be INVALID, and moved to Chromium issue tracker etc.?

At least to give Chromium a chance to argue differently.  Can you do that?
Flags: needinfo?(timdream)
I encountered this bug and had to add offsetY when drawing text
Hi,

the canvas textBaseline = "top" behavior in Firefox really seems to be the one which is wrong!
Especially when compared to all other browsers today.

The current behavior in Chrome, IE11, Edge and Safari (tested on almost all systems where each browser is available) is the SAME and beside of this also very USEFULLY - it allows taking the dom element offset sizes to place the text the same way as the browser itself does when rendering the text.

I have implemented a html to canvas renderer which works almost pixel perfect in all browsers except of Firefox because of this bug. And instead of trying ugly workarounds I would really prefer a fix on browser side...

The canvas specs also say:

"textBaseline=top - The text baseline is the top of the em square"

Here a link and quote from the related Firefox source code:
https://hg.mozilla.org/mozilla-central/file/fb4cf6555081/content/canvas/src/nsCanvasRenderingContext2D.cpp#l2723

 switch (CurrentState().textBaseline)
    {
    case TEXT_BASELINE_TOP:
        anchorY = fontMetrics.emAscent;
        break;

That means Firefox is 'only' using the 'emAscent' size as vertical offset - and this seems to be wrong...

E.g. check and compare to this picture:
https://www.microsoft.com/Typography/OTSpec/images/IMG00294.GIF

So please consider fixing this!
Matching all other browsers here would be a big help.

If required a can make a small test case for this scenario.

Thanks and best regards,
Klaus
For reference, current source in FF:

  * http://searchfox.org/mozilla-central/rev/7419b368156a6efa24777b21b0e5706be89a9c2f/dom/canvas/CanvasRenderingContext2D.cpp#4328

And in Blink:

  * https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp?l=927&rcl=b2ac08f1fcc9482fb5eb337b264233f3fdc39d65
  * https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp?l=854&rcl=b2ac08f1fcc9482fb5eb337b264233f3fdc39d65

I'm not too much into fonts or graphics, but both Gecko and Blink seem to draw at (x, y + ascent) for horizontal text + textBaseline = "top".

Blink seems to use the ascent as an integer in the common case, while Gecko seems to always use the floating point metric. I'm not sure this is the explanation for the behavior difference though, it'd be really sad this would end up being a rounding issue :(.

Jonathan, perhaps you could take another look at this given the above links and give a better diagnostic? Sorry for not being able to help more.
Flags: needinfo?(jfkthame)
Thanks for the quick reaction!

I was also searching further - maybe it's also about how the ascent is calculated (or get from the system) in each browser. I mean that the font-metric values itself are different ones.

E.g. in Chrome the ascent value seems to be scaled relative to a 'units/EM' size:
https://cs.chromium.org/chromium/src/third_party/skia/src/ports/SkFontHost_FreeType.cpp?l=1329

For Firefox I wasn't able to locate the source of the font-metric values yet to allow a comparison...
> https://www.microsoft.com/Typography/OTSpec/images/IMG00294.GIF

Just to be clear, that picture is for a single character.  A single character has an ascent and descent above the baseline, plus the baseline position within the em square.  That baseline position is generally uniform across all the glyphs in a font (that's what makes it a baseline).

The emAscent of fontMetrics in the canvas code in Gecko is character-agnostic (since the entire struct is character agnostic), and is the ascent of the em square above the font baseline.  All that assuming that you actually have a particular font, of course.  So it is in fact the distance from the top of the em square to the normal baseline of the font, and using emAscent to offset things means putting the baseline at the top of the em square, which is what the spec says to do.  At least assuming emAscent is not totally misnamed.

And Jonathan is right that the testcase linked in comment 20 should, per spec, show "Top", "Mid", and "Bot" all positioned the same vertically, which it definitely doesn't in Chrome and does in Firefox...  If the claim that all other browsers agree on this behavior is true, this basically means the spec is a fiction and should get fixed accordingly.  The problem is figuring out what it should say, so we can figure out what we should actually implement.  I just mailed the HTML spec editors about this.

Just for fun, I tried changing the use of emAscent/emDescent in Gecko's canvas code to maxAscent/maxDescent (which have nothing to do with the em square, note).  The rendering of http://jsfiddle.net/msreckovic/e7sw167g/4/ on Mac then _almost_ matches Chrome's.  If I round the ascent/descent to integers it still doesn't quite match exactly, though maybe my rounding doesn't exactly match how they do rounding.  What it does for other fonts, or on other OSes, or for other testcases, who knows.  :(
Thanks for the explanation!

About your change:

'I tried changing the use of emAscent/emDescent in Gecko's canvas code to maxAscent/maxDescent'

Sounds good!
Might there be a chance to put that change into a Nightly version for testing?
> Might there be a chance to put that change into a Nightly version for testing?

It's a clear spec violation and still doesn't match the other browsers, right?  So there's not much point testing it.
>'I tried changing the use of emAscent/emDescent in Gecko's canvas code to maxAscent/maxDescent'

Which ascenders/descenders is this using? There's the hheaAscender/Descender, there's the OS/2 typoAscender/Descender and there's the winAscent/winDescent. Those can all be different values and I'm not clear on who's using what.
Here's the best explanation of ascenders/descenders that I've found:
https://glyphsapp.com/tutorials/vertical-metrics
(In reply to Emilio Cobos Álvarez [:emilio] from comment #26)
> I'm not too much into fonts or graphics, but both Gecko and Blink seem to
> draw at (x, y + ascent) for horizontal text + textBaseline = "top".

IIUC, Gecko is using "emAscent" from our font metrics, which is a value that is set such that emAscent+emDescent = 1em. Hence, the difference between textBaseline=top and textBaseline=bottom will be exactly 1em, as per the HTML spec.

Blink, OTOH, is using some other "ascent" value (at a guess, maybe from the OS/2 table's typoAscender field), which is typically a maximum ascender height for the font, possibly including some extra space for accents, and is often significantly taller than Gecko's emAscent. There's no guarantee that the actual glyphs in a font will be confined to the height of the em-square, nor that the ascender and descender metrics will add up to 1em. So by using the font's ascender and descender metrics to determine the textBaseline positions "top" and "bottom", Blink will often end up with positions that are considerably more than 1em apart. This does *not* correspond to what the HTML spec says.

(The diagram at https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-textbaseline clearly shows baseline positions at the top and bottom of the em-square, even though the accent on the "Á" and the descender of the "y", which would likely be reflected in the font's ascender and descender metrics, extend significantly beyond these lines.)

Maybe we should modify Gecko's behavior here, but if so, the place to start is with the spec (thanks for raising it with the editors, Boris), as what we currently implement is what the spec currently says.
Flags: needinfo?(jfkthame)
I filed https://github.com/whatwg/html/issues/2470 to help sort this out. Input welcome, in particular with respect to web-platform-tests coverage and what this ideally does (and why).
This is a great test page for looking into this bug. I hope it will be closed... someday. 

https://cdn.rawgit.com/adamschwartz/typography.js/master/index.html#/
Chrome has fixed their implementation to align with the spec, as described in comment 33 and comment 34.

https://bugs.chromium.org/p/chromium/issues/detail?id=607053

I assume we can safely close this bug as WONTFIX since we are following the spec here?
Yeah, I'll mark it INVALID since we've been following the standard all along.
Status: NEW → RESOLVED
Closed: 6 years ago
Resolution: --- → INVALID
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: