mathjax / mathjax/MathJax

[v4.1.1] Font commands not respected when using Unicode characters

Open
#3,536 11 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Accepted Code Example v4
Dominant language
JavaScript
Stars
10.9k
Forks
1.2k
PR merge metrics
No merged PRs in 30d

Description

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:

  1. 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.

  2. 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
  1. Configure MathJax with tex: { mathStyle: 'ISO' }
  2. 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)  ✓

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the lcGreek and hl handlers, then compare their env.font handling with ucGreek and variable. Reproduce the examples with tex2chtml using mathStyle: 'ISO', and verify that font commands preserve the expected mathvariant for TeX and literal Greek characters.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.