MathJax icon indicating copy to clipboard operation
MathJax copied to clipboard

Neo-Euler support

Open ddimmery opened this issue 6 months ago • 12 comments

Issue Summary

When I attempt to load the Neo Euler font extensions as directed in documentation, the URL fails to load.

Steps to Reproduce:

  1. Configure MathJax using documentation on Neo Euler font extension: https://docs.mathjax.org/en/latest/output/fonts.html#font-extensions
  2. Load webpage with equations to be rendered by MathJax

An example that does not work is here: https://jsfiddle.net/pb3oy2xc/1/ Removing the Neo Euler loading makes rendering work as expected again using the mathjax-pagella font.

The console error shows:

"MathJax([font]/mathjax-euler-font): Can't load \"https://cdn.jsdelivr.net/npm/@mathjax/mathjax-euler-font.js\""

This makes sense, as such a file does not appear in the CDN.

Technical details:

  • MathJax Version: 4.0
  • Client OS: Mac OS X 14.2.1
  • Browser: Arc Version 1.107.0 (66519) Chromium Engine Version 139.0.7258.67

I am using the following MathJax configuration:

MathJax = {
    tex: {
      inlineMath: {'[+]': [['$', '$']]},
    },
    chtml: {
      font: 'mathjax-pagella'
    },
    loader: {
      paths: {font: 'https://cdn.jsdelivr.net/npm/@mathjax'},
      load: ['input/tex-base', '[tex]/newcommand', '[tex]/action', 'output/chtml', '[font]/mathjax-euler-font']
    },
  };

and loading MathJax via

<script src="https://cdn.jsdelivr.net/npm/mathjax@4/startup.js"></script>

Supporting information:

  • https://jsfiddle.net/pb3oy2xc/ -- Example where MathJax loads and renders correctly, without Neo Euler (no console errors/warnings)
  • https://jsfiddle.net/pb3oy2xc/1/ -- Example where MathJax configuration of Neo Euler fails and equation does not render.

ddimmery avatar Aug 12 '25 22:08 ddimmery

Sorry, the docs are wrong and need to be fixed. It should be '[font]/mathjax-euler-font-extension/chtml.js' in the load array. I will mark it for a documentation update.

dpvc avatar Aug 12 '25 23:08 dpvc

Thank you!

On JSFiddle and my (admittedly more complicated local application), this fixes the problem that rendering fails, but the rendered math does not display in Neo Euler. See: https://jsfiddle.net/259d8ofq/

ddimmery avatar Aug 12 '25 23:08 ddimmery

OK, thanks. I'll have to look into it to see what is going wrong.

dpvc avatar Aug 12 '25 23:08 dpvc

OK, I've made some progress with this. It turns out that there is a timing issue where the font extension is being loaded before the font itself is in place, and so it ends up not being able to apply itself to the font you are using. So we need to tell MathJax that it needs to wait for output/chtml to finish loading (which includes its loading of the mathjax-pagella font) before loading the Euler font extension. That can be done using the dependencies object of the loader section of your configuration, as shown below.

There is also a second issue, which is that the extension expects to have a mathjax-euler-extension path, which I forgot about when writing the documentation. So here is a configuration that should work for you:

 MathJax = {
    tex: {
      inlineMath: {'[+]': [['$', '$']]},
    },
    output: {
      font: 'mathjax-pagella'
    },
    loader: {
      dependencies: {
        '[mathjax-euler-extension]/chtml': ['output/chtml'],
      },
      paths: {
        font: 'https://cdn.jsdelivr.net/npm/@mathjax',
        'mathjax-euler-extension': '[font]/mathjax-euler-font-extension',
      },
      load: ['input/tex-base', '[tex]/newcommand', '[tex]/action', 'output/chtml', '[mathjax-euler-extension]/chtml']
    },
  };

You could combine the two paths into one, but it's OK to use both.

I will need to think about a better way to handle the extensions, as this is more complicated than it should be. It may be that the best way is to add a fontExtensions configuration option like font in the output section to give an array of extensions to load. I will work something out and make a PR for the next release.

dpvc avatar Aug 13 '25 14:08 dpvc

Thank you! That makes a lot of sense. It doesn't fix my local issue, but I think this is due to the unholy combination of Quarto/revealjs that I'm using and is not specifically a MathJax issue, as your config fixes the jsfiddle. Thank you for being so responsive!

ddimmery avatar Aug 13 '25 20:08 ddimmery

Maybe I'm missing something but I've tried

MathJax = {
      tex: {
        inlineMath: [['$', '$'], ['\\(', '\\)']]
      },
      output: {
        font: 'mathjax-fira'
      },
      loader: {
        dependencies: {
          '[mathjax-euler-extension]/chtml': ['output/chtml']
        },
        paths: {
          font: 'https://cdn.jsdelivr.net/npm/@mathjax',
          'mathjax-euler-extension': '[font]/mathjax-euler-font-extension'
        },
        load: ['input/tex-base', '[tex]/newcommand', '[tex]/action', 'output/chtml', '[mathjax-euler-extension]/chtml']
      }
    };

and got

