记录mktmltopdf转换pdf,MathJax公式渲染失败问题

755 阅读2分钟

在html页面渲染MathJax公式,可以选择MathJaxV2MathJaxV3,V2更适合通过cdn引用,本地化比较麻烦,也不能直接把cdn.js复制下来放在本地,因为里面还引了其他资源(字体等),会导致这些资源引用失败。

V3可以资源放在本地,还可以根据项目的需要,下载单独的包,比如tex-chtml及字体库。

 <script>
      window.MathJax = {
        chtml: {
          matchFontHeight: true,
          fontURL: "./formula/woff-v2", // 字体
          adaptiveCSS: true,
          scale: 0.8,
          minScale: .7,                  
        },
        options: {
          renderActions: {
            addMenu: [],
          }
        },
      };
    </script>
    <script type="text/javascript" src="./formula/tex-chtml.js" id="MathJax-script" ></script> //Mathjax tex-chtml

网页渲染Mathjax公式,只要花时间耐心看Mathjax的文档,基本没有问题,有问题网上也能找到比较多参考答案。

这里要说的是后端要用mkhtmltox将html转成pdf时,发现公式渲染不出来的问题。

我们采用的是上面所说V3的方式渲染公式,在浏览器看相应的html公式是可以渲染的,但是转出来的pdf却失败。

渲染失败:

image.png

渲染成功应该是这样

image (1).png

问题出现了,前端如何排查呢?这里先说一下排查方法。

  1. 先去下载安装windows版本的wkhtmltox
  2. <安装路径>\bin\wkhtmltopdf --debug-javascript <要转换的html路径>.html <生成的pdf保存路径>.pdf

--debug-javascript 显示 javascript 调试输出,这是wkhtmltopdf的全局可配置选项. 根据情况可能还需要用到--window-status <> --enable-local-file-access 等,更多选项请看

通过--debug-javascript后出现的日志,发现出现了语法报错原因是wkhtmltopdf 使用的WebKit 引擎版本比较低(wkhtmltopdf 这个工具也不怎么维护了,感觉WebKit 升级遥遥无期),无法解析tex-chtml.js里面的es6语法,同时不支持tex-chtml.js里使用到的localStorage。

首先考虑了再引入polyfill解决es6的编译问题,不支持localStorage就在tex-chtml.js里面写个localStorage,一番折腾下来,终于不报错了。

但是公式还是渲染失败,由于wkhtmltopdf 显示的调试信息太少了,无法继续深入研究。

转而考虑通过V2的cdn引入,Mathjax2官网里面的cdn在国内经常无法访问,wkhtmltopdf报cdn加载失败。

转而求其次,寻找国内大厂cdn,很多国内大厂的cdn源都能找到mathjax2,但是mathjax2里面引用的其他cdn都不全,公式编译还是失败。

最后staticfile Cdn库里的MathJax2 cdn是可用的,后端生成的pdf终于可以渲染出公式了。

感谢Staticfile CDN,感谢七牛云,感谢掘金。

image.png

最后终于渲染成功!

<script type="text/javascript">
  var el = document.createElement("script"); // 不要使用const等es6语法
  el.setAttribute("id", "MathJax-script");
  el.src =
    "https://cdn.staticfile.org/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML";

  document.body.appendChild(el);

  if (el.readyState) {
    //IE
    el.onreadystatechange = function () {
      if (el.readyState === "complete" || el.readyState === "loaded") {
        el.onreadystatechange = null;
        mathjaxConfig();
      }
    };
  } else {
    //非IE
    el.onload = function () {
      mathjaxConfig();
    };
  }

  function mathjaxConfig() {
    if (window.MathJax) {
      window.MathJax.Hub.Config({
        extensions: ["tex2jax.js"],
        jax: ["input/TeX", "output/HTML-CSS"],
        tex2jax: {
          inlineMath: [["\\(", "\\)"]],
          displayMath: [
            ["$$", "$$"],
            ["\\[", "\\]"],
          ],
          processEscapes: true,
        },
        "HTML-CSS": {
          availableFonts: ["TeX"],
          preferredFont: "TeX",
          minScaleAdjust: 100,
        },
      });

      window.MathJax.Hub.Queue([
        "Typeset",
        MathJax.Hub,
        document.getElementById("wrapper"),
        // MathJax把公式都渲染完成后,触发
        function () {
        // 这里是为了配合wkhtmltopdf转换选项--window-status onloadready使用,
        // 加载完html后,等window-status值为onloadready时wkhtmltopdf才会开始转换
          window.status = "onloadready"; 
        },
      ]);
    }
  }
</script>

至于很多的细节,比如一开始pdf渲染出来的公式字体很小等问题,就不一一赘述了。归结起来,这些都是通过花时间阅读MathJax2文档调整各种配置,实现出满意的渲染效果。