Gollum MathJax 精简内置指南:离线渲染 LaTeX 公式的原理与更新流程

原创2026-10-13 17:19:111,904 阅读
文章标签:文档知识库后端

Gollum MathJax 精简内置指南:离线渲染 LaTeX 公式的原理与更新流程

Gollum 将一份“精简版(SLIM)”的 MathJax 直接打包进仓库,使 Wiki 在完全离线的环境下也能渲染 LaTeX 公式,同时避免 MathJax 的完整发布包撑大 gollum 的 release tarball。本文基于仓库内的维护文档 HOWTO_UPDATE_MATHJAX.md,完整讲解 MathJax 在 gollum 中的加载链路与配置方式,并给出可复制、可运行的 MathJax 版本更新操作步骤,读完你可以独立完成 MathJax 的升级替换,并理解每一步命令背后的工程原因。

一、为什么 gollum 要自己内置一份精简的 MathJax

MathJax 的完整发布物体积很大,包含大量 gollum 用不到的组合组件(combined components)。gollum 的诉求很明确(引自 HOWTO_UPDATE_MATHJAX.md):

Our intention is to bundle a SLIM MathJax in gollum, so that gollum can work offline with latex equations without bloating the gollum release tarball. We do this by removing the combined component files that we don't need.

也就是说,两个目标同时满足:

  • 离线可用:不依赖 CDN,MathJax 文件全部随 gollum 一同分发;
  • 体积可控:只保留真正被引用的组合入口文件 tex-mml-chtml.js 和 tex-chtml-full-speech.js(后者用于无障碍/accessibility 场景),删掉其余组合组件文件。

二、MathJax 在 gollum 中的加载链路(源码佐证)

要理解更新操作为什么必须保留那两个文件,先看 gollum 是如何引用 MathJax 的。

1. 本地静态资源路由

lib/gollum/app.rb 在 before 钩子中确定了 MathJax 的根目录:

@mathjax_path = ::File.join(File.dirname(__FILE__), 'public/gollum/javascript/MathJax')

随后 lib/gollum/app.rb 注册了一条路由,把 URL 前缀 /gollum/assets/mathjax 剥离后,直接以该目录为 root 用 Rack::Static 返回文件:

get '/assets/mathjax/*' do
  env['PATH_INFO'].sub!('/gollum/assets/mathjax', '')
  Rack::Static.new(not_found_proc, {:root => @mathjax_path, :urls => ['']}).call(env)
end

这就是“离线渲染”的实现基础:浏览器请求的每个 MathJax 文件都来自仓库目录 lib/gollum/public/gollum/javascript/MathJax/,而非外部网络。

2. 入口文件的选择

页面模板实际加载的 MathJax 入口,由 lib/gollum/views/layout.rb 给出:

def mathjax_js
  "#{page_route('gollum/assets/mathjax/tex-mml-chtml.js')}"
end

这与维护文档的说法完全一致:页面只需要 tex-mml-chtml.js 这一个组合入口(TeX/MathML 输入 → CHTML 输出)。

3. 启用条件与渲染器开关

MathJax 是否加载由 --math 配置决定。lib/gollum/views/has_math.rb 定义了视图辅助方法:

def mathjax
  @math == :mathjax
end

def katex
  @math == :katex
end

def math_config
  @math_config ? page_route(@math_config) : false
end

其中 @math 与 @math_config 分别来自 lib/gollum/app.rb:

@math = settings.wiki_options[:math]
@math_config = settings.wiki_options.fetch(:math_config, false)

README.md 对 --math 参数的说明是:

Enable rendering of mathematical equations. Valid renderers: mathjax, katex. Default: katex. Add custom configuration for the renderer to math.config.js and commit it to the repo.

即 mathjax 与 katex 二选一,默认走 KaTeX;选择 MathJax 后,页面头部才会注入 MathJax 配置块并异步加载入口脚本。

4. 页面内的 MathJax 配置块

