MathJax-node: Problem parsing Tex

I have a simple latex forumula which can be parsed through browser Mathjax library but gives out error in the node implementation.

const mathjax = require('mathjax-node');
mathjax.start();
mathjax.config({
  jax: ['input/TeX', 'output/CommonHTML'],
  extensions: [
    'tex2jax.js',
  ],
  TeX: {
    extensions: ['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js'],
  },
  displayAlign: 'left',
  tex2jax: {
    inlineMath: [['$', '$'], ['\\(', '\\)']],
    processEscapes: true,
  },
});
const input = `\[ A = \begin{bmatrix} 1 & 3 & 5 \\ 2 & 5 & 4 \\ -2 & 3 & -1 \end{bmatrix} \]`;
mathjax.typeset({ math: input, html: true, speakText: false, format: ['TeX'] }, data => console.log(data));

It returns error parsing ampersand (&): TeX parse error: Misplaced &

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 17 (7 by maintainers)

Most upvoted comments

I don’t understand, why i get Tex parse error: Misplaced & with this input <p>\\( &gt; { h \\over {2\\pi}}\\)</p> using mathjax-node but works well for mathjax-node-page.

This is because mathjax-node takes a TeX string, not an HTML string, as its input. The &gt; is an HTML entity for >, but it is not valid TeX; the browser would turn &gt; into > as it processes the HTML page, which happens before MathJax runs. If you are extracting the math from an unprocessed HTML page, you will need to translate the entities yourself before passing them to mathjax-node.

The reason it works in mathjax-node-page is because mathjax-node-page uses a virtual DOM to process the page as HTML (just as it would be in a browser), and so &gt; is converted to > before it is processed by MathJax within that virtual DOM.

In order to handle references, MathJax maintains a list of the \tag{} values that are associated to \label{} values on the given page. But mathjax-node doesn’t have a “page” to work with, it just gets individual expressions, so that list is not in place, and all references will be undefined. Mathjax-node does allow you to maintain a page “state” by passing a state parameter that it will use to keep the list of labels (and other similar data), but your plugin does not have that.

You would have to modify the plug in to use the state parameter, and even with that, you would only be able to refer to equations that had already been processed (MathJax itself sets aside equations that have undefined references and reprocesses them at the end in order to provide for forward references).

It turns out that the plugin handles all the in-line math first, then all the display math, but since most \ref{} calls are found in in-line math, that means even if they are “backward” links in the document, the display equations to which they link won’t have been processed yet, so they will be undefined when the plugin processes them. So in addition to using the state variable, you should also switch the order of processing so display equations are done first, and then inline ones. That will give you the best chance of having your references be defined. (Using \ref within a displayed equation for a forward link would then be the only situation that wouldn’t work.)

So modifying the plugin’s index.js file to do something like

module.exports = async ({ markdownAST }, pluginOptions = {}) => {
  const nodes = [];
  const state = {};

  // for some reason this doesn't work:
  /*mjAPI.config({
    MathJax: pluginOptions
  });
  mjAPI.start();*/

  visit(markdownAST, `math`, node => {
    nodes.push({ node, format: 'TeX' })
  });

  visit(markdownAST, `inlineMath`, node => {
    nodes.push({ node, format: 'inline-TeX' })
  });

  for (const { node, format } of nodes) {
    await new Promise((resolve, reject) => {
      mjAPI.typeset({
        math: node.value,
        format: format,
        html: true,
        state: state
      }, data => {
        if (!data.errors) {
          node.type = `html`;
          node.value = data.html;
          resolve();
        } else
          reject(data.errors);
      });
    });
  }
}

might do the trick (this is untested, but should point you in the right direction).