在用GitBook整理笔记的过程中,因GitBook年久失修,碰到了很多问题。

安装 gitbook-cli Link to heading

使用GitBook需要先安装 node.js,之后安装 gitbook-cli

$ npm install -g gitbook-cli
$ npm list -g
...
└── gitbook-cli@2.3.2

目录结构及测试文件内容如下:

$ ls -1
book.json
README.md
SUMMARY.md

$ cat book.json
{
    "gitbook": "3.x.x",
    "title": "测试",
    "author": "fkon",
    "description": "测试",
    "language": "zh-hans"
}


$ cat README.md
# README

Hello!

$ cat SUMMARY.md
# Summary

* [README](README.md)

使用 gitbook serve 在本地测试书籍,结果报错:

$ gitbook serve
Live reload server started on port: 35729
Press CTRL+C to quit ...

C:\Users\user\AppData\Roaming\npm\node_modules\gitbook-cli\node_modules\npm\node_modules\graceful-fs\polyfills.js:287
      if (cb) cb.apply(this, arguments)
                 ^

TypeError: cb.apply is not a function
    at C:\Users\user\AppData\Roaming\npm\node_modules\gitbook-cli\node_modules\npm\node_modules\graceful-fs\polyfills.js:287:18
    at FSReqCallback.oncomplete (fs.js:169:5)

原因是 gitbook-cli 依赖旧版本的 graceful-fs,其中含有bug。解决办法是找到上述 gitbook-cli 的安装路径,升级 graceful-fs4.2.0。重新运行 gitbook serve,在浏览器中打开 localhost:4000,GitBook已成功运行:

$ cd C:\\Users\\user\\AppData\\Roaming\\npm\\node_modules\\gitbook-cli\\node_modules\\npm\\node_modules
$ npm install graceful-fs@4.2.0 --save
$ cd -
$ gitbook serve
Starting server ...
Serving book on http://localhost:4000

使用MathJax数学插件 Link to heading

MathJax和Katex是两个Latex数学公式渲染引擎,差异主要有:

  • MathJax支持的Latex符号更多,渲染速度慢,用于生成pdf时清晰度不够;
  • Katex支持的Latex符号不如MathJax多,渲染速度快,生成pdf时清晰度很高;

因此,在GitBook中应首先尝试Katex,其次MathJax。

作为示例,修改 README.md 内容如下:

# README

Batch Normalization output $y_i$:

$$
\mu_B \leftarrow \frac{1}{m}\sum_{i = 1}^{m}x_i \newline
\sigma_B^2 \leftarrow \frac{1}{m} \sum_{i=1}^{m}(x_i - \mu_B)^2 \newline
\hat{x_i} \leftarrow \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \newline
y_i \leftarrow \gamma \hat{x_i} + \beta \equiv \text{BN}_{\gamma,\beta}(x_i) \newline
$$

同时把 "plugins": ["mathjax"] 添加到 book.json 中,随后使用 gitbook install 安装插件。遇到安装错误,改用 npm 安装插件,运行 gitbook serve,再次报错:

$ gitbook install
info: install plugin "mathjax" (*) from NPM with version 1.1.2
C:\Users\user\.gitbook\versions\3.2.3\node_modules\npm\node_modules\aproba\index.js:25
    if (args[ii] == null) throw missingRequiredArg(ii)
                          ^

$ npm install gitbook-plugin-mathjax

$ gitbook serve
info: loading plugin "mathjax"... ERROR

PluginError: Error with plugin "mathjax": Unexpected token '<<'

查看 mathjax 插件源码发现竟然存在未解决的冲突,先手动解决冲突:

--- node_modules/gitbook-plugin-mathjax/index.js  2021-09-19 19:09:11.486548700 +0800
+++ node_modules/gitbook-plugin-mathjax/index.js  2021-09-19 19:09:22.438413500 +0800
@@ -1,10 +1,3 @@
-<<<<<<< HEAD
-var Promise = require('q');
-var crc     = require('crc');
-var mjAPI   = require('mathjax-node/lib/mj-single.js');
-
-var started   = false;
-=======
 var Q = require('q');
 var fs = require('fs');
 var path = require('path');
@@ -13,7 +6,6 @@
 var mjAPI = require('mathjax-node/lib/mj-single.js');

 var started = false;
->>>>>>> master
 var countMath = 0;
 var cache     = {};

再次尝试 gitbook serve, 继续报错:

$ gitbook serve
info: loading plugin "mathjax"... ERROR

PluginError: Error with plugin "mathjax": Cannot find module 'mathjax/unpacked/MathJax'

