Hugo中优雅地使用数学公式

Hugo中优雅地使用数学公式

2024.2 更新:hugo已在v0.122.0正式支持公式语法

参考hugo官方文档Mathematics in markdown,原理是通过识别公式块符号,然后将其内容直接传递到Html而不渲染。配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[markup]
  [markup.goldmark]
    [markup.goldmark.extensions]
      [markup.goldmark.extensions.passthrough]
        enable = true
        [markup.goldmark.extensions.passthrough.delimiters]
          block = [['\[', '\]'], ['$$', '$$']]
          inline = [['\(', '\)'],['$', '$']]
[params]
  math = true

可以配置自己喜欢的直通块符号。注意['$', '$']内联符号使用时要额外处理数学环境外的转义问题

上面的配置、JavaScript 和示例使用 \(...\) 分隔符对来表示内联方程。 $...$ 分隔符对是一种常见的替代方案,但如果您在数学上下文之外使用 $ 符号,则使用它可能会导致意外的格式设置。

如果将 $...$ 分隔符对添加到配置和 JavaScript,则在数学上下文之外时必须对 $ 进行双重转义,无论页面上是否启用了数学渲染。例如:

1
A \\$5 bill _saved_ is a \\$5 bill _earned_.

以下内容已过时,仅作存档

hugo中的数学渲染问题

hugo内置的goldmark渲染器并不能识别直接识别$[行内公式]$$$[公式块]$$语法的数学公式,所以在遇到公式时会将其中\\、\_之类的符号仍然按照markdown的的转义语法渲染,导致\\在输出中变为\。当用户或者主题需要渲染公式而引入mathjax或者katex等库时,会因此无法正确识别换行等语法导致公式排版混乱,或者直接报错。

在历史上,hugo使用的内置markdown渲染器经历了mmark、blackfriday、goldmark的变化。其中只有mmark支持直接识别公式,但目前hugo官方只支持goldmark渲染器(另外还有pandoc,这个后续会提到),前两种都已经被弃用。

自mmark被弃用起,公式渲染这一问题已经在hugo的社区和issue中讨论了很长时间,但目前hugo官方并没有解决的打算。一个原因是Hugo 的贡献要求中具有一条 “no cgo rule”, 而实现数学功能的goldmark插件大多都依赖cgo特性来调用其他JS引擎,如 goldmark-qjs-katex

经过一番查阅与尝试,目前想要在hugo中正确渲染数学公式,有如下几种解决方案:

方法效果侵入性优雅程度配置复杂性
shortcode需要修改md源文件麻烦简单
pandoc可能影响主题一般一般
自编译hugo方便折腾

其中shortcode需要修改每个markdown文件,不够优雅;pandoc兼容性较差,无法同时使用完整的主题功能。而自编译hugo的方式在折腾编译时可能较为麻烦,但编译完成之后就可以直接支持数学公式。对于其他使用者,也能下载编译好的支持数学公式的程序直接使用。

方法一:使用shortcode

部分主题提供了能够保持内容不受转义的shortcode,例如LoveIt/FitIt提供的的

……
shortcode,将公式块包围在这个shortcode之间就可以避免被渲染器转义。

如果主题并没有提供类似的shortcode,可以自己创建。参考hugo关于创建shortcode的文档,创建layouts/shortcodes/keepit.html并写入如下内容:

1
{{- .Inner | safeHTML -}}

然后可以在markdown中将公式使用上述shortcode包围:

1
2
3
4
5
{{<keepit>}}
$$
E=mc^2
$$
{{</keepit>}}

这样公式就不会被转义,能够正常被mathjax/katex解析。

使用shortcode也是目前最方便,应用最广的方法。

但是这种方法在markdown源文件中额外引入了shortcode这一非原生语法,在使用typora等其他markdown编辑器查看和编辑时视觉体验较差。

方法二:使用pandoc作为md渲染器

根据hugo的内容格式文档,当源文件扩展名为.pdc时,hugo会直接调用pandoc进行解析和渲染。但通过一些配置也可以使用pandoc直接渲染md文件。

配置hugo使用pandoc

要使用pandoc,首先请安装pandoc。由于hugo的安全设计,安装pandoc之后还要在配置中将pandoc加入允许执行的外部程序白名单(前面几项是hugo的默认值):

1
2
[security.exec]
    allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$', '^pandoc$']

接下来有两种方式可以使用pandoc。

第一种:在markdown文件的yaml front matter添加markup: pdc,或者将xxx.md重命名为xxx.pdc。这样hugo就会单独使用pandoc渲染这一篇文章。但这种方式需要修改md源文件,因此如果只有少量文章需要使用数学公式,可以使用这一方法。

第二种:将pandoc作为默认markdown渲染器。在配置中添加:

1
2
[markup]
	defaultMarkdownHandler = "pandoc"

这样hugo就会调用pandoc作为默认markdown渲染器。而pandoc本身就可以识别数学公式,因此输出时不会存在goldmark的转义问题。

pandoc输出的兼容性问题

pandoc输出的HTML页面中元素的组织结构和属性与goldmark不同。

由于hugo使用goldmark已经有很长一段时间,许多主题利用仅受goldmark支持的hugo render hook来实现某些效果,或是在theme.js中有许多操作依赖goldmark生成的HTML元素属性。

这就造成使用pandoc生成的页面中,**部分主题的某些效果和组件可能会失效。**其中较为常见的是输出的代码块无法高亮,或者目录消失等。

方法三、自编译支持数学插件的hugo

开箱即用

在介绍手动修改步骤前,我将修改过程编写为了自动化脚本上传到了github,并且配置了github action以在hugo发布新版本时自动编译生成支持数学公式的构建版本。

