MathJax icon indicating copy to clipboard operation
MathJax copied to clipboard

Negative numbers in MathML output has wrong spacing in Firefox

Open hbghlyj opened this issue 3 years ago • 3 comments

Is your feature request related to a problem? Please describe. (-1) and ({-1}) render the same in in MathJax image but the output MathML render differently in Firefox image

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  <mo stretchy="false">(</mo>
  <mo>−</mo>
  <mn>1</mn>
  <mo stretchy="false">)</mo>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  <mo stretchy="false">(</mo>
  <mrow data-mjx-texclass="ORD">
    <mo>−</mo>
    <mn>1</mn>
  </mrow>
  <mo stretchy="false">)</mo>
</math>

Similar problem for |-1| and |{-1}| : In MathJax: image

Output MathML rendered in Firefox: image

Similar problem for ordered triples (2,-3,5) and (2,{-3},5) : In MathJax: image

Output MathML rendered in Firefox: image

Describe the solution you'd like Add form="prefix" to <mo>−</mo>

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
  <mo stretchy="false">(</mo>
  <mo form="prefix">−</mo>
  <mn>1</mn>
  <mo stretchy="false">)</mo>
</math>

Or, add inferred mrow ?

Describe alternatives you've considered It would be good to produce <mn>-1</mn> rather than <mo>-</mo><mn>1</mn>.

Additional context Windows 11 FireFox 102.0a1 (2022-05-28) (64-bit) MathJax v3.2.2

hbghlyj avatar Jul 04 '22 00:07 hbghlyj

By default, MathJax output renderers use TeX spacing rules, not MathML spacing rules, so there are situations (like this) where the results in Firefox may differ from that in MathJax. You can use a TeX input jax post-filter to adjust the output to your needs, however. For example, this configuration

MathJax = {
  startup: {
    ready() {
      MathJax.startup.defaultReady();
      MathJax.startup.document.inputJax[0].postFilters.add(({math, data}) => {
        const remove = [];
        for (const mo of data.getList('mo')) {
          const text = mo.getText();
          if (text !== '-' && text !== '\u2212') continue;
          const i = mo.parent.childIndex(mo);
          const next = mo.parent.childNodes[i + 1];
          const prev = mo.parent.childNodes[i - 1];
          if (prev && next && prev.isKind('mo') && next.isKind('mn')) {
            next.childNodes[0].setText('-' + next.getText());
            mo.parent.childNodes.splice(i, 1);
            remove.push(mo);
          }
        }
        data.removeFromList('mo', remove);
      });
    }
  }
};

will convert your situations to using <mn>-1</mn>, as you request, though my understanding is that the recommended encoding for this should be <mrow><mo>-</mo><mn>1</mn></mrow>.

Note that this issue will affect any situation where the minus is intended as negation when it is not at the beginning of a (sub-)expression, e.g., (-x) or (1, -x, 3), f(-(x+y)), etc., and the approach above does not resolve that. One could use

MathJax = {
  startup: {
    ready() {
      MathJax.startup.defaultReady();
      MathJax.startup.document.inputJax[0].postFilters.add(({math, data}) => {
        for (const mo of data.getList('mo')) {
          const text = mo.getText();
          if (text !== '-' && text !== '\u2212') continue;
          const prev = mo.parent.childNodes[mo.parent.childIndex(mo) - 1];
          if (prev && prev.isKind('mo')) {
            mo.attributes.set('form', 'prefix');
          }
        }
      });
    }
  }
};

as an alternative, though I'm not sure this isn't too aggressive an approach.

A proper solution requires a semantic understanding of the underlying mathematics, which MathJax doesn't have (nor does TeX). You could use MathJax's semantic-enrichment extension to address this (it will add the <mrow> around the negation at its term):

MathJax = {
  loader: {load: ['a11y/semantic-enrich']}
};

The enrichment adds a number of additional attributes to every node, giving more information about the enriched structure. If you don't want to see these, you can filter them out, using

MathJax = {
  loader: {load: ['a11y/semantic-enrich']},
  options: {
    renderActions: {
      removeAttributes: [40,
        (doc) => {for (const math of doc.math) MathJax.config.removeAttributes(math)},
        (doc, math) => MathJax.config.removeAttributes(math)
      ]
    }
  },
  removeAttributes(math) {
    math.root.walkTree((node) => {
      const attributes = node.attributes;
      if (!attributes) return;
      const all = attributes.getAllAttributes();
      for (const name of Object.keys(all)) {
        if (name.substr(0, 14) === 'data-semantic-') {
          delete all[name];
        }
      }
    });
  }
};

dpvc avatar Jul 05 '22 11:07 dpvc

Also, I note that the spacing around the minus sign in

$$|-a|$$

and

$$\left|-a\right|$$

are the same in MathJax CHTML or SVG output, but are different in LaTeX: texlive.net

hbghlyj avatar Jul 13 '22 20:07 hbghlyj

Your last issue about |-a| is fixed in the pull request mathjax/MathJax-src#907.

dpvc avatar Feb 22 '23 20:02 dpvc