Skip to content

MathJax v4.0.0-alpha.1

Pre-release
Pre-release
Compare
Choose a tag to compare
@dpvc dpvc released this 02 Oct 13:52

This is the first alpha release of a major update to MathJax, adding a number of new features and enhancements. As this includes significant changes that involve potentially breaking changes, in order to not have these changes affect existing web sites, this release increments the major version number so that sites using the mathjax@3 URLs will be protected from these changes until they update to version 4 explicitly. That is, this new release is an opt-in update.



Major New Features

Version 4 of MathJax introduces the following significant new features. It also fixes some outstanding bugs, adds some new TeX macros, and improves support for some others.

Extended Font Support

This version includes support for a number of new font sets for MathJax, and changes the default font to one based on the Latin-Modern fonts; it offers support for a much larger range of characters than MathJax's original TeX font set, but is consistent with the look-and-feel of the original MathJax TeX fonts. The new set is slightly lighter, so will not seem so bold and will fit in better on Windows machines, without losing too much on linux, Mac OS, and iOS displays. The original MathJax TeX font set is also available as an option, for those who are unwilling to part with it.

There are 11 fonts available for MathJax v4:

Font Name Original Source
mathjax-modern A version of Latin-Modern (the new default font in MathJax)
mathjax-asana A version of the Asana-Math font
mathjax-bonum A version of the Gyre Bonum font
mathjax-dejavu A version of the Gyre DejaVu font
mathjax-pagella A version of the Gyre Pagella font
mathjax-schola A version of the Gyre Schola font
mathjax-termes A version of the Gyre Termes font
mathjax-stix2 A version of the STIX2 font
mathjax-fira A version of the Fira and Fira-Math fonts
mathjax-euler A version of the Neo Euler font as an extension to mathjax-modern
mathjax-tex The original MathJax TeX font

You can specify the font you want to use by setting the font option in the new output block of your MathJax configuration (where options common to both output renders can be placed). For example,

MathJax = {
  output: {
    font: 'mathjax-stix2'
  }
};

will select the mathjax-stix2 font. For in-browser use, this will obtain the font and its data from cdn.jsdelivr.net and no other configuration is necessary. For node applications, first install the font via

npm install mathjax-stix2-font

(add -font to the name of whichever font you want and install that); MathJax should find the font in your node_modules folder. It is also possible to configure the path to the fonts using the fontPath option of the output block. This should be set to a string that indicates where the font can be found; that string should include %%FONT%% in any part of the path where the font name needs to appear. For example,

MathJax = {
  output: {
    fontPath: './node_modules/%%FONT%%-font/es5/output/fonts/%%FONT%%'
  }
};

is the default path in node applications.

It is also possible to specify an explicit URL as the font name in the configuration:

MathJax = {
  output: {
     font: 'https://cdn.jsdelivr.net/npm/mathjax-stix2-font/es5/output/fonts/mathjax-stix2'
  }
};

For those who wish to use the original MathJax font as it appears in version 3, specify the font as mathjax-tex.

The combined component files, like tex-chtml.js and mml-svg.js, include the new mathjax-modern font as part of the component so that only one file needs to be downloaded. But if you want to use a different font, you probably don't want to download mathjax-modern first and then the font you actually want to use. Instead, you should use a component ending in -nofont.js, for example, tex-chtml-nofont.js, so that the initial download is smaller, as it doesn't include mathjax-modern. See the section on MathJax v4.0 and Promises for more details concerning the proper handling of typesetting with the new fonts.

The tools for building the data needed by MathJax for your own font will be made available after version 4 is officially released. They were used to create these new fonts, but are not yet ready for public release, as they need cleaning up and documentation. But in the future, you will be able to generate an extension to an existing font (for example, to replace the letters and numbers with a different font while leaving all the rest of the characters unchanged), or a completely new font. So look for that functionality in the future.

Line-breaking Support