查看MathJax版本,发现是 3.2.0,降级到 2.7.7 后上面错误消失,新的错误出现:

$ npm install mathjax@2.7.7
$ gitbook serve
info: loading plugin "mathjax"... ERROR

Error: GitBook doesn't satisfy the requirements of this plugin: >=4.0.0-alpha

GitBook的MathJax插件中要求GitBook的engine版本高于 4.0.0-alpha,先尝试降低要求到 3.0.0

--- node_modules/gitbook-plugin-mathjax/package.json  2021-09-19 19:25:28.224953200 +0800
+++ node_modules/gitbook-plugin-mathjax/package.json  2021-09-19 19:25:46.678249600 +0800
@@ -6,7 +6,7 @@
     "icon": "./icon.png",
     "version": "2.0.0",
     "engines": {
-        "gitbook": ">=4.0.0-alpha"
+        "gitbook": ">=3.0.0"
     },
     "dependencies": {
         "crc": "^3.2.1",

再次报错:

$ gitbook serve
error: error while generating page "README.md":

Template render error: (C:\Users\user\Desktop\exp\README.md)
  TypeError: Cannot read property '0' of undefined

好吧,错误没完没了。官方的MathJax插件已经失修太久,改为第三方插件,比如 mathjax-pro。在 book.json 中修改插件为 "plugins": ["mathjax-pro"],然后安装插件:

$ gitbook install
$ gitbook serve

终于成功运行,然而打开网页一看,示例中的两个公式都没有成功渲染。

前一个是行内公式,查看MathJax插件源代码 plugin-mathjax/blob/master/index.js#L134,发现是 inline/display math,分界符都是双 $$。因此,使用此插件时需要注意任何公式都要用 $$ 作为公式分界符,单纯修改插件代码添加单个 $ 作为inline math的解决方案存在bug。

第二个公式是display math,原来MathJax对多行公式要求严格,需要放在 aligned 环境中,而Katex则不需要,修改后的 README.md 内容:

# README

Batch Normalization output $$y_i$$:

$$
\begin{aligned}
\mu_B \leftarrow \frac{1}{m}\sum_{i = 1}^{m}x_i \newline
\sigma_B^2 \leftarrow \frac{1}{m} \sum_{i=1}^{m}(x_i - \mu_B)^2 \newline
\hat{x_i} \leftarrow \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \newline
y_i \leftarrow \gamma \hat{x_i} + \beta \equiv \text{BN}_{\gamma,\beta}(x_i) \newline
\end{aligned}
$$

此时两个公式都可以正常显示,但第二个公式却是左对齐,通常情况下display math都是居中显示,因此还存在bug。查看插件源代码,发现判断是否是inline的逻辑是 var isInline = !(tex[0] == "\n");,display math是以换行符开头的,但在Windows上换行符是 \r\n,所以这个判断逻辑对以 \r\n 换行的Markdown文件失效。一个临时的修复方法是判断公式文本中是否包含 \n,对于绝大多数情况下这样做没有问题:

--- node_modules/gitbook-plugin-mathjax-pro/index.js    2021-09-19 20:06:15.709172100 +0800
+++ node_modules/gitbook-plugin-mathjax-pro/index.js    2021-09-19 20:06:43.695891500 +0800
@@ -72,7 +72,7 @@
 function processBlock(blk) {
     var book = this;
     var tex = blk.body;
-    var isInline = !(tex[0] == "\n");
+    var isInline = !tex.includes("\n");

     // For website return as script
     var config = book.config.get('pluginsConfig.mathjax-pro', {});

至此,GitBook终于能够用MathJax渲染数学公式了。

使用Katex数学插件 Link to heading

虽然MathJax功能强大,但是出于渲染速度和生成pdf文档时清晰度的考虑,还是希望用Katex。在 book.json 中把插件改为 katex,即 "plugins": ["katex"],然后安装Katex插件:

$ gitbook install
$ gitbook serve
error: error while generating page "README.md":

Template render error: (C:\Users\user\Desktop\exp\README.md)
  ParseError: KaTeX parse error: Expected & or \\ or \end at position 68: …{i = 1}^{m}x_i n̲e̲w̲l̲i̲n̲e̲
\sigma_B^2 \l

查看Katex版本,发现是 0.7.1,非常古老。第三方插件 katex-pp 已经修复了这个问题,修改 book.json 后安装插件:

$ gitbook install
$ gitbook serve

运行成功,但是4行道公式挤在了1行。使用Katex时以上公式并不需要 alinged 环境,移除后即可。重新运行后display公式也是左对齐,这个MathJax的问题一样,插件没有正确区分inline/display公式,主要原因是换行符所致,按下面的path修改插件源代码后,Katex正常渲染数学公式。

--- node_modules/gitbook-plugin-katex-pp/index.js  2021-09-19 20:50:13.948081800 +0800
+++ node_modules/gitbook-plugin-katex-pp/index.js  2021-09-19 20:51:10.518884100 +0800
@@ -23,9 +23,9 @@
             },
             process: function(blk) {
                 var tex = blk.body;
-                var isInline = !(tex[0] == "\n");
+                var isDisplay = tex.includes("\n");
                 var output = katex.renderToString(tex, {
-                    displayMode: !isInline,
+                    displayMode: isDisplay,
                     leqno: false,
                     output: "htmlAndMathml"
                 });

生成pdf文档 Link to heading

gitbook-cli的一个很重要的功能是生成pdf格式电子书,生成pdf时需要安装 svgexportebook-convert

$ npm install -g svgexport ebook-convert
$ npm list -g
...
├── ebook-convert@2.0.1
├── gitbook-cli@2.3.2
└── svgexport@0.4.2

除此之外,需要在配置生成pdf时的属性,将如下内容添加到 book.json 中:

    "pdf": {
        "pageNumbers": true,
        "fontFamily": "Arial",
        "fontSize": 12,
        "paperSize": "a4",
        "margin": {
            "right": 31,
            "left": 31,
            "top": 28,
            "bottom": 28
        }
    }

生成pdf使用 gitbook pdf,中途报错:

$ gitbook pdf
error: error while generating page "README.md":

Error: Error converting C:\Users\user\AppData\Local\Temp\tmp-3416kGYRf4rnonJy\de680f32.svg into C:\Users\user\AppData\Local\Temp\tmp-3416kGYRf4rnonJy\94bab1f.png

手动调用 svgexport 把SVG转为PNG格式也报错,打开上述SVG文件,发现它并不是一个有效的SVG文件,主要是缺少了 xmlns 属性。作为测试,打开SVG文件并把 <svg ...> 标签改为 <svg xmlns="http://www.w3.org/2000/svg" ...>,再次手动转PNG,成功。

$ gitbook pdf
error: error while generating page "README.md":

Error: Error converting C:\Users\user\AppData\Local\Temp\tmp-3416kGYRf4rnonJy\de680f32.svg into C:\Users\user\AppData\Local\Temp\tmp-3416kGYRf4rnonJy\94bab1f.png

$ svgexport 'C:\Users\user\AppData\Local\Temp\tmp-3416kGYRf4rnonJy\de680f32.svg' 'tmp.png'
Error: Evaluation failed: TypeError: el.getBBox is not a function
    at __puppeteer_evaluation_script__:26:22

$ vim C:/Users/user/AppData/Local/Temp/tmp-3416kGYRf4rnonJy/de680f32.svg
# change <svg ...> to <svg xmlns="http://www.w3.org/2000/svg" ...>

$ svgexport 'C:\Users\user\AppData\Local\Temp\tmp-3416kGYRf4rnonJy\de680f32.svg' 'tmp.png'
... png 100% 1x 0:0:6400:20.47 6400:20.47

可见,问题并不发生在 svgexport,而是因为GitBook在把Katex的输出渲染为DOM时丢失了必要的 xmlns 属性,如 lib/output/modifiers/svgToImg.js#L34。找到GitBook的安装位置,在Windows上通常在 C:\Users\user\.gitbook\versions\3.2.3,修改源代码,为每个SVG文件添加 xmlns 属性:

--- lib/output/modifiers/svgToImg.js    2021-09-19 21:19:43.428873100 +0800
+++ lib/output/modifiers/svgToImg.js    2021-09-19 21:20:05.747750000 +0800
@@ -32,6 +32,10 @@
     var currentDirectory = path.dirname(currentFile);

     return editHTMLElement($, 'svg', function($svg) {
+        for (let i = 0; i < $svg.length; i++) {
+            $svg[i].attribs.xmlns = "http://www.w3.org/2000/svg";
+        }
+
         var content = '<?xml version="1.0" encoding="UTF-8"?>' +
             renderDOM($, $svg);

重新运行 gitbook serve,成功生成pdf文档。

总结 Link to heading

GitBook是一个用Markdown写书的工具,但是由于项目多年没更新,官方用于支持MathJax和Katex的插件都已无法运行,需要使用第三方插件或者自己修正问题。