tex-mml-chtml.js:1 No version information available for component input/tex-base
(anonymous) @ tex-mml-chtml.js:1
Promise.then
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
gi @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
load @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
loadFont @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
tex-mml-chtml.js:1 No version information available for component [tex]/newcommand
(anonymous) @ tex-mml-chtml.js:1
Promise.then
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
gi @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
load @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
loadFont @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
calligraphic.js:1 Uncaught TypeError: Cannot read properties of undefined (reading 'chtml_ts')
(anonymous) @ calligraphic.js:1
(anonymous) @ calligraphic.js:1
tex-mml-chtml.js:1 No version information available for component [mathjax-euler-extension]/chtml/dynamic/calligraphic.js
(anonymous) @ tex-mml-chtml.js:1
Promise.then
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
gi @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
load @ tex-mml-chtml.js:1
MathJax._.mathjax.mathjax.asyncLoad @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
ro @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Cl @ tex-mml-chtml.js:1
loadDynamicFile @ tex-mml-chtml.js:1
getChar @ tex-mml-chtml.js:1
getVariantChar @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
getBBox @ tex-mml-chtml.js:1
getOuterBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
getBBox @ tex-mml-chtml.js:1
getOuterBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
getBBox @ tex-mml-chtml.js:1
getOuterBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
getBBox @ tex-mml-chtml.js:1
getOuterBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
getBBox @ tex-mml-chtml.js:1
getOuterBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
computeBBox @ tex-mml-chtml.js:1
getBBox @ tex-mml-chtml.js:1
getOuterBBox @ tex-mml-chtml.js:1
getLineBBox @ tex-mml-chtml.js:1
handleSpace @ tex-mml-chtml.js:1
standardChtmlNodes @ tex-mml-chtml.js:1
toCHTML @ tex-mml-chtml.js:1
toCHTML @ tex-mml-chtml.js:1
processMath @ tex-mml-chtml.js:1
toDOM @ tex-mml-chtml.js:1
typeset @ tex-mml-chtml.js:1
typeset @ tex-mml-chtml.js:1
typeset @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
renderDoc @ tex-mml-chtml.js:1
render @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Rn @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
e @ tex-mml-chtml.js:1
In @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
whenReady @ tex-mml-chtml.js:1
renderPromise @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
ji @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
whenReady @ tex-mml-chtml.js:1
typesetPromise @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
defaultPageReady @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
defaultReady @ tex-mml-chtml.js:1
defaultReady @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
Promise.then
(anonymous) @ tex-mml-chtml.js:1
Promise.then
loadFont @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
(anonymous) @ tex-mml-chtml.js:1
2tex-mml-chtml.js:1 Uncaught (in promise) Error: dynamic file 'calligraphic' failed to load

Maybe this feature doesn't work yet, even with the fix proposed in https://github.com/mathjax/MathJax/issues/3410#issuecomment-3184108697?

quoc-ho avatar Aug 20 '25 15:08 quoc-ho

@quoc-ho, you don't say what component you are using to load MathJax itself, but it looks like you are loading tex-mml-chtml.js. Note that the example above is for startup.js. The original poster was not using a combined component like tex-mml-chtml.js but rather loading individual pieces using the loader.load array. Since you are using a combined configuration, you don't need to include most of what he has listed, as they are already part of tex-mml-chtml. That is what is causing the error about no version information being available.

It turns out there is an additional problem, however, that occurs when a dynamic range of characters is loaded, like the calligraphic set, that I didn't take account of in my earlier post. Here is a revised configuration that you should be able to use with tex-mml-chtml.js.

MathJax = {
  tex: {
    inlineMath: {'[+]': [['$', '$']]},
  },
  output: {
    font: 'mathjax-fira'
  },
  loader: {
    paths: {
      font: 'https://cdn.jsdelivr.net/npm/@mathjax',
      'mathjax-euler-extension': '[font]/mathjax-euler-font-extension',
    },
    load: ['[mathjax-euler-extension]/chtml']
  },
  startup: {
    ready() {
      MathJax.startup.defaultReady();
      MathJax._.output.fonts.generic = {
        chtml_ts: {GenericFont: MathJax._.output.fonts['mathjax-fira'].chtml_ts.MathJaxFiraFont}
      }
    }
  }
};

I will need to revise the PR that resolves this issue to work around this as well.

dpvc avatar Aug 21 '25 08:08 dpvc

@dpvc Thanks so much! This renders everything in Euler perfectly..

One question though: since I already set Fira as my main font, I was expecting the Euler font to only kick in in places where Fira doesn't make sense, for example, \mathcal. Is it possible to have the Euler font only for, say, \mathcal?

quoc-ho avatar Aug 21 '25 09:08 quoc-ho

@quoc-ho, as you have found out, the Euler extension replaces the original font's characters with its own. While it would be possible to make an extension that replaces only the calligraphic characters, we don't have one set up for that. There are many possible extensions that are now possible, but only this one is available at the moment.

Note, however, that mathjax-fira does include a calligraphic set already. We added character from other fonts, when necessary, to make sure all the fonts available in MathJax had coverage for all the main variants.

In the future, we may provide more mix-and-match extensions for the font variants, but for now, this is how mathjax-euler works.

dpvc avatar Aug 21 '25 09:08 dpvc

@dpvc Thank you!

quoc-ho avatar Aug 21 '25 09:08 quoc-ho

@dpvc As it turns out, this additional config in startup also solves the issue with Euler on my local setup and everything renders perfectly!

ddimmery avatar Aug 22 '25 14:08 ddimmery

@ddimmery, glad your local setup now works as well. I have made a PR to try to straighten this all out, and in the future, one will simply add fontExtensions: ['mathjax-euler'] to the output (or chtml) section of the configuration, and everything else should be taken care of automatically.

dpvc avatar Aug 22 '25 15:08 dpvc