Version 4 includes the long-awaited support for automatic and explicit line breaking of math expressions. The support in v4 is an improvement over that in v2 in a number of ways. In particular, version 4 includes the option of breaking in-line expressions so that long expressions near the end of a line will automatically break and wrap to the next line. This is accomplished by allowing the browser to break the expressions where it needs to (following TeX's rules for what constitutes a valid in-line breakpoint). For display equations, version 4 provides support not only for automatic line breaking, but also for several other options for handling wide equations, including scaling the equation (to fit the container size), and scrolling if it is too wide. The page author can set the default, but there is also a new menu item where the viewer can switch the overflow handling to match their preferences. Version 4 also implements line-breaking of <mtext> elements (which are created by \text{} and other text-mode macros) , so long textual material can be broken automatically; this was not possible in version 2.

As part of the line-breaking support, a number of new TeX macros have been made available to control line breaks (to make them more or less desirable, or force or prevent them entirely). Also, support has been added for additional array environment template patterns that can be used to control the width of and automatic line breaking within table cells, as well as insert text before or after every cell in a column, or adjust the spacing between columns. New macros for creating boxes of specific widths in which line breaking will occur are also available, and there are options for controlling justification and indentation of the text. Such boxes are also available in MathML via additional options for the mpadded element.

Finally, version 4 now attempts to break cells within tables based on the size of table as a whole (whereas v2 broke cells only if they individually were too wide for the container, and broke them to the container width regardless of the size of the rest of the table).

Display breaking

The algorithm used in version 4 for breaking displayed equations is based on the one from version 2, but is not identical to it. Unlike version 2, the results should be nearly identical between the CHTML and SVG output renders, and the code is set up so that the algorithm can be updated or even replaced much easier than in v2. We do have plans for improvements that we hope to make in the future.

The page author can control how long expressions are handled using the new displayOverflow output jax configuration option, which can be set to overflow, scale, scroll, truncate, linebreak, or elide, though the latter is not yet implemented. The reader can override that default using the MathJax contextual menu, which has a new item in the "Math Settings" submenu for handling "Wide Expressions". For MathML input, MathJax version 4 now honors the overflow attribute of the math element, so you can mark a single long expression for line breaking, or for scrolling, for example.

Note that there is now a new output configuration block that can be used to provide options that are common to both CHTML and SVG output, so that you may have set for your default output jax will stay in effect when the user changes renderers via the contextual menu.

When displayOverflow is set to linebreak, the breaking is controlled by the settings in the linebreaks sub-block of the output (or chtml or svg) block. The default settings are

    linebreaks: {                  // options for when overflow is linebreak
      inline: true,                   // true for browser-based breaking of inline equations
      width: '100%',                  // a fixed size or a percentage of the container width
      lineleading: .2,                // the default lineleading in em units
      LinebreakVisitor: null,         // The LinebreakVisitor to use
    },

The last option is used to replace the line-breaking algorithm with a new one, so is a developer option, but the others are author-level settings that control things like how wide the lines are allowed to be, and how much extra space to put between lines.

In-line breaking

In version 4, in-line expressions can be allowed to break automatically by the browser. This is controlled via the inline option of the linebreaks block described above. When true (the default), MathJax will arrange for in-line expressions to be broken into pieces so that the browser can move parts of the equation onto the next line, if they would otherwise extend beyond the width of the expression's container. (In version 2, in-line expressions are only broken when the expression by itself would be wider than the container, and in that case, the expression would essentially act like it was inside a <div> element, so it badly disrupts the flow of the paragraph, and could cause misleading wrapping of text around the broken expression).

Note, however, that in order to do this, MathJax must make several separate elements containing math, and for SVG output in particular, several separate top-level <svg> elements. For this reason, node applications that are trying to create single svg images for the mathematics would want to set linebreaks.inline to false to avoid that.

Finally, because the browser is doing the actual determination of the locations for in-line breaks, these breaks are chosen purely by how much of the expression can fit at the end of the line before the break. That is, the parameters that mark breakpoints as good or bad (described below) are not taken into effect; however, forced breaks and no-break markers are respected.

New TeX Array Preamble Options

To help support line breaking within cells of wide tables, MathJax v4 includes support for the preamble column declarations defined in the array TeX package. These include the traditional c, l, and r for alignment of the contents of the cell (centered, left, or right), but adds support for p{width}, m{width}, and b{width} for vertical alignment of a fixed-width column in which line-breaking will occur at the given width, as well as w{align}{width} and W{align}{width}. There is also new support for >{...} and <{...} for adding content that is put before or after every entry in a column, as well as @{...} for replacing the inter-column space with the given content, and !{...} for replacing inter-column rules. Support for | and the non-standard : are improved so multiple copies of | and : now produce multiple rules that are close together. Finally, non-standard P{...}, M{...}, and B{...} are defined that produce math-mode versions of their corresponding lower-case counterparts. The \newcolumntype macro for declaring new column specifications is also available.

Note that for p, m, b, w, W, P, M, and B columns, line-breaking will occur to the given column with if line-breaking is the active overflow setting. Otherwise, wide content will overflow the width, as in actual LaTeX.

Line-breaking macros in TeX

In MathML, <mo> and <mspace> items can be marked as either good or bad breakpoint options via the linebreak="goodbreak" or linebreak="badbreak" options, or linebreaks can be prevented via linebreak="nobreak" or forced with linebreak="newline". In TeX, these can be controlled via the \goodbreak, \badbreak, \nobreak, and \break (or \\) macros. These will try to mark the operator that follows (or in some case precedes) the macro using the appropriate linebreak attribute. If there is no operator, then one will be introduced into the expression at that location. There is also \allowbreak that inserts a breakpoint that can be used if one is needed.

The \parbox[align]{width}{text} macro has been added in v4 to provide a line-breaking context of a given width and vertical alignment (t, b, c for top, bottom, center (the default), with m allowed as an alias for c) for text-mode material. Previous versions of MathJax include \vcenter{} for vertical centering, and v4 adds \vtop{} and \vbox{} for material to be aligned on the top line or bottom line of the contents. In LaTeX, their content is text-mode, but in MathJax, they are in math mode (since MathJax mainly does math-mode, and for backward compatibility with the original \vcenter{} implementation). The width of these boxes can be controlled using \hsize=<dimen> within the box, so \vtop{\hsize=10em ...} would make a box that is 10em wide whose content is line broken and aligned on the baseline of the first line. Finally, the \makebox[width][align]{text} macro can also be used to produce a line-breaking text box of a given width and vertical alignment. (This complements the \mathmakebox[width][align]{math} macro already in the mathtools package.)

While the new array preamble options allow you to control the cell widths in an array, they aren't available for other environments, like align. When line-breaking is enabled, you may want to have more control over how long lines of an alignment are broken. You can use \hbox or \mbox to avoid line breaks, but when you do allow breaks, you may want more control over indenting and alignment in such settings. For this reason, MathJax v4 introduces a non-standard indentalign environment that can be used within a cell of a table (or in any line-breaking context) to adjust the indentation amount and the horizontal alignment of any wrapped lines:

\begin{indentalign}[first][middle][last]{align}
   (long line of math)
\end{indentalign}

where first, middle, and last are optional dimensions that specify how much indentation to use for the first, middle, and last lines (where middle is any but the first or last lines). If only first and middle are provided, last will be the same as middle, and if only first is given, all three will use the same value. The align argument is one to three letters, each being one of l, c, or r, and these represent the alignments for the first, middle, and last lines. So

\begin{indentalign}[0em][1em]{l}
   (long line of math)
\end{indentalign}

would left align all lines, and indent the second and subsequent lines by 1em, when used in a context where line-breaking is in effect.

Options for <mpadded> elements

The various line-breaking boxes described above are implemented via the MathML <mpadded> element. In order to facility that, MathJax v4 adds two non-standard attributes to the mpadded element: data-overflow and data-align. When data-overflow="linebreak" is used, the contents performs line-breaking to the width specified in the element's width attribute. (No other value for data-linebreak is implemented). The data-align attribute value can be left, center or right, to get the contents (line-broken or not) aligned to the left, center, or right of the specified width. You can use an <mstyle> element within the <mpadded> element in order to set the indentshift, indentalign, and similar attributes (for first and last lines) of the content, or can specify those attributes on the individual <mo> or <mspace> elements within the <mpadded> container.

Support for HTML in MathML and TeX

HTML in MathML

The HTML5 specification allows for mixing HTML nodes inside MathML token nodes, and it is a long-standing request for MathJax to implement that as well. Version 4 finally does so. You can now use HTML nodes as children of token nodes, such as <mtext>. Thus

<mtext>a button <input type="button" value="Push Me"> to press</mtext>

is allowed, and would produce an <mtext> element containing a button surrounded by some plain text.

Because the HTML is not currently sanitized (something that could be added to the safe extension), allowing HTML in token elements would be a security issue for sites that allow user-entered MathML. For this reason, the MathML input jax has a new option allowHtmlInTokenNodes to control whether to allow it, and it is false by default, so you have to opt into this new feature if you want to use it on your site.

HTML in TeX

HTML is now allowed in TeX and LaTeX input as well. This is handled through the special <tex-html> node, which (unlike most HTML nodes) can be included within the math delimiters. So, for example

$$3 + <tex-html><input type="text" id="answer" size="10"></tex-html> = 10$$

would present an expression with an input box where a student could fill in an answer. This feature is implemented via the texhtml extension package for the TeX input jax, so you would use a configuration like

MathJax = {
  loader: {load: ['[tex]/texhtml']}},
  tex: {
      allowTexHTML: true,
      packages: {'[+]': ['texhtml']}
  }
};

