LLM Use Disclaimer
The bug was discovered after using Github Copilot through VSCode to replace Obsidian's v3 MathJax instance with v4. The following summary was written by Copilot as well.
Issue Summary
Font-switching commands (\symup, \mathrm, \mathup, etc.) fail to apply the correct mathvariant to Greek letters in two related scenarios:
-
lcGreek handler ignores env.font entirely — \symup{\alpha}, \mathrm{\alpha}, etc. never produce upright lowercase Greek, regardless of mathStyle. The handler unconditionally applies mathStyle() without checking whether a font command has set env.font. This affects all mathStyle configurations.
-
hl Unicode handler overwrites env.font with mathStyle — When a literal Unicode Greek character is used inside a font command (e.g. \symup{Π}), the Unicode fallback handler computes the mathStyle variant after env.font has been set and overwrites the mathvariant attribute. For uppercase Greek, this manifests only with mathStyle: 'ISO' (the only style where uppercase Greek is italic). For lowercase Greek, it compounds with bug 1.
The ucGreek handler (used by TeX commands like \Pi) is the only handler that correctly prioritises env.font over mathStyle. The variable handler (used by Latin letters) also works correctly.
Steps to Reproduce
- Configure MathJax with
tex: { mathStyle: 'ISO' }
- Render the following expressions and compare output glyphs:
| Input |
Expected |
Actual |
Handler |
\symup{\Pi} |
Upright Π (U+03A0) |
✓ Upright (U+03A0) |
ucGreek |
\symup{Π} (Unicode) |
Upright Π (U+03A0) |
✗ Italic 𝛱 (U+1D6F1) |
hl — bug 2 |
\mathrm{Π} (Unicode) |
Upright Π (U+03A0) |
✗ Italic 𝛱 (U+1D6F1) |
hl — bug 2 |
\symup{\alpha} |
Upright α (U+03B1) |
✗ Italic 𝛼 (U+1D6FC) |
lcGreek — bug 1 |
\mathrm{\alpha} |
Upright α (U+03B1) |
✗ Italic 𝛼 (U+1D6FC) |
lcGreek — bug 1 |
\symup{α} (Unicode) |
Upright α (U+03B1) |
✗ Italic 𝛼 (U+1D6FC) |
hl — bugs 1+2 |
\symup{x} |
Upright x (U+0078) |
✓ Upright (U+0078) |
variable |
\symup{X} |
Upright X (U+0058) |
✓ Upright (U+0058) |
variable |
Bug 1 (lcGreek) reproduces with all mathStyle values. Bug 2 (hl) reproduces with mathStyle: 'ISO' for uppercase Greek; for lowercase Greek and Latin, the effect depends on whether mathStyle returns a non-NORMAL variant.
Root Cause
Bug 1 — lcGreek ignores env.font:
// lcGreek — never checks env.font
lcGreek(t, e) {
const s = { mathvariant: t.configuration.mathStyle(e.char) || ITALIC };
// ...
}
// Compare ucGreek — correctly checks env.font first
ucGreek(t, e) {
const s = { mathvariant: t.stack.env.font || t.configuration.mathStyle(e.char, true) || NORMAL };
// ...
}
Fix: lcGreek should check t.stack.env.font before falling back to mathStyle, matching ucGreek.
Bug 2 — hl (Unicode fallback handler) overwrites env.font:
function hl(t, e) {
const s = t.stack.env.font; // e.g. "normal" from \symup
const i = s ? { mathvariant: s } : {}; // correctly sets mathvariant="normal"
const c = t.create("token", a, i, ...); // token created with "normal"
const l = isLatinOrGreekChar(e)
? t.configuration.mathStyle(e, true) || r
: ""; // l = "italic" (ISO uppercase Greek)
const h = o[4] || (s && l === NORMAL ? "" : l); // h = "italic" (since l ≠ NORMAL)
h && c.attributes.set("mathvariant", h); // OVERWRITES "normal" → "italic"
}
Fix: hl should skip the mathStyle override when env.font is already set.
Technical Details
- MathJax Version: 4.1.1
- Client OS: Windows 11
- Browser: Electron (Chromium-based, Obsidian desktop app)
I am using the following MathJax configuration:
MathJax = {
tex: {
packages: ['base', 'ams', 'newcommand', 'configmacros', 'noundefined', 'textmacros'],
mathStyle: 'ISO',
},
startup: {
typeset: false,
},
};
and loading a custom-built tex-chtml component (all TeX extensions pre-bundled via components/bin/makeAll).
Supporting Information
Verification via tex2chtml() in the console:
// Bug 1: lcGreek ignores env.font — all mathStyle values
MathJax.tex2chtml('\\symup{\\alpha}').querySelector('mjx-c').className
// → "mjx-c1D6FC" (U+1D6FC = italic 𝛼) ✗ expected "mjx-c3B1" (U+03B1 = upright α)
// Bug 2: hl overwrites env.font — mathStyle: 'ISO' only (for uppercase Greek)
MathJax.tex2chtml('\\symup{Π}').querySelector('mjx-c').className
// → "mjx-c1D6F1" (U+1D6F1 = italic 𝛱) ✗ expected "mjx-c3A0" (U+03A0 = upright Π)
// Working: ucGreek handler correctly respects env.font
MathJax.tex2chtml('\\symup{\\Pi}').querySelector('mjx-c').className
// → "mjx-c3A0" (U+03A0 = upright Π) ✓
// Working: variable handler correctly respects env.font (Latin)
MathJax.tex2chtml('\\symup{x}').querySelector('mjx-c').className
// → "mjx-c78" (U+0078 = upright x) ✓
LLM Use Disclaimer
The bug was discovered after using Github Copilot through VSCode to replace Obsidian's v3 MathJax instance with v4. The following summary was written by Copilot as well.
Issue Summary
Font-switching commands (
\symup,\mathrm,\mathup, etc.) fail to apply the correctmathvariantto Greek letters in two related scenarios:lcGreekhandler ignoresenv.fontentirely —\symup{\alpha},\mathrm{\alpha}, etc. never produce upright lowercase Greek, regardless ofmathStyle. The handler unconditionally appliesmathStyle()without checking whether a font command has setenv.font. This affects allmathStyleconfigurations.hlUnicode handler overwritesenv.fontwithmathStyle— When a literal Unicode Greek character is used inside a font command (e.g.\symup{Π}), the Unicode fallback handler computes themathStylevariant afterenv.fonthas been set and overwrites themathvariantattribute. For uppercase Greek, this manifests only withmathStyle: 'ISO'(the only style where uppercase Greek is italic). For lowercase Greek, it compounds with bug 1.The
ucGreekhandler (used by TeX commands like\Pi) is the only handler that correctly prioritisesenv.fontovermathStyle. Thevariablehandler (used by Latin letters) also works correctly.Steps to Reproduce
tex: { mathStyle: 'ISO' }\symup{\Pi}ucGreek\symup{Π}(Unicode)hl— bug 2\mathrm{Π}(Unicode)hl— bug 2\symup{\alpha}lcGreek— bug 1\mathrm{\alpha}lcGreek— bug 1\symup{α}(Unicode)hl— bugs 1+2\symup{x}variable\symup{X}variableBug 1 (
lcGreek) reproduces with allmathStylevalues. Bug 2 (hl) reproduces withmathStyle: 'ISO'for uppercase Greek; for lowercase Greek and Latin, the effect depends on whethermathStylereturns a non-NORMALvariant.Root Cause
Bug 1 —
lcGreekignoresenv.font:Fix:
lcGreekshould checkt.stack.env.fontbefore falling back tomathStyle, matchingucGreek.Bug 2 —
hl(Unicode fallback handler) overwritesenv.font:Fix:
hlshould skip themathStyleoverride whenenv.fontis already set.Technical Details
I am using the following MathJax configuration:
and loading a custom-built
tex-chtmlcomponent (all TeX extensions pre-bundled viacomponents/bin/makeAll).Supporting Information
Verification via
tex2chtml()in the console: