Search code examples
javascriptsvgmathjaxpaperjs

render latex as svg in JavaScript and display the result with paper.js


The following code gives "script error". Any help greatly appreciated.

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.11/paper-full.min.js"></script>

<script>
  window.MathJax = {
    // options
  };
</script>

<script type="text/javascript" id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>

<canvas id="canvas_1"></canvas>

javascript

var graphScope = new paper.PaperScope();
var canvas_1 = document.getElementById('canvas_1');
graphScope.setup(canvas_1);
graphScope.activate();

const latexToImg = function(formula) {
  return new Promise((resolve, reject) => {
    let wrapper = MathJax.tex2svg(`${formula}`, {
      em: 10,
      ex: 5,
      display: true
    })
    let output = {
      svg: "",
      img: ""
    }
    let mjOut = wrapper.getElementsByTagName("svg")[0]
    // mjOut.setAttribute("xmlns", "http://www.w3.org/2000/svg")
    output.svg = mjOut.outerHTML
    var image = new Image()
    image.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(output.svg)));
    image.onload = function() {
      var canvas = document.createElement('canvas');
      canvas.width = image.width;
      canvas.height = image.height;
      var context = canvas.getContext('2d');
      context.drawImage(image, 0, 0);
      output.img = canvas.toDataURL('image/png');
      resolve(output.img)
    }
    image.onerror = function() {
      reject()
    }
  })
}

const svgGroup = graphScope.project.importSVG(latexToImg('x^2'));

Solution

  • MathJax and Paper.js both work with SVG directly, so there's no apparent need to create an intermediate canvas element. Furthermore, MathJax.tex2svg creates an SVG DOM element, which Paper.js can use (importSVG can work with an SVG string, a DOM element, or a url string). This simplifies the code considerably, since it removes all of the canvas operations and all of the operations are synchronous.

    For some reason though, the size is pretty tiny, so I've scaled it up 20 times.

    var graphScope = new paper.PaperScope();
    var canvas_1 = document.getElementById('canvas_1');
    graphScope.setup(canvas_1);
    graphScope.activate();
    const latexToImg = function(formula) {
      let wrapper = MathJax.tex2svg(formula, {
        em: 10,
        ex: 5,
        display: true
      })
      return wrapper.querySelector('svg');
    }
    const svgGroup = graphScope.project.importSVG(latexToImg('x^2'));
    var bounds;
    svgGroup.scale(20);
    // for automatic scaling, use these lines instead:
    // bounds = svgGroup.strokeBounds;
    // svgGroup.scale(40 / bounds.height);
    bounds = svgGroup.strokeBounds;
    svgGroup.position.x = -bounds.x;
    svgGroup.position.y = -bounds.y;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.11/paper-full.min.js"></script>
    <script>
      window.MathJax = {
        // options
      };
    </script>
    <script type="text/javascript" id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
    <canvas id="canvas_1"></canvas>