to load it, add it to the packages to use, and enable it.

In its usual use case in the browser, the HTML will come from the DOM already, and so MathJax doesn't include HTML sanitization in this extension. Because of this, however, the texhtml extension does represent a security risk on sites that allow user content, if they don't sanitize the user input themselves. For this reason, there is an allowTexHTML option for the TeX input jax that must be enabled in order for the <tex-html> elements to be used. Note that \require{} is configured not to load the texhtml package, so unless you explicitly load it yourself, there should be no security issue.

Specifying the size of the HTML

In a browser, MathJax can measure the size of the HTML so that it can provide the proper amount of space for it within the equation, but in node applications, that is not possible, so MathJax provides a method for you to specify the size of the HTML explicitly. To specify the dimensions, add data-mjx-hdw="H D W" to the top-level HTML element inside the MathML token element, where H, D, and W are the height, depth, and width of the HTML. They can be in any units, but em units will work best.

How this attribute is used is handled via a new option to the output jax, htmlHDW, which can be set to 'auto' (the default), 'ignore', 'use', or 'force'. When set to ignore, the data-mjx-hdw attribute is ignored, and MathJax will try to measure the size of the HTML directly. This works well in the browser, but not in the liteDOM, jsdom, linkedom, or other non-browser adaptors. The force option means that MathJax will use the data-mjx-hdw values and will surround the HTML with additional nodes that force the HTML to have the given dimensions. This would make the browser and node both have the same representation, not relying on the browser measurements. The value use means that MathJax will assume the data-mjx-hdw values are correct and will use them without forcing the HTML to have the given dimensions. Finally, auto means that MathJax will determine which to use; this will be ignore when in the browser and force when in node applications.

