mathjax / MathJax

Beautiful and accessible math in all browsers
http://www.mathjax.org/
Apache License 2.0
10.12k stars 1.16k forks source link

Letter spacing is incorrect when custom fonts are disabled or unavailable #2904

Closed nagisa closed 2 years ago

nagisa commented 2 years ago

Issue Summary

I’ve a shortlist of fonts that I am able to enjoy reading at all. In order to make web accessible to me I’ve instructed my user agent to not load custom fonts and set it up to only use the fonts I’m okay with. This does cause some breakage, but it is usually limited to just invalid use of fonts (for e.g. icons) which comes up rarely enough for it to not be a huge problem.

MathJax appears to be super reliant on custom fonts too, though, which ends up causing a pretty awful looking output when these fonts are not available for any reason. For example consider the WebAssembly specification text: https://www.w3.org/TR/wasm-core-2/binary/modules.html#import-section

Screenshot

I have to resort to fiddling in the inspector to hide the html output and show the MathML output in order to make this specification readable. Would it be possible to make this sort of use-case work better?

Steps to Reproduce:

  1. Disable custom fonts in the user agent
  2. Render some maths (e.g. https://www.w3.org/TR/wasm-core-2/binary/modules.html#import-section)

Technical details:

dpvc commented 2 years ago

Most fonts don't include the characters needed to typeset mathematical formulas (e.g., several different sizes of parentheses, large and small integral signs, the pieces needed to make multi-character assemblies for stretchy delimiters and overbraces, the math alphabets like Fraktur, script, double-struck, and so on), so MathJax uses its own web fonts in order to guarantee that the needed characters are available. Furthermore, even if such characters were available in your system fonts, they might come from different fonts, and not match each other visually. And different fonts have different ways of access things like the different sizes of delimiters, or other variants that are needed. So yes, MathJax relies heavily on being able to switch fonts. If you restrict that ability, MathJax has no way to work around that, and so you will have prevented MathJax from working correctly, and there is very little MathJax can do about that.

In order to handle the math layout, MathJax has to have information about the bounding box of each glyph in the font, so MathJax can't use arbitrary fonts, but only the ones for which it has that metric data. If MathJax thinks it is using its usual font, it will lay out the characters so that they have the expected size; but if you have prevent that don't from loading and the browser substitutes some other font, then the sizes of the characters are not likely to be the same, and you will get what looks like incorrect placement, since MathJax is expecting the characters to be a size that they aren't. That is what you are seeing in your image. Note that there is not (currently) an API for javascript programs to ask the browser for the font metrics, so MathJax has the data built in for its fonts. That's why it can't use an arbitrary font.

Normally, I would have suggested that you use the MathJax contextual menu to select the SVG output renderer instead of the CHTML one, since the SVG output does not use fonts, but rather SVG paths for rending the characters, and so your restrictions on loading external fonts would not be encountered. But the site you link to uses its own copy of MathJax, and they seem to have not included the complete installation, and seem to have deleted the SVG files. Although you can select eh SVG renderer, it won't load (since it is not there). They also seem to have not bothered to include the MathJax Typewriter (monospaced) font, even though they make extensive use of it, so the output is messed up even if you don't restrict the fonts, since they have not bothered to include one of the ones you use.

So that website makes it hard for you to do what you would like. But there is still a possible solution, which is to run a bit of your own javascript code after you load the page. The code would untypeset the math, remove MathJax itself and the stylesheets that it inserts, reset the MathJax configuration to the page's original settings, and then load a fresh copy of MathJax from a CDN that has a complete installation. This is code that would do it for the page that you cite:

(function () {
MathJax.startup.document.state(0, true);
window.MathJax = {
  "tex": {"maxBuffer": 30720}
};
document.head.removeChild(document.head.lastChild);
document.head.removeChild(document.head.lastChild);
document.head.removeChild(document.head.lastChild);
document.head.removeChild(document.head.lastChild);
document.head.removeChild(document.head.lastChild);
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';
document.head.append(script);
})();

You could make a bookmarklet for this so that it is easy to run when need to. Or if you are really ambitious, you could make a TamperMonkey script that would run any time you load one of these pages. There are techniques for preventing a script from running in the first place (something like this, but checking the script src attribute to only block MathJax from loading from their site), so you could be even more clever and block the bacd copy of MathJax rather than letting it run and removing it, and then you would only have to load the new copy via the last three lines above. Use whatever approach best suits your needs and abilities.

On other sites that have access to a full MathJax installation, you can simply use the MathJax contextual menu to switch to SVG output, and it will be remembered for when you use that site in the future.

nagisa commented 2 years ago

Aha, it seems to be a problem with how w3 is set up. The spec at https://webassembly.github.io/spec/ does work alright and the SVG renderer does indeed work well.