当 {{#mathjax}} 分支生效时,lib/gollum/templates/layout.mustache 会注入完整的运行时配置:

<script type="text/javascript">
  window.MathJax = {
    tex: {
      inlineMath: [ ['$','$'], ['\\(','\\)'] ],
      displayMath: [ ['$$','$$'], ['\\[','\\]'] ],
      processEscapes: true,
      autoload: {
        color: [],
        colorv2: ['color']
      },
      packages: {'[+]': ['noerrors']}
    },
    options: {
      ignoreHtmlClass: 'tex2jax_ignore',
      processHtmlClass: 'tex2jax_process',
      safeOptions: {
        allow: {
          URLs:    'safe',
          classes: 'none',
          cssIDs:  'none',
          styles:  'none',
        },
      },
    },
    loader: {
      load: ['[tex]/noerrors', 'ui/safe']
    },
  };
</script>

关键点:

  • inlineMath / displayMath 定义了 $...$、\(...\)、$$...$$、\[...\] 四种定界符;
  • loader.load 显式要求加载 <a href="https://link.gitcode.com/i/ed0ccf9ad36cca8683c2601f88c1ad69" target="_blank">tex]/noerrors 与 ui/safe 两个模块,对应目录下的 [noerrors 扩展 与 ui/safe.js。更新 MathJax 后如果这些模块路径变化,该配置会失配,这也是升级时需要注意的兼容性点;
  • safeOptions.allow 收紧了 URL/类名/样式注入,配合 ui/safe 形成安全防护。

入口脚本的注入位置在 lib/gollum/templates/layout.mustache:

{{#mathjax}}
<script async src="{{mathjax_js}}"></script>
{{/mathjax}}

此外,如果 Wiki 仓库中提交了自定义配置(--math-config / math.config.js),lib/gollum/templates/layout.mustache 会先于 MathJax 入口脚本注入该自定义脚本,用户可在其中覆盖 window.MathJax 的任意字段。

5. 编辑器预览也依赖 MathJax

不只是页面渲染,实时预览同样会调用 MathJax API 重新排版公式,见 lib/gollum/public/gollum/javascript/gollum.js.erb:

if (window.MathJax != null) {
  MathJax.texReset();
  MathJax.typesetClear();
  MathJax.typesetPromise([previewContent]);
}

这解释了为什么 MathJax 必须以“完整可用”的形式内置:升级或删错文件时,受损的不只是页面渲染,还有编辑器的公式实时预览。

三、MathJax 更新操作步骤(完整继承自维护文档)

以下流程引自 HOWTO_UPDATE_MATHJAX.md,所有命令均要求 在 gollum 仓库根目录下执行。From the root of the gollum repository:

第 1 步:克隆最新的 MathJax

git clone https://github.com/mathjax/MathJax.git /tmp/MathJax

第 2 步:删除旧版 MathJax

git rm -r lib/gollum/public/gollum/javascript/MathJax/*

第 3 步:复制新的 ES5 构建产物

cp -r /tmp/MathJax/es5/* lib/gollum/public/gollum/javascript/MathJax/

第 4 步:删除不需要的组合组件文件,共 7 个(相对仓库根目录):

  • lib/gollum/public/gollum/javascript/MathJax/mml-chtml.js
  • lib/gollum/public/gollum/javascript/MathJax/mml-svg.js
  • lib/gollum/public/gollum/javascript/MathJax/tex-chtml-full.js
  • lib/gollum/public/gollum/javascript/MathJax/tex-chtml.js
  • lib/gollum/public/gollum/javascript/MathJax/tex-mml-svg.js
  • lib/gollum/public/gollum/javascript/MathJax/tex-svg-full.js
  • lib/gollum/public/gollum/javascript/MathJax/tex-svg.js

第 5 步:将新文件加入暂存区

git add lib/gollum/public/gollum/javascript/MathJax

文档还提供了一个把第 4 步合并成单条命令的 one-liner(同样从仓库根目录执行):

git rm lib/gollum/public/gollum/javascript/MathJax/mml-chtml.js lib/gollum/public/gollum/javascript/MathJax/mml-svg.js lib/gollum/public/gollum/javascript/MathJax/tex-chtml-full.js lib/gollum/public/gollum/javascript/MathJax/tex-chtml.js lib/gollum/public/gollum/javascript/MathJax/tex-mml-svg.js lib/gollum/public/gollum/javascript/MathJax/tex-svg-full.js lib/gollum/public/gollum/javascript/MathJax/tex-svg.js

保留与删除的逻辑:为什么是这两个文件

对照当前仓库实际的 MathJax/ 目录,可以看到文档所述流程已经被执行过:目录顶层只有 tex-mml-chtml.js 与 tex-chtml-full-speech.js 两个组合入口,7 个被删除的组合文件均不存在。其分工是:

  • tex-mml-chtml.js:页面实际加载的入口(TeX + MathML 输入 → CHTML 输出),由上文 layout.rb 的 mathjax_js 指定;
  • tex-chtml-full-speech.js:带语音朗读能力的完整组合(full + speech),服务于 a11y/ 无障碍模块——这也是为什么仓库中保留了 MathJax/a11y/ 与 MathJax/sre/mathmaps/(各语言数学语音朗读映射)等目录。

gemspec 同步:更新后必须核对的文件清单

一个容易遗漏的细节:gollum 以 gem 形式分发时,打包的文件清单显式列在 gollum.gemspec 中,其中逐条列出了当前精简后的 MathJax 文件(如 MathJax/tex-mml-chtml.js、MathJax/tex-chtml-full-speech.js、MathJax/a11y/*.js、MathJax/sre/mathmaps/*.json、woff 字体等)。从源码结构看,更新 MathJax 大版本时,若新增/移除了目录结构,还需要核对 gemspec 中的清单是否与新目录一致,否则 gem 发布包会缺失或引用旧文件。

四、更新后的验证与注意事项

结合上文链路,升级完成后建议做如下验证:

  1. 检查目录顶层:lib/gollum/public/gollum/javascript/MathJax/ 下应只有 tex-mml-chtml.js 与 tex-chtml-full-speech.js 两个组合入口,7 个被删文件不得出现;
  2. 核对 loader 依赖:确认 input/tex/extensions/noerrors.js 与 ui/safe.js 路径存在,保证 layout.mustache 中 loader.load: ['[tex]/noerrors', 'ui/safe'] 可正常解析;
  3. 启动服务验证:以 --math mathjax 启动 gollum(见 README.md 中 --math 参数说明),访问含 $$...$$ 公式的页面,确认公式渲染正常、编辑器实时预览(MathJax.typesetPromise,见 gollum.js.erb)可用;
  4. 确认路由命中:直接访问 /gollum/assets/mathjax/tex-mml-chtml.js 应返回 200,由 app.rb 的 Rack::Static 路由从本地目录提供,全程无外网请求。

五、小结

gollum 对 MathJax 的处理是一条清晰而克制的工程链路:本地内置(离线)→ 精简组合入口(控体积)→ 本地静态路由分发(/assets/mathjax/*)→ 模板内最小化配置(--math mathjax + 可选 math.config.js)。更新 MathJax 的核心动作即“克隆 → 替换 es5/ 产物 → 删掉 7 个无用组合文件 → 重新暂存”,同时保持 tex-mml-chtml.js 与 tex-chtml-full-speech.js 这两个关键入口不被误删,并留意 gollum.gemspec 中文件清单与新目录结构保持一致,即可安全地完成一次 MathJax 版本升级。

登录后查看全文
gollum