Having accurate values for the data-mjx-hdw attribute is crucial to the quality of the output. To that end, the following HTML file computes the needed values. These values depend on the surrounding font, so there is a place to enter that, as well. You can also specify the font in the MathML token element that holds the HTML, as in <mtext fontfamily="Arial"><div>...</div></mtext> or <mo style="font-family: arial"><div>...</div></mo>. Otherwise, MathJax will use the surrounding font. The page below gives you a place to enter the HTML you want to measure and the font to use. Press the "Compute HDW" and the HTML is shown below together with modified HTML source that includes the needed data-mjx-hdw attribute. You can copy that and replace the original HTML will it.


<!DOCTYPE html>
<html>
<head>
<title>Compute HDW values for HTML in Token nodes</title>
<script>
function GetHDW() {
  const html = document.querySelector('mjx-html');
  const content = html.getBoundingClientRect();
  const baseline = document.querySelector('mjx-baseline').getBoundingClientRect();
  const em = parseFloat(window.getComputedStyle(html).fontSize);
  const h = baseline.top - content.top;
  const d = content.bottom - baseline.top;
  const w = content.right - content.left;
  return [h, d, w].map(x => (x / em).toFixed(3).replace(/\.?0+$/, '') + 'em').join(' ');
}
function ShowHDW() {
  const html = document.querySelector('#html').value;
  const content = document.querySelector('mjx-html');
  content.style.fontFamily = document.querySelector('#family').value;
  content.innerHTML = html
  const output = document.querySelector('#output');
  content.firstChild.setAttribute('data-mjx-hdw', GetHDW());
  output.innerHTML = content.innerHTML.replace(/</g, '&lt;');
}
</script>
<style>
mjx-measure {
  display: inline-block;
  border-left: 2px solid red;
  border-right: 2px solid red;
}
mjx-baseline {
  display: inline-block;
  height: 0;
  width: 0;
}
mjx-html {
  display: inline-block;
}
mjx-line {
  display: inline-block;
  height: 0;
  width: 1em;
  border-top: 1px solid blue;
}
#input {
  display: inline-block;
}
#input textarea {
  margin-bottom: 3px;
}
#input input[type="button"] {
  float: right;
}
</style>
</head>
<body>

