Featured image of post 让Hugo优雅的显示公式

让Hugo优雅的显示公式

解决Hugo下KaTeX转义的问题

继上文《在PowerPoint里优雅的插入LaTeX公式》后,没想到我又遇到了LaTeX相关的问题,这次是我博客无法正确的显示公式了。当然,最简单和粗暴的方法就是直接转成图片,但本着能折腾就折腾的精神,还是研究了一下如何优雅的解决。

# 问题复现

错误的公式渲染

由于在LaTeX语法中,_是用来表示下标,由于下标经常被使用,很容易就会导致一句话中出现两个_。而在Markdown语法里,下划线是表示斜体的意思,于是Hugo就会将其转义成<em>标签,导致公式格式错误,从而使KaTeX无法正确渲染。

hugo转义后的代码

这就很笨了,本地的Markdown编辑器都没有出现这样的问题,怎么到你Hugo这就拉了呢?

Is there any way to ignore underscore when rendering markdown? - support - HUGO (gohugo.io)

有这样问题的人也不止我一个,官方论坛上早在2017年就有人提问了,那么官方的答复是:

喜多可爱捏

没错,官方并没有给出好的解决方案,只有在文档中稍微提到了一嘴,你可以使用pandoc来进行Markdown的转义。1

🤔本来用你就是图你的生成速度快,这调用其他程序后,这速度不就慢了没优势了吗。

没办法,还是自己动手解决吧。

# 解决方案

在搜索过后,网上给出的解决方案是在下划线前面加上\,避免被Hugo进行转义。但是如果在写文档的时候就加上的话,就会导致本地公式无法正确渲染,所以边写边加是肯定不可能了。写完后手动添加,如果是一两个还好,但下划线在LaTeX里实在是太常用了,所以手工修改也不现实。

于是,我便决定写个小程序来自动修改。由于天天Apex大脑退化严重,我实在想不到什么比较好的字符串处理方法,只好用遍历大法。(这既视感,编译原理:……,什么……在想我的事情……?)

不玩梗了,直接上代码:

  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
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

const bool DEBUG = false;

int main(int argc, char* argv[]) {
    string file;
    ifstream fin;
    if (argc <= 1) {
        cout << "请输入文件名称:";
        cin >> file;
    } else {
        file = argv[1];
    }
    fin.open(file);
    if (!fin.is_open()) {
        cout << "无法打开文件" << endl;
        system("pause");
        return 1;
    }
    // 读入文件
    string code((std::istreambuf_iterator<char>(fin)),
                std::istreambuf_iterator<char>());
    fin.close();
    if (DEBUG) {
        cout << code << endl;
        cout << "-----------------" << endl;
    }
    // 所有数学公式的开头和结尾
    vector<pair<int, int>> maths;
    cout << "开始寻找所有数学公式" << endl;
    for (int i = 0; i < code.length(); i++) {
        if (code[i] == '$') {
            i++;
            bool is_double = false;
            int start = i;
            if (code[i] == '$') {
                is_double = true;
                i++;
                start = i;
            }
            while (i < code.length() && code[i] != '$') {
                i++;
            }
            if (is_double) {
                i++;
            }
            if (i >= code.length() || code[i] != '$') {
                cout << "错误:缺少结束符" << endl;
                system("pause");
                return 1;
            }
            int end = i;
            maths.push_back(pair<int, int>(start, end));
            cout << start << " " << end << endl;
            if (DEBUG) {
                string math = code.substr(start, end - start);
                cout << math << endl;
            }
        }
    }
    cout << "寻找完成,开始处理" << endl;
    int count = 0;
    for (int i = 0; i < maths.size(); i++) {
        for (int j = maths[i].first + count; j < maths[i].second + count; j++) {
            if (code[j] == '_') {
                code.insert(j, "\\");
                count++;
                j++;
            } else if (code[j] == '\\') {
                j++;
                if (code[j] == '\\') {
                    code.insert(j, "\\\\");
                    count += 2;
                    j += 2;
                } else if (code[j] == '{' || code[j] == '}') {
                    code.insert(j, "\\");
                    count++;
                    j++;
                }
            }
        }
    }
    if (DEBUG) {
        cout << code << endl;
    }
    cout << "处理完成,开始写入文件" << endl;
    ofstream fout;
    string location = file.substr(0, file.find_last_of('\\') + 1);
    fout.open(location + "index.md");
    if (!fout.is_open()) {
        cout << "无法打开文件" << endl;
        system("pause");
        return 1;
    }
    fout << code;
    return 0;
}

由于只是随手写的小工具,所以也没有分函数啥的,直接全放一起了。

这个程序会匹配$...$$$...$$这两种格式,将其中所有的_替换为\_,所有的\\替换为\\\\,同时将文件在原位置保存为index.md

这样每次写好文章,只要将其拖到该程序上就能自动转换完成了。

这样就能确保本地查看编辑和网页都可以正确显示公式了。缺点则是每个有公式的文件都会多保存一份,不过文本文件并不是很大,所以也无伤大雅。

当然,这个程序还有优化的空间,比如自动对含有公式的文章进行处理等等。不过看这我文章更新的速度🕊️,手动拖一下还是可以接受的。

# 更新记录

2023.7.20:

  • 增加对\{\}的处理

2023.1.3:

  • 增加对\\的处理