26

I'm doing some writing where I use MathJax to render the math. I also occasionally include SVG diagrams which are generated dynamically by javascript. Those SVG diagrams occasionally include math.

I'd like the text elements in those SVG diagrams to be rendered using MathJax. I know how to cause the dynamic math to be rendered. However, the mathjax output is in <span>s which aren't valid SVG and don't show up.

This persists when I configure MathJax to use SVG output mode, though this is perhaps due to an improper use of SVG output mode. I changed by MathJax CDN link to http://cdn.mathjax.org/mathjax/2.1-latest/MathJax.js?config=TeX-AMS-MML_SVG, which did not produce SVG output. I haven't been able to coax MathJax into actually outputting SVG elements yet.

I've considered using an SVG <foreignObject> tag which is non-ideal because (to my knowledge) I must specify a width and height, which is inconvenient.

Is there a better way to include MathJax rendered text inside SVG inside HTML?

So8res
  • 9,856
  • 9
  • 56
  • 86

3 Answers3

14

Currently, the only way to include MathJax within an SVG diagram is through <foreignObject>. Yes, it is not ideal, not only because you need to provide a size, but also because IE9+ doesn't support <foreignObject>.

As for the SVG output, if you have used the MathJax contextual menu to select a math render, that will override the renderer choice in the document, so you may still be seeing HTML-CSS output for that reason. The value is stored in a cookie, so that it will be remembered from session to session. You can delete the cookie to remove the setting, or use the MathJax menu again to select SVG rendering.

Note, however, that this isn't going to help you either, as MathJax's SVG output is not just an SVG snippet that can be included into a larger SVG file, but include some HTML and the <svg> element itself. Moreover, MathJax needs the surrounding HTML to determine things like the font-size to use, and some other factors, so it is not going to be able to placed directly into your SVG diagram.

The <foreignObject> approach is really the only reliable way at the moment.

Edit: Here is a full example:

<!DOCTYPE html>
<html>
<head>
<title>MathJax in SVG diagram</title>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG"></script>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1000" height="500">
  <circle cx="100" cy="100" r="99" fill="yellow" stroke="red" />
  <circle cx="100" cy="100" r="3" fill="blue" />
  <foreignObject x="100" y="100" width="100" height="100">
    <div xmlns="http://www.w3.org/1999/xhtml" style="font-family:Times; font-size:15px">
    \(\displaystyle{x+1\over y-1}\)
    </div>
  </foreignObject>
</svg>
</body>
</html>
Davide Cervone
  • 11,211
  • 1
  • 28
  • 48
  • would you have a full example somewhere? I have tried that inside an IPython notebook, and couldn't get the math part to be processed (although other formulas on the same page are properly handled). TIA! – akim May 23 '14 at 15:12
  • @akim, I've added an example to my answer. – Davide Cervone May 25 '14 at 12:38
  • Thanks a lot. Actually I had already found this exact same example :) But I can't get this to work inside an IPython notebook :( The LaTeX \( \), $...$ etc. remain. Actually, it works if I build a full HTML widget, but I fail to have it work when I try to generate a SVG widget. Any clue? – akim May 26 '14 at 08:13
  • Actually, to be more specific, my goal is to render a GraphViz source inside IPython, with LaTeX labels, to be rendered by MathJax. I know this is kinda wrong since dot will use the size of the source code instead of the size of the rendered formulas, but that's better than plain text anyway. – akim May 26 '14 at 08:23
  • Sorry, I have never used IPython, so I can't give you any advice there. It may have to do with when MathJax is called in comparison to when the SVG is created (MathJax will need to be called after the SVG is in place). It might also be that the SVG is protected from MathJax by a `tex2jax_ignore` element. You'll probably need to work the the IPython folks to figure it out. – Davide Cervone May 26 '14 at 20:45
  • Remember to use `TeX-AMS-MML_SVG`, rather than the default `TeX-AMS-MML_HTMLorMML` config for MathJax. – herrlich10 Jan 31 '15 at 04:48
  • one way around the problem of providing width and height that I use, is to always set foreignObject size to cover the whole svg, and position the text inside the html (or css) which is inside it – qbolec Jul 16 '15 at 14:39
  • Note from the future: cdn.mathjax.org is nearing its end-of-life, check https://www.mathjax.org/cdn-shutting-down for migration tips (and perhaps update your post for future readers). – Peter Krautzberger Apr 21 '17 at 07:39
  • This does no longer seem to work? Copy pasting the provided code works in mathjax 2.5, but not in 2.6 and 2.7 – R D May 03 '17 at 10:06
  • @RD, it still works for me in 2.7. What browsers are you using? – Davide Cervone May 03 '17 at 12:11
  • @DavideCervone Chrome 58 64bit Win10 without extensions: it shows the preview of the equation for a few milliseconds, then the preview is hidden and nothing displayed. Works as expected in Firefox with Mathjax 2.7 or in Chrome with versions <= 2.5. – R D May 03 '17 at 16:01
  • @RD, not sure what to tell you. It works for me in Chrome 58 64-bit Win10 with MathJax 2.7. Can you clear your cookies and see if that makes a difference? – Davide Cervone May 03 '17 at 18:43
