Getting latex labels into an SVG: newbie tries and fails

Hi All:

I’m new to observable and trying to understand how to get nicely-formatted math into my axis labels in plots created using the Plot library.

After much searching, all the examples I’ve found that use latex in graphics do not use the Plot library.

So, I’m thinking the way to do this is to create the plot with Plot, then use d3 to select the plot and add the labels I want.

I’ve succeeded in adding shapes and plain text to my plots with d3, but have gotten stuck trying to add latex. And the example notebooks that use latex in svgs that I’ve been able to find go over my head. What I’m really hoping to gain from an answer here is a minimal example that adds latex to an svg, plus some explanation for how the simple example works.

Here’s a notebook show me trying and failing to use the standard library to put latex into an svg in a figure in the most basic way. What am I doing wrong?

Maybe I should also mention that I’ve run across a notebook showing how to use MathJax to put latex in SVG (linked below). That seems fine, but before I reach for that I’d really like to understand how to do this using tools in Observable’s built-in libraries.

1 Like

Your approach with foreignObject should work fine; I simply increased the size of the foreignObject and removed the viewBox and the image looked good. Thus:

{
const fragmentContainingMath = svg`
  <foreignObject width="200" height="100">
    <div xmlns="http://www.w3.org/1999/xhtml" width="10" height="10">
      ${md`This axis is the sum of the effort levels ${tex`\sum_i e_i`}`}
    </div>
  </foreignObject>
  `;

return (svg`
  <svg width="200" height="200">
  <g transform = "translate(10,10)">
    ${fragmentContainingMath}
  </g>
  </svg>
`)
}

In the past, I’ve found the appearance of foreignObjects in SVG to vary widely across browsers and platforms; perhaps, that’s changed. Another reliable technique, though, is to generate a relatively positioned DIV that contains your SVG and then lay an absolutely positioned DIV over that containing your KaTeX. Here’s a simple example:


Neither of these approaches yield results that easily export to SVG, though. That’s the main reason that I personally prefer to embed SVG generated by MathJax into the image because it is then fully incorporated into the image. For example, if you download the images from the SVG and MathJax notebook, you’ll find that the TeX images are included in the output SVG. The overlay option that I’ve outlined here looks nice on Observable and in an embeded web page but the KaTeX is not included on export to SVG. That same problem

3 Likes

Thank you, mcmcclur! Your explanation of the options available and the advantages of Mathjax-generated SVG is really great. I think I’ve seen all these issues mentioned in the various items on latex in SVG that I’ve run across on this forum and elsewhere. But yours is the first account I’ve seen that clearly and directly lays out how this all works!!!

2 Likes

I’ve got a follow up question for you…In SVG and MathJax 3, you use the function tex2svg from MathJax. I’ve been looking at the MathJax docs trying to find the full documentation for what that function returns. I can see from your use of it in that notebook that it returns an object, with querySelector one if it’s methods. But where are all the methods and properties for that object documented? At this point, the entirely of the API section of the MathJax docs seems to be under construction. Is the only option to comb through the source code?

Thanks!

It returns an mjx-container object defined in the tex2svg script. It extends HTML Element, which is why there’s a querySelector method. The raw SVG itself appears as part of the innerHTML of the object. Assuming the script has been loaded in the head of an HTML document, the shortest bit of code to get math into a webpage might look like so:

document.body.appendChild(MathJax.tex2svg(String.raw`\sqrt{x^2+1}`));

I think the SVG is displayed natively. The mjx-container implements a number of other goodies accessible via a contextual menu.

Yeah, there’s no real API reference, as far as I know. The documentation, though, is pretty good - if you’re willing to read through it. Here’s a specific section on converting input formats to output formats.

1 Like

thanks again!