因此如果你只需要一个Hugo的数学公式解决方案,可以在此处直接下载现成的hugo_katex可执行文件,并跳转使用方式章节。

goldmark的数学插件

实际上,goldmark只是本体不支持识别数学公式,其本身支持插件系统。在goldmark的github仓库页面,可以找到如下能够提供数学功能的插件:

然而hugo使用的goldmark是作为go模块静态编译到hugo内,只包含本体不能增加插件。hugo官方由于“no cgo rule”,也没有将数学支持插件一同打包的打算。

幸运的是,hugo是开源的,我们可以自己编译一份加入了goldmark数学插件的hugo版本。在hugo的PR/fork中,已经有用户进行了相关尝试:

经过测试,上述fork维护时间已经较为久远,已经behind官方repo很多个commit。为方便起见我们可以按相同方式自己修改并编译一份。

手动修改步骤

下面是修改过程记录。

由于我完全不会go语言,所以记录得详细一点。下面的内容以引入goldmark-qjs-katex为例。

参考hugo的contributing.md,可以按如下步骤进行(操作环境为linux但理论上通用):

  1. 首先安装golang开发环境。

  2. 找一个干净的文件夹,clone hugo的仓库。

    1
    
    git clone https://github.com/gohugoio/hugo
  3. 然后安装mage,这个命令会把mage安装到~/go/bin/mage

    1
    
    go install github.com/magefile/mage
  4. cd到hugo的仓库里,切换到stable分支。这是由于master分支还有很多不稳定的feature。当然,你也可以选择一个release tag。

    1
    2
    
    cd hugo
    git checkout stable
  5. 添加goldmark-qjs-katex依赖:

    1
    
    go get github.com/graemephi/goldmark-qjs-katex

    这个命令会更新go.mod和go.sum

  6. 添加执行代码,找到markup/goldmark/convert.go,首先在开头的import添加导入:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    package goldmark
    
    import (
    	"bytes"
    	……//此处省略多行
    	"github.com/yuin/goldmark/renderer/html"
    	"github.com/yuin/goldmark/text"
    	//添加下面这行
    	qjskatex "github.com/graemephi/goldmark-qjs-katex"
    )

    然后在中间的代码中(约134行)将qjskatex添加到插件列表:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    if cfg.Extensions.Footnote {
        extensions = append(extensions, extension.Footnote)
    }
    //下面的是插入内容
    if cfg.Extensions.KaTeX {
        extensions = append(extensions, &qjskatex.Extension{})
    }
    //插入内容结束
    if cfg.Parser.AutoHeadingID {
        parserOptions = append(parserOptions, parser.WithAutoHeadingID())
    }
  7. 增加一个hugo配置项,找到markup/goldmark/goldmark_config/config.go,在第33行左右的Default Config中添加一个用于启用katex支持的布尔键。注意末尾的逗号不能省略;以及在第46行的Extensions定义中同样增加一行。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    var Default = Config{
    	Extensions: Extensions{
    		Typographer:     true,
    		Footnote:        true,
    		DefinitionList:  true,
    		Table:           true,
    		Strikethrough:   true,
    		Linkify:         true,
    		LinkifyProtocol: "https",
    		TaskList:        true,
    		KaTeX:           false,//添加了这一行
    	},
        ……
    type Extensions struct {
    	Typographer    Typographer
    	Footnote       bool
    	DefinitionList bool
    
    	// GitHub flavored markdown
    	Table           bool
    	Strikethrough   bool
    	Linkify         bool
    	LinkifyProtocol string
    	TaskList        bool
    	KaTeX           bool//添加了这一行
    }
  8. 最后构建。这里的环境变量是启用extended特性,如果不需要相关特性可以不设置。

    1
    
    HUGO_BUILD_TAGS=extended ~/go/bin/mage hugo

    此时会在当前目录编译生成一份hugo可执行文件。

使用方式

  1. 将前述编译的hugo程序复制到博客所在目录,为便于区分,可以重命名为hugok或者hugo-katex等名字。当然你也可以添加到环境变量以在任何地方使用,这样你也可以卸载默认的hugo并将编译的hugo作为替代。

  2. 在博客的config.toml中,添加站点配置启用katex功能。

    1
    2
    
    [markup.goldmark.extensions]
    	katex = true
  3. katex样式表章节内容,开启主题自带katex支持,或手动引入css

  4. 最后如果是复制到博客目录,可以用如下方式启动演示服务器或者生成。其他参数等与原版hugo完全相同。

    1
    2
    
    ./hugok serve # 演示服务器
    ./hugok       # 生成

katex样式表

goldmark-qjs-katex默认只处理数学公式并输出,不包含katex对应的css,会导致直接使用时页面展示的公式排版较为奇怪。

使用主题提供的katex库(如果有)

如果在使用自行编译的hugo之前,所使用的主题已经支持通过shortcode编写数学公式并使用katex渲染,那么此时只需要在主题配置中启用对应的数学配置即可。

例如,在LoveIt主题中,可设置:

1
2
[page.math]
	enable = true

需要说明的是,由于我们编译的hugo直接支持数学公式,无需使用主题提供的shortcode,这里启用该选项是为了让hugo在生成时引入主题已经包含的katex相关库文件和样式。

自行引入css

如果使用的主题本身不包括katex支持,需要自行在页面中引入katex样式表。可以使用cdn的链接,例如https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css。

大部分主题都提供了引入自定义css的方式,请自行查看主题提供的配置项。

如果主题连添加自定义css的功能都没提供,可以查看这两个教程手动编写模板添加css:

0%