3

I'm trying to pre-render a MathJax html file using PhantomJS. For example, suppose in math.html I have:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="MathJax/MathJax.js"></script>
    <script src="ConfigMathJax.js"></script>
  </head>
  <body>
    <span class="math">\(e = m c^2\)</span>
  </body>
</html>

My (broken) render script currently looks like:

var page = require('webpage').create();
var system = require('system');
var fs = require('fs');
page.open(system.args[1], function () {
    page.evaluate(function(){
      var flag = false;
      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
      MathJax.Hub.Queue(function(){
        console.log(page.content);
        phantom.exit();
      });
    });
});

I've attempted to write the page to standard out and exit after the MathJax render command is called from the queue. But it seems I'm in the "page"'s context rather than the top-level Phantom context. The variable page can't be found: ReferenceError: Can't find variable: page.

It I hack in a setTimeout instead of using the flag:

var page = require('webpage').create();                                                  
var system = require('system');                                                          
var fs = require('fs');                                                                  
page.open(system.args[1], function () {                                                  
    page.evaluate(function(){                                                            
      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);                                        
    });                                                                                  
    setTimeout(function(){                                                               
      console.log(page.content);                                                         
      phantom.exit();                                                                    
    },10000);                                                                            
}); 

then I get the desired output, but of course the wait time 10000ms will depend on the content.

How can I let PhantomJS know that MathJax is done rendering?

Is this a sandbox issue?

Alec Jacobson
  • 6,032
  • 5
  • 51
  • 88
  • 1
    JavaScript is single-threaded. You can't stop an infinite loop with an asynchronous event, because the event is blocked until the thread is available. – 4castle Nov 18 '16 at 04:53
  • Ah, I read the answers on this too quickly: http://stackoverflow.com/a/22125915/148668 – Alec Jacobson Nov 18 '16 at 04:59
  • 1
    Exactly, so rather than setting a flag in the callback, put the statements you want to execute in the callback. – 4castle Nov 18 '16 at 05:00
  • I agree now that the `while(flag==false)` is wrong, but the phantomjs sandbox seems to be making it hard to call `console.log(page.content); phantom.exit(); ` in the write _context_ (if that's the right term here). – Alec Jacobson Nov 18 '16 at 05:05
  • 1
    Not a direct answer but we created [mathjax-node](https://github.com/mathjax/mathjax-node) to make server-side processing easy. – Peter Krautzberger Nov 18 '16 at 08:08
  • 1
    @Peter Krautzberger, That totally solved what I was trying to do. This question is a bit moot now (though I'm still curious why it fails). – Alec Jacobson Nov 18 '16 at 14:22

1 Answers1

1

Try the following:

var page = require('webpage').create();
var system = require('system');                                                          
var fs = require('fs');
page.open(system.args[1], function () {
  page.evaluate(function () {
    MathJax.Hub.Queue(
      ["Typeset",MathJax.Hub],
      function () {
        console.log(page.content);
        phantom.exit();
      }
    );
  });
});

This will queue the console output and phantom.exit() calls to occur immediately after the typesetting occurs. I haven't tested the code, but this is the way to synchronize something with MathJax's process.


UPDATE

Try this:

var page = require('webpage').create();
var system = require('system');                                                          
var fs = require('fs');
page.open(system.args[1], function () {
  page.onAlert = function (msg) {
    if (msg === "MathJax Done") {
      console.log(page.content);
    } else if (msg === "MathJax Timeout") {
      console.log("Timed out waiting for MathJax");
    } else {console.log(msg)}
    phantom.exit();
  };
  page.evaluate(function () {
    MathJax.Hub.Queue(
      ["Typeset",MathJax.Hub],
      [alert,'MathJax Done']
    );
    setTimeout(function () {alert("MathJax Timeout")},10000);  // timeout after 10 seconds
  });
});
Davide Cervone
  • 11,211
  • 1
  • 28
  • 48
  • Seems like the sandbox is foiling this. I get `ReferenceError: Can't find variable: page`. – Alec Jacobson Nov 21 '16 at 00:03
  • I have updated my answer with a new version to try. You might also look at [code I provided](https://groups.google.com/forum/#!msg/mathjax-users/nQXVaFi4IKQ/AwZ-UrRhiDAJ) years ago for producing an SVG from MathJax via phantom.js (that has since been superseded by mathjax-node, as Peter suggested). I cribbed the code above from that earlier code example. – Davide Cervone Nov 21 '16 at 00:59
  • The updated code works. That's quite a workaround, I wouldn't have thought to use alert that way. – Alec Jacobson Nov 22 '16 at 21:07