4

There is a way to include MathJax SVG elements in an SVG diagram, without requiring foreignObject. A full demonstration of the technique is at http://puzlet.com/m/b00b3.

Say you have an SVG diagram like this (and containing any SVG elements) in your web page:

<svg id="diagram" xmlns='http://www.w3.org/2000/svg' 
    xmlns:xlink="http://www.w3.org/1999/xlink"
    version='1.1' width='720' height='100'>
</svg>

and the following MathJax:

<div id="mathjaxSource">
$\Omega$
</div>

Use this CoffeeScript (plus jQuery) to integrate the MathJax element into your SVG diagram:

diagram = $("#diagram")  # SVG diagram
obj = new MathJaxObject "mathjaxSource"  # MathJax object (see class definition below)
obj.appendTo diagram  # Append MathJax object to diagram
obj.translate 100, 200  # Position MathJax object within diagram

You can also use the width() and height() methods for centering and vertical alignment.

class MathJaxObject

    constructor: (@divId, @scale=0.02) ->
        @source = $("##{@divId}").find ".MathJax_SVG"
        @svg = @source.find "svg"
        g = @svg.find "g"
        @group = $(g[0]).clone()
        @translate 0, 0

    viewBox: -> @svg[0].viewBox

    width: -> @scale * @viewBox().baseVal.width

    height: -> @scale * @viewBox().baseVal.height

    translate: (@dx, @dy) ->
        dy = @dy + (-@scale * @viewBox().baseVal.y)
        @group[0].setAttribute "transform", 
            "translate(#{@dx} #{dy}) scale(#{@scale}) matrix(1 0 0 -1 0 0)"

    appendTo: (diagram) ->
        diagram.append @group
martinc
  • 84
  • 3
  • 1
    Could you explain what is going on here? I would really like to avoid a dependency on CoffeeScript and jQuery just to do this if it is a simple task. – Jason S May 22 '14 at 18:17
  • nm, I figured it out, I'll post a decoupled solution when I get a chance – Jason S May 22 '14 at 19:30
  • 2
    you may also read this https://www.embeddedrelated.com/showarticle/599.php (by @JasonS I presume) – yota Sep 04 '19 at 11:43
1

I've used the following, which defines a function that takes latex code as input and appends the MathJax processed svg to a target SVG element.

// First create a new element to hold the initial Latex code
// and eventual MathJax generated SVG. This element isn't added
// to the main docuemnt, so it's never seen.
let mathBuffer = document.createElement("div");

// Then define a function that takes LaTeX source code
// and places the resulting generated SVG in a target SVG element.
mathSVG = function (latex, target) {
  // Update the buffer with the source latex.
  mathBuffer.textContent = latex;
  // Get MathJax to process and typeset.
  MathJax.Hub.Queue(["Typeset", MathJax.Hub, mathBuffer]);
  // Queue a callback function that will execute once it's finished typesetting.
  MathJax.Hub.Queue(function () {
    // This (svg) is the generated graphics.
    const svg = mathBuffer.childNodes[1].childNodes[0];
    // The next line is optional, play with positioning as you see fit.
    svg.setAttribute("y", "-11pt");
    // Move the generated svg from the buffer to the target element.
    target.appendChild(svg);
    // Clear the buffer.
    mathBuffer.textContent = "";
  });
};

In the above, I'm assuming an html host document with svg elements within. It should also work in a svg host document too.

Freddie Page
  • 104
  • 1
  • 2
  • 6
  • 1
    Any chance you would know how to translate this into MathJax 3? It looks pretty nice, but I'd love to use the cleaner async interface of MathJax 3. – David Roundy Aug 25 '20 at 21:49