MathJax icon indicating copy to clipboard operation
MathJax copied to clipboard

ui/lazy and printing

Open jirilebl opened this issue 4 years ago • 1 comments

I was experimenting with the very neat ui/lazy extension but I found one issue. When the user prints the page, it is printed in the state it is currently in, not with all the math processed. E.g., the user navigates to page, does not scroll down, and hits print. Then what's printed has math typeset only in the bit that is visible on screen.

The expected behavior would be to typeset all the math before doing a print.

I am using Chrome 91 on Linux if that makes any difference.

jirilebl avatar Jun 18 '21 07:06 jirilebl

Thanks for trying out the lazy typesetting, and for the issue report. You are correct, printing doesn't work as expected; obviously, I didn't consider this situation.

It is possible to use a beforeprint event handler to typeset the rest of the page before the printing occurs, as I describe below. Unfortunately, not all browser implement that, but for the others, you use a matchMedia('print') event handler, so it looks like we can cover all the bases.

Here is a configuration that should do the trick:

MathJax = {
  loader: {
    load: ['ui/lazy'],
  },
  startup: {
    ready() {
      //
      // Get MathJax objects.
      //
      const {STATE} = MathJax._.core.MathItem;
      const {mathjax} = MathJax._.mathjax;
      
      //
      //  Make sure we only run once.
      //
      let complete = false;

      //
      // A function to typeset any remaining equations.
      //
      const typesetAll = (event) => {
        //
        // Only run once.
        //
        if (complete) return;
        complete = true;
        //
        // The MathDocument in use.
        //
        const doc = MathJax.startup.document;
        //
        // The state we need to go back to (COMPILED or TYPESET).
        //
        let state = STATE.LAST;
        //
        // Loop through all the math...
        //   
        for (const math of doc.math) {
          //
          // If it is not laxy compile or typeset, skip it.
          //
          if (!math.lazyCompile && !math.lazyTypeset) continue;
          //
          // Mark the state that we need to start at.
          //
          if (math.lazyCompile) {
            math.state(STATE.COMPILED - 1);
            state = STATE.COMPILED;
          } else {
            math.state(STATE.TYPESET - 1);
            if (STATE.TYPESET < state) state = STATE.TYPESET;
          }
          //
          // Mark it as not lazy and remove it from the observer.
          //
          math.lazyCompile = math.lazyTypeset = false;
          math.lazyMarker && doc.lazyObserver.unobserve(math.lazyMarker);
        }
        //
        // If something needs updating
        //
        if (state !== STATE.LAST) {
          //
          // Reset the document state to the starting state that we need.
          //
          doc.state(state - 1, null);
          //
          // Save the SVG font cache and set it to "none" temporarily 
          //   (needed by Firefox, which doesn't seem to process the 
          //    xlinks otherwise).
          //
          const fontCache = doc.outputJax.options.fontCache;
          if (fontCache) doc.outputJax.options.fontCache = 'none';
          //
          //  Typeset the math and put back the font cache when done.
          //
          MathJax.typesetPromise().then(() => {
            if (fontCache) doc.outputJax.options.fontCache = fontCache;
          });
        }
      };
      
      //
      // Install the print listeners.
      //
      window.matchMedia('print').addListener(typesetAll);  // for Safari
      window.addEventListener('beforeprint', typesetAll);  // for everyone else

      //
      // Do the regular startup.
      //
      MathJax.startup.defaultReady();
    }
  }

};

This should work, provided that the remaining mathematics to be typeset does not include any autoloaded extensions, as the handler has to run synchronously, and loading extensions is asynchronous. So you might want to use one of the components that includes all the extensions (they end in -full), or include any extensions that you expect to use in the loader section.

If there is a log of mathematics that has not yet been typeset, it may take some time to drop the typesetting, and that will either mean that the print dialog may take time to appear or prepare its preview, so some patience may be needed.

I've tested with Chrome, Firefox, and Safari. Since Edge is now based on blink (Chrome's engine) I expect it will work as well.

dpvc avatar Jun 18 '21 18:06 dpvc