Text rendering looks off on maps.apple.com on Windows
Categories
(Core :: Graphics: Text, defect)
Tracking
()
People
(Reporter: bgrins, Assigned: jfkthame, NeedInfo)
References
(Blocks 1 open bug, Regressed 1 open bug, Regression)
Details
(Keywords: regression, webcompat:platform-bug, webcompat:site-report)
Attachments
(5 files)
STR:
- Load https://beta.maps.apple.com/?address=600+Montgomery+St%2C+San+Francisco%2C+CA+94111%2C+United+States&auid=13461352009204515340&ll=37.795167%2C-122.4027996&lsp=9902&q=Transamerica+Pyramid on Firefox in Windows
- Look at the "Transamerica" text
Reporter | ||
Comment 1•3 months ago
|
||
Same text on Edge
Comment 2•3 months ago
|
||
Jonathan, any ideas what's wrong with the glyph layout here? Apple seems interested in fixing this.
Assignee | ||
Comment 3•2 months ago
|
||
I can't seem to inspect that text to see how it's being styled, but my guess is this might be a bad interaction between letter-spacing, scaling, and pixel-snapping of glyph positions.
Comment 4•2 months ago
|
||
The text is rendered on canvas.rt-root
(you can use this search query in the inspector to find the element), which has the following rule:
.mk-map-view .rt-root {
letter-spacing: initial;
}
But removing, or changing this live doesn't seem to fix the issue, but I'm not sure the text get re-rendered without reloading the page.
Also note that the rendering issue doesn't reproduce on Linux.
Comment 5•2 months ago
|
||
Based on native method call tracing, I suspect, but I'm not 100% sure that the text is rendered around here:
https://share.firefox.dev/3TDKKL4
I don't know Canvas API enough to spot if something here would possibly render some text in the canvas.
Comment 6•2 months ago
|
||
Using the debugger, I think I found where we render the text.
https://beta.maps.apple.com/static/maps-app-web-client/mapkitjs/libs/mapkit.core.1b57c0.js?mkjsVersion=5.78.83
line 1227 (pretty printed)
e
is the canvas rendering context
draw: function (e) {
var t = this._node,
i = this.params[t.kind];
this._setTextProperties(e);
var o = t.parent.annotation.darkColorScheme ? 'dark' : 'light';
e.fillStyle = i.color[o],
e.strokeStyle = this.params.haloColor[o],
e.lineWidth = this.params.haloSize;
var a = t.size.width / 2,
n = t.size.height - i.yPadding;
e.strokeText(t.text, a, n),
e.fillText(t.text, a, n)
}
- t.text = "Transamerica"
- a = 38.5
- n = 15
- e.lineWidth = 2
- e.font = 600 11px "SFProRegular", "-apple-system", "BlinkMacSystemFont", "Helvetica Neue", Helvetica, Arial, sans-serif
- e.textAlign = center
- e.textBaseline = bottom
- e.miterLimit = 2
Also interesting, in the same source.
Line 1543 (pretty printed)
e
is the canvas context,t
is "Transamerica pyramid"i
is "600 11px "SFProRegular", "-apple-system", "BlinkMacSystemFont", "Helvetica Neue", Helvetica, Arial, sans-serif"
return function (e, t, i) {
e.font = i;
var o = t.trim().split(/[\f\n\r\t\v\u0020\u2000-\u200b\u205f\u3000]+/),
a = [];
for (; o.length > 0; ) {
for (var n = [
o.shift()
], s = n.join(' ').length; o.length > 0 && s <= 16; ) n.push(o.shift()),
s = n.join(' ').length;
s > 16 &&
n.length > 1 &&
o.unshift(n.pop());
const t = n.join(' '),
i = e.measureText(t),
l = i.width + u.labelOutlineWidth;
a.push([t,
l,
i])
}
return a
Same source, line 1135
e
is "Pyramid"t
is "Title"i
is 47a
is a TextMetrics objectl
is false
function l(e, t, i, a, l) {
o.BaseNode.call(this),
this.text = e,
this.kind = t,
this._renderer = new n(this),
this.textAscent = a.ascent ||
0,
this.textDescent = a.descent ||
0,
this.size = new s(i, this._renderer.params[this.kind].height),
this.keyForFrozenLayer = (l ? 'dark' : 'light') + '-' + t + '#' + this.text
}
Assignee | ||
Comment 7•2 months ago
|
||
Yeah, the "Transamerica Pyramid" label is drawn using canvas2d.fillText (unlike the other labels around the map), and that doesn't handle spacing well in Arial, which is the font we end up using on Windows.
The result looks a lot better if you remove Arial from the gfx.font_rendering.cleartype_params.force_gdi_classic_for_families
pref. (Requires browser restart to see the change, I believe.)
There's a long history behind that setting, and it may be that it's time we got rid of it altogether, but any change to Windows font rendering tends to be controversial. :(
Assignee | ||
Comment 8•2 months ago
•
|
||
Note that if I render the same text "Transamerica Pyramid" using font: 600 11px Arial
in HTML, the spacing is much better. The bad result here is specific to canvas2d text, which apparently doesn't handle the attempt to set the cleartype mode very well.
Assignee | ||
Comment 9•2 months ago
|
||
Assignee | ||
Comment 10•2 months ago
|
||
In the HTML-rendered text, we can see the effect of "force GDI classic" mode, e.g. making the "m" glyph wider. It looks like the canvas2d-rendered text has laid out the glyphs according to the GDI-classic metrics, but the actual rendering is not using this mode and so we get a narrower "m" and bad spacing. (Other glyphs are also affected, but the "m" is perhaps the clearest.)
Assignee | ||
Comment 11•2 months ago
|
||
We don't render the glyphs using "GDI classic" mode in canvas because we explicitly set params.allowGDI
to false here. This dates back to bug 1730772.
Removing that line results in much better spacing of the "Transamerica Pyramid" text, but presumably it may regress things that bug 1730772 was aiming to fix.
Comment 12•2 months ago
|
||
A quick side note to explain how I found this JS code.
I hacked something to break on any function call whose any of its arguments was equal to Transamerica pyramid
.
It paused on many function calls related to the left sidebar, but ultimately paused on the right function calling the canvas API.
Updated•2 months ago
|
Comment 13•2 months ago
|
||
Set release status flags based on info from the regressing bug 1730772
:bas.schouten, since you are the author of the regressor, bug 1730772, could you take a look? Also, could you set the severity field?
For more information, please visit BugBot documentation.
Reporter | ||
Updated•2 months ago
|
Updated•2 months ago
|
Assignee | ||
Comment 14•2 months ago
|
||
Updated•2 months ago
|
Updated•2 months ago
|
Comment 15•2 months ago
|
||
Comment 16•2 months ago
|
||
Backed out for causing bc failures on browser_less_common_selection_manipulations.js.
[task 2024-09-24T20:58:00.287Z] 20:58:00 INFO - TEST-PASS | browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js | Check initial selection - [8,13] deepEqual [8,13] -
[task 2024-09-24T20:58:00.288Z] 20:58:00 INFO - Test Drag Selection from the first character - Full host
[task 2024-09-24T20:58:00.289Z] 20:58:00 INFO - Selected text is <example.co>
[task 2024-09-24T20:58:00.289Z] 20:58:00 INFO - Buffered messages finished
[task 2024-09-24T20:58:00.291Z] 20:58:00 INFO - TEST-UNEXPECTED-FAIL | browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js | Check initial selection - [0,19] deepEqual [8,18] -
[task 2024-09-24T20:58:00.291Z] 20:58:00 INFO - Stack trace:
[task 2024-09-24T20:58:00.291Z] 20:58:00 INFO - chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js:doTest/<:275
[task 2024-09-24T20:58:00.291Z] 20:58:00 INFO - resource://testing-common/BrowserTestUtils.sys.mjs:withNewTab:121
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js:doTest:259
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js:https:250
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:handleTask:1145
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:_runTaskBasedTest:1217
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:Tester_execTest:1358
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:nextTest/<:1134
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:SimpleTest.waitForFocus/<:1058
[task 2024-09-24T20:58:00.292Z] 20:58:00 INFO - Test Drag Selection from the last character
[task 2024-09-24T20:58:00.293Z] 20:58:00 INFO - Selected text is <example.com/test/some/page.htm>
[task 2024-09-24T20:58:00.294Z] 20:58:00 INFO - TEST-PASS | browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js | Check initial selection - [0,30] deepEqual [0,30] -
[task 2024-09-24T20:58:00.294Z] 20:58:00 INFO - Test Drag Selection in the middle of the string
[task 2024-09-24T20:58:00.294Z] 20:58:00 INFO - Selected text is <le.co>
[task 2024-09-24T20:58:00.295Z] 20:58:00 INFO - TEST-PASS | browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js | Check initial selection - [13,18] deepEqual [13,18] -
[task 2024-09-24T20:58:00.296Z] 20:58:00 INFO - Test Double-click on word
[task 2024-09-24T20:58:00.296Z] 20:58:00 INFO - Selected text is </>
[task 2024-09-24T20:58:00.297Z] 20:58:00 INFO - Not taking screenshot here: see the one that was previously logged
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - TEST-UNEXPECTED-FAIL | browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js | Check initial selection - [20,24] deepEqual [19,20] -
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - Stack trace:
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js:doTest/<:275
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - resource://testing-common/BrowserTestUtils.sys.mjs:withNewTab:121
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js:doTest:259
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js:https:250
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:handleTask:1145
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:_runTaskBasedTest:1217
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:Tester_execTest:1358
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochikit/content/browser-test.js:nextTest/<:1134
[task 2024-09-24T20:58:00.298Z] 20:58:00 INFO - chrome://mochikit/content/tests/SimpleTest/SimpleTest.js:SimpleTest.waitForFocus/<:1058
[task 2024-09-24T20:58:00.300Z] 20:58:00 INFO - Click at the right of the text
[task 2024-09-24T20:58:00.300Z] 20:58:00 INFO - Selected text is <https://example.com/test/some/page.htm>
[task 2024-09-24T20:58:00.301Z] 20:58:00 INFO - TEST-PASS | browser/components/urlbar/tests/browser/browser_less_common_selection_manipulations.js | Check initial selection - [0,38] deepEqual [0,38] -
Assignee | ||
Comment 17•2 months ago
|
||
The browser_less_common_selection_manipulations.js failures are happening because the test uses canvas2d.measureText and assumes the resulting metrics will match the text in the URL bar, but because the system font in the URLbar is using GDI Classic mode, and we no longer use those metrics in canvas, the widths don't match.
A workaround for this, I think, is to use an HTML-based (rather than canvas-based) approach to measuring substrings of the URLbar content. I'll push a try run to check... https://treeherder.mozilla.org/jobs?repo=try&revision=fd3691742d870cd51d305f512af0aade142d0d0c
Assignee | ||
Comment 18•2 months ago
|
||
Because of the possibility of different rendering modes, it's not guaranteed that
canvas2d.measureText will return exactly the same text metrics as HTML uses.
So instead of relying on canvas to measure substrings of the URLbar content,
measure by putting the text in a <span> and querying its offsetWidth.
Comment 19•2 months ago
|
||
Comment 20•2 months ago
|
||
bugherder |
https://hg.mozilla.org/mozilla-central/rev/356a7ea855bc
https://hg.mozilla.org/mozilla-central/rev/31bd8fe7b5ce
Updated•2 months ago
|
Comment 21•2 months ago
|
||
Given the regressions, this doesn't sound like something we want to fast-track shipping.
Comment 22•2 months ago
|
||
Jonathan, I think we should probably back this out for now until we get a better handle on the regressions.
Assignee | ||
Comment 23•2 months ago
|
||
Possibly, but let's give it a little bit more time: I've just (re-)lando'd bug 1920733, which should resolve the pdf.js-related problems, assuming it sticks.
(leaving ni? in place for now)
Comment 26•1 month ago
•
|
||
Followup on backout plans: per bug 1923150 comment 12, it looks like we're leaving this fix in mozilla-central/Nightly, but we're planning to backout from the beta channel after the next central-to-beta merge (so as not to ship this bug's regressions in 133 beta/release).
[please correct me if that's wrong; I'm just noting the latest plan I could find on this & associated bugs]
Comment 27•1 month ago
|
||
Possible duplicates: bug 1885846, bug 890733, bug 1029327
Comment 28•1 month ago
|
||
Comment 29•1 month ago
|
||
Backed out of beta for causing Bug 1923150
https://hg.mozilla.org/releases/mozilla-beta/rev/9862f84eed67bde6041920e06e5e7ca8261df85e
Updated•18 days ago
|
Updated•3 days ago
|
Description
•