diff --git a/CHANGES.rst b/CHANGES.rst index cd36d83957b..845302312b2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -68,6 +68,9 @@ Features added Patch by Jean-François B. * #13508: Initial support for :pep:`695` type aliases. Patch by Martin Matouš, Jeremy Maitin-Shepard, and Adam Turner. +* #14023: Add the new :confval:`mathjax_config_path` option + to load MathJax configuration from a file. + Patch by Randolf Scholz and Adam Turner. Bugs fixed ---------- diff --git a/doc/usage/extensions/math.rst b/doc/usage/extensions/math.rst index fb41d66d8fb..f39aaa24fd2 100644 --- a/doc/usage/extensions/math.rst +++ b/doc/usage/extensions/math.rst @@ -197,8 +197,8 @@ are built: :synopsis: Render math using JavaScript via MathJax. .. warning:: - Version 4.0 changes the version of MathJax used to version 3. You may need to - override ``mathjax_path`` to + Sphinx 4.0 changes the version of MathJax used to version 3. + You may need to override :confval:`mathjax_path` to ``https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML`` or update your configuration options for version 3 (see :confval:`mathjax3_config`). @@ -217,6 +217,13 @@ Sphinx but is set to automatically include it from a third-party site. You should use the math :rst:dir:`directive ` and :rst:role:`role `, not the native MathJax ``$$``, ``\(``, etc. +.. tip:: + + MathJax configuration can be supplied in a JavaScript file + by using the :confval:`mathjax_config_path` option. + This is useful for more complex configurations that are hard to express + only using a Python dictionary, for example JavaScript functions. + .. confval:: mathjax_path :type: :code-py:`str` @@ -268,8 +275,9 @@ Sphinx but is set to automatically include it from a third-party site. :default: :code-py:`None` The configuration options for MathJax v3 (which is used by default). - The given dictionary is assigned to the JavaScript variable - ``window.MathJax``. + If given, the dictionary is converted to a JSON object + and assigned to the JavaScript variable ``window.MathJax``. + For more information, please read `Configuring MathJax`__. __ https://docs.mathjax.org/en/latest/web/configuration.html#configuration @@ -318,6 +326,28 @@ Sphinx but is set to automatically include it from a third-party site. This has been renamed to :confval:`mathjax2_config`. :confval:`mathjax_config` is still supported for backwards compatibility. +.. confval:: mathjax_config_path + :type: :code-py:`str` + :default: :code-py:`''` + + If given, this must be the path of a JavaScript (:file:`.js`) file + (path relative to the :term:`configuration directory`) + that contains the configuration options for MathJax. + Example: + + .. code-block:: python + + mathjax_config_path = 'mathjax-config.js' + + .. important:: The user is responsible for ensuring that the given file + is compatible with the version of MathJax being used. + + For more information, please read `Configuring MathJax`__. + + __ https://docs.mathjax.org/en/latest/web/configuration.html#configuration + + .. versionadded:: 8.3 + :mod:`sphinxcontrib.jsmath` -- Render math via JavaScript --------------------------------------------------------- diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py index 62220cc697f..d8d083725c4 100644 --- a/sphinx/ext/mathjax.py +++ b/sphinx/ext/mathjax.py @@ -95,30 +95,43 @@ def install_mathjax( builder = cast('StandaloneHTMLBuilder', app.builder) page_has_equations = context.get('has_maths_elements', False) + + # Enable mathjax only if equations exists if app.registry.html_assets_policy == 'always' or page_has_equations: - # Enable mathjax only if equations exists if app.config.mathjax2_config: if app.config.mathjax_path == MATHJAX_URL: logger.warning( 'mathjax_config/mathjax2_config does not work ' 'for the current MathJax version, use mathjax3_config instead' ) - body = 'MathJax.Hub.Config(%s)' % json.dumps(app.config.mathjax2_config) + body = f'MathJax.Hub.Config({json.dumps(app.config.mathjax2_config)})' builder.add_js_file('', type='text/x-mathjax-config', body=body) + if app.config.mathjax3_config: - body = 'window.MathJax = %s' % json.dumps(app.config.mathjax3_config) + body = f'window.MathJax = {json.dumps(app.config.mathjax3_config)}' + builder.add_js_file('', body=body) + + if app.config.mathjax_config_path: + config_path = app.confdir / app.config.mathjax_config_path + if not config_path.exists(): + msg = f'mathjax_config_path file not found: {config_path}' + raise ExtensionError(msg) + if not config_path.is_file() or config_path.suffix != '.js': + msg = f'mathjax_config_path: expected a .js file, but got {config_path}' + raise ExtensionError(msg) + body = config_path.read_text(encoding='utf-8') builder.add_js_file('', body=body) options = {} if app.config.mathjax_options: options.update(app.config.mathjax_options) if 'async' not in options and 'defer' not in options: - if app.config.mathjax3_config: - # Load MathJax v3 via "defer" method - options['defer'] = 'defer' - else: - # Load other MathJax via "async" method + if app.config.mathjax2_config or app.config.mathjax_config: + # Load old MathJax versions via the 'async' method options['async'] = 'async' + else: + # Load MathJax v3+ via the 'defer' method + options['defer'] = 'defer' builder.add_js_file(app.config.mathjax_path, **options) @@ -149,6 +162,7 @@ def setup(app: Sphinx) -> ExtensionMetadata: app.add_config_value( 'mathjax3_config', None, 'html', types=frozenset({dict, NoneType}) ) + app.add_config_value('mathjax_config_path', '', 'html', types=frozenset({str})) app.connect('html-page-context', install_mathjax) return { diff --git a/tests/roots/test-ext-math/_static/custom_mathjax_config.js b/tests/roots/test-ext-math/_static/custom_mathjax_config.js new file mode 100644 index 00000000000..bdcc29251b0 --- /dev/null +++ b/tests/roots/test-ext-math/_static/custom_mathjax_config.js @@ -0,0 +1 @@ +window.MathJax = {"extensions": ["tex2jax.js"]} diff --git a/tests/test_extensions/test_ext_math.py b/tests/test_extensions/test_ext_math.py index 9c8e620d655..3df10de23af 100644 --- a/tests/test_extensions/test_ext_math.py +++ b/tests/test_extensions/test_ext_math.py @@ -124,7 +124,7 @@ def test_mathjax_options(app: SphinxTestApp) -> None: content = (app.outdir / 'index.html').read_text(encoding='utf8') shutil.rmtree(app.outdir) assert ( - '' ) in content @@ -376,6 +376,25 @@ def test_mathjax3_config(app: SphinxTestApp) -> None: assert '' in content +@pytest.mark.sphinx( + 'html', + testroot='ext-math', + confoverrides={ + 'extensions': ['sphinx.ext.mathjax'], + 'mathjax_config_path': '_static/custom_mathjax_config.js', + }, +) +def test_mathjax_config_path_config(app: SphinxTestApp) -> None: + app.build(force_all=True) + + content = (app.outdir / 'index.html').read_text(encoding='utf8') + assert MATHJAX_URL in content + assert f'