MVS (U+180E) is rendered with zero advance in Mongolian text, even when the font's GSUB rule produces a visible mvs.narrow / mvs.wide glyph
Categories
(Core :: Layout: Text and Fonts, defect, P3)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox152 | --- | fixed |
People
(Reporter: satsrag, Assigned: jfkthame)
References
Details
Attachments
(3 files)
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36
Steps to reproduce:
- Open the attached HTML file (
firefox-mvs-bug-report.html) in Firefox 150. - Wait for Noto Sans Mongolian to load via Google Fonts CDN.
- Look at the two test rows at 200px font size:
ᠠᠨᠠ(U+1820 U+1828 U+180E U+1820)ᠠᠨᠢ(U+1820 U+1828 U+180E U+1822)
- Compare the JavaScript-measured widths and the verdict at the bottom.
- Open the same HTML file in Chrome to verify the bug is Firefox-specific (Chrome renders it correctly).
Actual results:
The MVS (U+180E) glyph is stripped to zero advance, breaking Mongolian narrow-vowel typography:
| Input | Predicted (px) | Firefox actual (px) | Diff (px / font-units) |
|---|---|---|---|
| ᠠᠨᠠ | 336.60 | 325.60 | -11 px / -55 fu (= mvs.narrow advance, exactly) |
| ᠠᠨᠢ | 404.20 | 352.20 | -52 px / -260 fu (= mvs.wide advance, exactly) |
The diffs match the MVS glyph's advance exactly. Surrounding letters are shaped correctly (ᠨ takes N.fina.mvs, vowels take Aa.isol / I.isol) — only the MVS glyph itself is removed.
Chrome (also using HarfBuzz, same machine, same font) renders the same HTML correctly with the proper visible MVS spacing → the bug is Firefox-specific post-processing, not HarfBuzz or the font.
Root cause
In gfx/thebes/gfxHarfBuzzShaper.cpp (~line 1742, after the hb_shape call):
// Check for default-ignorable char that didn't get filtered, combined,
// etc by the shaping process, and remove from the run.
// (This may be done within harfbuzz eventually.)
if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
aText[baseCharIndex])) {
glyphStart = glyphEnd;
charStart = charEnd;
continue; // glyph silently dropped
}
And gfx/thebes/gfxFont.cpp::FilterIfIgnorable (~line 848):
bool gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh) {
if (IsIgnorable(aCh)) {
// Exception only for Letter category (Hangul fillers, bug 1238243)
if (GetGenCategory(aCh) == nsUGenCategory::kLetter && /* … */) {
return false;
}
g.SetComplex(/* … */).SetMissing(); // becomes zero-width
return true;
}
return false;
}
MVS is gc=Cf and Default_Ignorable_Code_Point=Yes, so it falls past the Letter exception (added in bug 1238243 for Hangul fillers) and is unconditionally hidden. But the font's GSUB deliberately produces
mvs.narrow=55 / mvs.wide=260 glyphs with non-zero advances — Firefox is overriding the font's intent.
Cross-verified that HarfBuzz itself does NOT strip the glyph: hb-shape returns the visible mvs.narrow / mvs.wide glyphs identically across HarfBuzz 8.3.0 (Ubuntu 24.04), 13.1.1 (Firefox-bundled, built from
source for testing), and 14.2.0 (current Homebrew).
Expected results:
```markdown
The MVS glyph should render with its visible advance, as produced by the font's GSUB rule and as returned by HarfBuzz:
| Input | Glyph sequence (advance, em=1000) | Total fu | Expected px @ 200pt |
|-------|------------------------------------|----------|---------------------|
| ᠠᠨᠠ | AA.init=786 \| N.fina.mvs=427 \| **mvs.narrow=55** \| Aa.isol=415 | 1683 | 336.6 px |
| ᠠᠨᠢ | AA.init=786 \| A.fina=427 \| **mvs.wide=260** \| I.isol=548 | 2021 | 404.2 px |
Verified by `hb-shape --script=Mong` against the same font.
Chrome renders these widths correctly.
## Suggested fix
Either:
1. **Skip the filter when HarfBuzz produced a non-zero advance** — i.e. the font deliberately gave the codepoint a visible glyph; trust the font.
2. **Add a context-aware exception** for default-ignorable Cf codepoints used as visible-width markers in their primary script (MVS in `mong`). Analogous to the Hangul-filler exception added in bug 1238243.
Option 1 is preferable — it's font-driven rather than hard-coding script knowledge into the shaper.
## Affected users
Mongolian (traditional script) typography in Firefox is broken anywhere MVS is used, which includes most multi-syllable words ending in vowels with gender-suffix patterns. Visible effect: the "narrow vowel
space" gap disappears, characters appear too tightly packed.
The same Firefox post-processing affects any other default-ignorable Cf codepoint used as a visible-width marker by a font's GSUB.
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36
Steps to reproduce:
- Open the attached HTML file (
firefox-mvs-bug-report.html) in Firefox 150. - Wait for Noto Sans Mongolian to load via Google Fonts CDN.
- Look at the two test rows at 200px font size:
ᠠᠨᠠ(U+1820 U+1828 U+180E U+1820)ᠠᠨᠢ(U+1820 U+1828 U+180E U+1822) ─
- Compare the JavaScript-measured widths and the verdict at the bottom.
- Open the same HTML file in Chrome to verify the bug is Firefox-specific (Chrome renders it correctly).
Actual results:
The MVS (U+180E) glyph is stripped to zero advance, breaking Mongolian narrow-vowel typography:
| Input | Predicted (px) | Firefox actual (px) | Diff (px / font-units) |
|---|---|---|---|
| ᠠᠨᠠ | 336.60 | 325.60 | -11 px / -55 fu (= mvs.narrow advance, exactly) |
| ᠠᠨᠢ | 404.20 | 352.20 | -52 px / -260 fu (= mvs.wide advance, exactly) |
The diffs match the MVS glyph's advance exactly. Surrounding letters are shaped correctly (ᠨ takes N.fina.mvs, vowels take Aa.isol / I.isol) — only the MVS glyph itself is removed.
Chrome (also using HarfBuzz, same machine, same font) renders the same HTML correctly with the proper visible MVS spacing → the bug is Firefox-specific post-processing, not HarfBuzz or the font.
Root cause
In gfx/thebes/gfxHarfBuzzShaper.cpp (~line 1742, after the hb_shape call):
// Check for default-ignorable char that didn't get filtered, combined,
// etc by the shaping process, and remove from the run.
// (This may be done within harfbuzz eventually.)
if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
aText[baseCharIndex])) {
glyphStart = glyphEnd;
charStart = charEnd;
continue; // glyph silently dropped
}
And gfx/thebes/gfxFont.cpp::FilterIfIgnorable (~line 848):
bool gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh) {
if (IsIgnorable(aCh)) {
// Exception only for Letter category (Hangul fillers, bug 1238243)
if (GetGenCategory(aCh) == nsUGenCategory::kLetter && /* … */) {
return false;
}
g.SetComplex(/* … */).SetMissing(); // becomes zero-width
return true;
}
return false;
}
MVS is gc=Cf and Default_Ignorable_Code_Point=Yes, so it falls past the Letter exception (added in bug 1238243 for Hangul fillers) and is unconditionally hidden. But the font's GSUB deliberately produces
mvs.narrow=55 / mvs.wide=260 glyphs with non-zero advances — Firefox is overriding the font's intent.
Cross-verified that HarfBuzz itself does NOT strip the glyph: hb-shape returns the visible mvs.narrow / mvs.wide glyphs identically across HarfBuzz 8.3.0 (Ubuntu 24.04), 13.1.1 (Firefox-bundled, built from
source for testing), and 14.2.0 (current Homebrew).
Box 3: What should have happened? (expected results)
The MVS glyph should render with its visible advance, as produced by the font's GSUB rule and as returned by HarfBuzz:
| Input | Glyph sequence (advance, em=1000) | Total fu | Expected px @ 200pt |
|---|---|---|---|
| ᠠᠨᠠ | AA.init=786 | N.fina.mvs=427 | mvs.narrow=55 | Aa.isol=415 | 1683 | 336.6 px |
| ᠠᠨᠢ | AA.init=786 | A.fina=427 | mvs.wide=260 | I.isol=548 | 2021 | 404.2 px |
Verified by hb-shape --script=Mong against the same font.
Chrome renders these widths correctly.
Suggested fix
Either:
- Skip the filter when HarfBuzz produced a non-zero advance — i.e. the font deliberately gave the codepoint a visible glyph; trust the font.
- Add a context-aware exception for default-ignorable Cf codepoints used as visible-width markers in their primary script (MVS in
mong). Analogous to the Hangul-filler exception added in bug 1238243.
Option 1 is preferable — it's font-driven rather than hard-coding script knowledge into the shaper.
Affected users
Mongolian (traditional script) typography in Firefox is broken anywhere MVS is used, which includes most multi-syllable words ending in vowels with gender-suffix patterns. Visible effect: the "narrow vowel
space" gap disappears, characters appear too tightly packed.
The same Firefox post-processing affects any other default-ignorable Cf codepoint used as a visible-width marker by a font's GSUB.
Comment 2•19 days ago
|
||
The Bugbug bot thinks this bug should belong to the 'Core::Layout: Text and Fonts' component, and is moving the bug to that component. Please correct in case you think the bot is wrong.
Updated•19 days ago
|
| Assignee | ||
Comment 3•18 days ago
|
||
Thanks for the report and analysis. For now, at least, I propose to do a patch along the lines of the suggested option 2, more narrowly targeted than option 1, because I'm concerned there are fonts where many of the default-ignorable controls, etc, are given "placeholder" glyphs whose display is not normally desired. So I think option 2 is a lower-risk approach to this issue.
| Assignee | ||
Comment 4•18 days ago
|
||
Updated•18 days ago
|
| Assignee | ||
Comment 6•17 days ago
|
||
Created web-platform-tests PR https://github.com/web-platform-tests/wpt/pull/59849 for changes under testing/web-platform/tests
Comment 10•15 days ago
|
||
Backed out for causing wpt failures on mvs-shaping.html.
| Assignee | ||
Comment 11•15 days ago
|
||
Ugh, looks like our Linux machines are using a non-linear hinting/rasterization mode, which results in discrepancies a bit larger than a pixel once multiple glyphs are involved. I guess we can either annotate the test as failing on Linux, or relax the comparison to allow more than just accumulated floating-point error; instead it's accumulated glyph-advance rounding.
Upstream PR was closed without merging
Comment 13•14 days ago
|
||
Comment 14•14 days ago
|
||
| bugherder | ||
https://hg.mozilla.org/mozilla-central/rev/6035eb8121d7
https://hg.mozilla.org/mozilla-central/rev/15f06a5ffa4b
Upstream PR merged by moz-wptsync-bot
Updated•6 days ago
|
Description
•