-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support rendering tex even when global MathJax rendering mode is not SVG #2994
Conversation
1. Specify the SVG font 2. Set rendering mode to SVG just prior to typesetting, then restore it to it's original value just afterwards With this change, latex rendering works in the following MathJax configurations: - TeX-MML-AM_CHTML - TeX-MML-AM_HTMLorMML - TeX-MML-AM_SVG - TeX-AMS-MML_HTMLorMML - TeX-AMS_CHTML - TeX-AMS_SVG - TeX-AMS_HTML - TeX-AMS-MML_SVG (It still does not work if the global MathJax TeX extensions are not loaded.) Previously it only worked in the TeX-AMS-MML_SVG configuration. Also, with these changes latex rendering works without any additional configuration when used from Python (and probably R) inside the Jupyter Notebook.
src/lib/svg_text_utils.js
Outdated
if(originalRenderer !== 'SVG') { | ||
MathJax.Hub.Queue(['setRenderer', MathJax.Hub, originalRenderer]); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this decision synchronously?
function finalize() { ... /* all the stuff we do with the results */ }
var originalRenderer = MathJax.Hub.config.menuSettings.renderer;
if(originalRenderer !== 'SVG') {
MathJax.Hub.Queue(['setRenderer', ...], ['Typeset', ...], ['setRenderer', ...], finalize);
}
else {
MathJax.Hub.Queue(['Typeset', ...], finalize);
}
As is I'm concerned
- that it adds unnecessary overhead to the svg-only case
- there's a potential race condition if someone else adds to the queue before we reset the renderer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trouble is that MathJax.Hub.config.menuSettings.renderer
isn't initialized right away, so if it's accessed outside of a Queue
command it will be null in the beginning. Perhaps there's a better way to wait for the first initialization than putting the MathJax.Hub.config.menuSettings.renderer
on the Queue
, but I'm not sure at this point.
Let me know if you have any ideas, I'll try a few more experiments tonight...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trouble is that
MathJax.Hub.config.menuSettings.renderer
isn't initialized right away
Ah OK... I see it immediately but perhaps that's just due to how we loaded MathJax in the first place... but I guess even if it's present, there's a case where someone else has put something in the Queue
that changes the renderer, so by the time our expression gets rendered we have a different value from the one we read synchronously.
OK, so perhaps we can't do anything about the overhead - MathJax has enough overhead anyway, this is probably the least of our problems - but the race condition is still there. Since we're already in a function being executed by the Queue
, can we just call MathJax.Hub.setRenderer(originalRenderer)
directly, right where you have it, rather than putting it in the Queue
? Or I guess, to be even safer, move this to the end of our finalize function so we can return MathJax.Hub.setRenderer(originalRenderer)
in the (presumably unlikely) event that this happens async.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I see what you mean about the race condition. I'll give that a try again, but for some reason when I was testing earlier the direct MathJax.Hub.setRenderer
call (not through the Queue
) wasn't working properly, but I don't remember in what context that was. I'll report back...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it does work fine to end with return MathJax.Hub.setRenderer(originalRenderer)
and remove the extra Queue
command. Done in 1dd4fd3.
Thanks @jonmmease - I had gone looking for a way to query and reset the renderer a while back and failed... so thanks for finding this! The mocks that include LaTeX have changed - presumably because of the explicit font. Presumably explicit is more robust than implicit, and this one looks to be slightly more compact which is probably better for backward compatibility than if it had gotten larger... @etpinard are you OK with this change? If so we just need to regenerate these baseline images. related: #2300, #2403 - does this solve those issues, or is there more required? Can we write a test for this, something like: MathJax.Hub.Queue(
['setRenderer', /* not SVG */ ... ],
function() {
Plotly.newPlot(gd, /* something with MathJax */).then(function() {
expect(renderer).toBe(/* whatever we set it to above */);
})
.catch(failTest).then(done);
}
); |
To fix #2300 (and maybe #2403) I think Plotly.js would need this change, and nbconvert should also explicitly invoke typesetting, instead of relying on the default MathJax behavior of automatically typesetting everything. On the other hand, maybe we don't need to disable automatic typesetting anymore. I'll try removing |
This should remove a potential race condition
That might be a breaking change for some users. I would love it if we could figure out a way to allow you to invoke that mode (and/or other config changes that we would then reset just like you're doing here with the renderer), but that seems tricky given that we're making the current MathJax config call on load. I suppose we could imagine deferring that call to the first |
Are you thinking that removing the But when I remove the option, and let MathJax perform the auto typesetting on load, that seems to break the Plotly.js latex rendering, even with the BTW, I've been testing this outside of the plotly.js project like this: <!DOCTYPE html>
<html lang="en">
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML">
</script>
<script type="text/javascript" src="dist/plotly.min.js"></script>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="graph"></div>
<div>
<p>Some $$\LaTeX$$ math</p>
</div>
</body>
<script>
Plotly.plot('graph', [{y: [2,1,2]}],
{title: '$f(x) = \\int_0^\\infty \\Psi(x, t) \\; \\text{d} t $'}).then(function() {
});
// MathJax.Hub.Queue(['Typeset', MathJax.Hub]);
</script>
</html> Right now, with If I remove |
This avoids race condition where an external typesetting operation is in progress (or already on the Queue) at the start `texToSVG` function. With this change, we no longer need to set `skipStartupTypeset` to true, which alters the global default MathJax behavior of typesetting the full page automatically on startup. Other utilities, such as nbconvert, have not worked properly with plotly because they rely on this default behavior
Alright, I think I'm getting somewhere!
This could be a breaking change for Plotly.js users if they were relying on the presence of Plotly.js to disable MathJax's default behavior of running typesetting on startup. To disable this explicitly, a user would need to declare MathJax like this: <script type="text/x-mathjax-config">
MathJax.Hub.Config({skipStartupTypeset: true});
</script>
<script src="mathjax/MathJax.js?config=TeX-AMS-MML_SVG"></script> and if we keep this change it would probably make sense to add this to the recommended configuration in
Fonts: If we want to lock in the font, I think it makes sense to go with Here's a font side-by-side Performance: Testing: |
@jonmmease that was some great detective work! If we were adding MathJax support today knowing what we know now, this would be the solution, no question. I might try some things to reduce the overhead, particularly for the case where plotly.js is the only MathJax consumer so the config setting/resetting is unnecessary... but you're probably right that this is a negligible portion of overall MathJax rendering time, so I wouldn't hold it up for that. We need to think through the potential breaking change though - three bad things could happen:
The first seems like an exceedingly rare problem; the second is purportedly negligible though I haven't measured it personally; the third is perhaps the most likely, though I get the impression that the number of users in this camp is far fewer than those who are currently unable to adapt to the config we've set, and these users (and any bothered by the first two issues) need only make a small change, reinstating the piece(s) of our config that they depended on as Jon shows above. So I guess I would call this an acceptably-small break given the benefits and ease of adaptation for affected users. But would appreciate other perspectives, particularly from @etpinard. |
How hard would it be to make to cover @jonmmease 's brilliant new MathJax logic under a config flag? For example, {
MathJaxConfig: 'global' || 'local'
} where |
Problem is MathJax config happens now on loading plotly.js, not on plotting. So there are effects before any |
I don't really understand what's happening here, but MathJax seems to support configuration parameters as part of the
Could we (and would we want to) do something similar for plotly.js?
Although, this might not cover the JupyterWidgets case since that's packaged through webpack and you never write down the |
Some more observations/thoughts.It appears that not all of the config settings can be set and unset successfully. For example, it doesn't look like it's possible for the outer context to use the Also, if we don't disable startup typesetting then the annoying "MathJax rendering" notifications display in the bottom of the browser on load. These are currently disabled by setting ProposalUpdate: We decided against this, but for posterity... So here's a proposal for a compromise between where we are, what we want, and what MathJax can do.
MathJax.Hub.Config({
skipStartupTypeset: true,
tex2jax: {
inlineMath: [['$', '$'], ['\\(', '\\)']]
},
});
MathJax.Hub.Config({
messageStyle: 'none',
displayAlign: 'left',
}) Then we restore the renderer and configuration afterward.
Rational
Implicationsfor plotly.js users: This would only be breaking if they were relying on the presence of Plotly.js to set the global alignment to for plotly.py: Plotly.py will be responsible for making sure that MathJax is configured with an available SVG font before invoking plotly.js. This will be done in a small straightforward bit of extra logic for for nbconvert / sphnix / etc.:. To work with and without plotly.js, I would recommend they too configure MathJax with If all three of these projects/groups adapt in these ways, then I think plotly.js latex could finally be widely used in these important technical computing contexts. |
Add a MathJaxConfig option that, when set to 'local', causes plotly.js to bypass all global MathJax configuration. Regardless of the value of MathJaxConfig, all MathJax rendering commands are surrounded by logic to properly configure the MathJax properties that plotly.js needs. When MathJaxConfig is not 'local', plotly.js will continue to setup the global MathJax config as it always has. This is the default behavior for backward compatibility. When plotly.js is used in a situation where it is not the only component using MathJax, then MathJaxConfig should be set to 'local' to keep from overwriting the user's desired global configuration.
dist/README.md
Outdated
@@ -33,6 +33,19 @@ or the un-minified version as: | |||
|
|||
You can grab the relevant MathJax files in `./dist/extras/mathjax/`. | |||
|
|||
By default, plotly.js will modify the global MathJax configuration on load. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dist/README.md
is a generated file, you'll need to move this block to
https://github.com/plotly/plotly.js/blob/master/tasks/stats.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 57e32ba
OK to merge this @jonmmease @alexcjohnson ? I think all our concerns were addressed. |
I'm happy with it 🙂 |
💃 🏆 🎉 |
Merging this thing! |
This PR makes two small changes, that together make it possible for Plotly.js to successfully render Latex even when the global MathJax rendering mode is not SVG (For example, in the Jupyter Notebook)
Changes:
With this change, latex rendering works in the following MathJax combined configurations:
Previously it only worked in the TeX-AMS-MML_SVG configuration.
(Note: It still does not work if the global MathJax TeX extensions are not loaded, as in the case for the "AM_SVG" configuration for example.)