<h1>Compute HDW values for HTML in Token nodes</h1>

<p id="input">
<textarea id="html" cols="80" rows="10">
<div>HTML</div>
</textarea><br>
Font family: <input type="text" id="family">
<input type="button" value="Compute HDW" onclick="ShowHDW()">
</p>
<h2>The HTML:</h2>
<p>
<mjx-line></mjx-line><mjx-measure><mjx-baseline></mjx-baseline><mjx-html>
&#xA0;
</mjx-html></mjx-measure><mjx-line></mjx-line>
</p>
<h2>The HTML with the HDW attribute:</h2>
<p id="output">
</p>

</body>
</html>

Of course, you can use this as a basis for automating the process using something like puppeteer, if you wish.

Expression Explorer Included in Combined Components

In version 3 of MathJax, the expression explorer was not active by default, and so those with assistive needs had to turn it on explicitly, and it was loaded dynamically at that time. In its place, the assistive-MathML extension was used to generate a hidden MathML expression that could be read by those screen readers that can process MathML. This was a stop-gap measure that was both clunky and somewhat fragile, and changes in screen readers often rendered it ineffective (e.g., the current VoiceOver version doesn't read the mathematics and skips it entirely).

Because of these problems, and because the Speech Rule Engine (SRE) that underlies MathJax's assistive support has been substantially rewritten, we are now packaging the expression explorer and SRE as part of all the combined configuration files, and the assistive MathML extension has been removed from them (though it can still be turned on in the Accessibility sub-menu of the MathJax contextual menu). The SRE generates speech automatically for the expressions in your page and makes these speech strings available to screen readers using ARIA labels and live regions, a technology that has far better support in screen readers than the hidden MathML. This is a substantial change, however, and there are many screen-reader/browser/operating-system combinations, so some issues may arise. Please report problems that you encounter to our issue tracker.

While basic speech is provided for expression via an aria-label attribute, so that a screen reader can read the math naturally as part of the page, it is still possible to interactively explore expressions. This features is available by default in version 4, but interaction with the expressions is now achieved using the ARIA tree view paradigm, moving away from the previously used application role. This should provide smoother interaction with screen readers on all platforms. Speech during exploration is still exposed via a live region; however, we are still experimenting with a number of alternative methods before the final v4.0 release.

In addition, MathJax now provides optional auto-voicing of expressions together with step-by-step highlighting of expression while a formula is spoken. This feature is primarily aimed at users who do not normally utilize a screen reader, and in particular as support for dyslexic readers. It currently has to be switched on explicitly, either in the speech sub-item of the explorer sub-menu of the MathJax contextual menu, or using the voicing option in the a11y sub-block of the options configuration block. Speech is generated by providing SSML annotations to the browser's speechSynthesis API. While this makes use of the full range of prosody annotations available in SRE's speech rules, the feature is only available in browsers that come with an implementation of the speechSynthesis API and with built in voices.

MathJax v4.0 and Promises

Because the new MathJax fonts include more extensive character coverage, meaning much more data is required, the fonts have been broken down into smaller pieces that can be loaded dynamically, rather than being one big data file, as was the case with version 3. This allows the initial download of MathJax to be smaller, while still accommodating rarely used glyphs for those who need them.

As a result, however, when the data for one of these ranges is needed, MathJax will pause and wait for the data to arrive from the CDN. That means that producing MathJax output is now potentially an asynchronous process, which was not the case in v3. So in the past, as long as you pre-loaded all the TeX extensions that you needed (e.g., with one of the -full components), you could use synchronous calls to MathJax.tex2svg() or the other similar functions. With the new (larger) dynamic fonts, that is no longer guaranteed. That means you should instead use the promise-based versions of these calls, like MathJax.tex2svgPromise(), in order to properly handle the potential for dynamically loaded font data. Without this, you may get errors indicating a "retry" error, which is what MathJax uses to mediate its asynchronous loading actions.

If you can not avoid using synchronous calls, then you may need to load all the font dynamic data up front using a single promise-based call before you start using MathJax synchronously. This can be done using

MathJax.startup.document.outputJax.font.loadDynamicFiles();

to load all the font dynamic data. This function returns a promise, and you should wait for it to resolve before calling any MathJax conversion functions. Note, however, that there can be a lot of font data, and these fonts may include many characters that will never get used, so only do this if you absolutely have to. It is better to use the promise-based conversion functions if you can.

For node applications, you can use

MathJax.startup.document.outputJax.font.loadDynamicFilesSync();

to load the font data synchronously, provided you have defined the MathJax loading mechanism by importing mathjax-full/js/util/asyncLoad/node.ts before hand.

Availability of v4.0.0-alpha

The MathJax 4.0.0-alpha.1 version can be accessed via CDN as

https://cdn.jsdelivr.net/npm/mathjax@4.0.0-alpha.1/es5/tex-mml-chtml.js

or using one of the other combined configuration files:

  • tex-html.js
  • tex-svg.js
  • tex-mml-chtml.js
  • tex-mml-svg.js
  • mml-html.js
  • mml-svg.js

Each of these includes the mathjax-modern font, but also comes in a version ending in -nofont.js (e.g., tex-mml-chtml-nofont.js) that does not include it, where you are expected to specify the font using the output.font configuration option. This saves your readers from having to download the mathjax-modern font that is not going to be used. The other combined configurations have been removed, as they were either redundant (now that the explorer is already included in all combined components), or where the ones that included tex-full. The latter have been removed because you will need to use the promise-based calls anyway, and that will handle the autoloading of extensions as well, and since the all-packages extension doesn't include the newer packages, and so isn't really "all" packages anyway.

The source code for MathJax v4 is available in the mathjax-full@4.0.0-alpha.1 npm package, or in the v4.0.0-alpha branch of the MathJax-src repository on GitHub.

The fonts are each in their own npm package, e.g., mathjax-modern-font, mathjax-stix2-font, etc., which you can install in your own node applications as needed. In a browser, MathJax should access the fonts from jsdelivr automatically. These font packages also include combined configuration files that are like tex-mml-chtml.js and tex-mml-svg.js, but that include that package's font rather than mathjax-modern. For example, the mathjax-stix2-font package include tex-mml-chtml-mathjax-stix2.js and tex-mml-svg-mathjax-stix2.js, so you can use

https://cdn.jsdelivr.net/npm/mathjax-stix2-font/ex5/tex-mml-chtml-mathjax-stix2.js

in order to get a single-file MathJax component that includes the mathjax-stix2 font rather than mathjax-modern. In particular, you can get the equivalent of the tex-mml-html.js file with the original MathJax TeX font all in one file using

https://cdn.jsdelivr.net/npm/mathjax-tex-font/ex5/tex-mml-chtml-mathjax-tex.js

This font does not have dynamic ranges (all the font data is in one file), so it should operate much the same as MathJax v3 in that respect.