我们注意到 Snacks.nvim 中的 image 功能模块以更快的加载速度 和一站式集成的体验胜过了 image.nvim。 而这两者,或者甚至可以包括 LaTeX 在内,对中文的支持始终让人相当头疼。 本文试图给出一种能在公式中使用中文的方案。
结论先行
参考配置如下:
{
"folke/snacks.nvim",
-- 中略
opts = {
-- 中略
image = {
enabled = true,
-- 中略
math = {
enabled = true,
latex = {
packages = {"amsmath", "amssymb", "amsfonts", "amscd", "mathtools",
"fontspec", "xeCJK"},
tpl = [[
\documentclass[preview, border = 0pt, varwidth, 12pt]{standalone}
\usepackage{${packages}}
\setCJKmainfont{Noto Serif CJK SC}
\begin{document}
${header}
{ \${font_size} \selectfont
\color[HTML]{${color}}
${content}}
\end{document}
]],
},
},
},
},
-- 后略
},
你需要安装一个版本尽可能新的 tectonic 以使其按预期发挥作用。
详细分析
我们注意到 Snacks.nvim 官方默认配置 中 是这样描述这一段的:
math = {
enabled = true, -- enable math expression rendering
-- in the templates below, `${header}` comes from any section in your document,
-- between a start/end header comment. Comment syntax is language-specific.
-- * start comment: `// snacks: header start`
-- * end comment: `// snacks: header end`
typst = {
tpl = [[
#set page(width: auto, height: auto, margin: (x: 2pt, y: 2pt))
#show math.equation.where(block: false): set text(top-edge: "bounds", bottom-edge: "bounds")
#set text(size: 12pt, fill: rgb("${color}"))
${header}
${content}]],
},
latex = {
font_size = "Large", -- see https://www.sascha-frank.com/latex-font-size.html
-- for latex documents, the doc packages are included automatically,
-- but you can add more packages here. Useful for markdown documents.
packages = { "amsmath", "amssymb", "amsfonts", "amscd", "mathtools" },
tpl = [[
\documentclass[preview,border=0pt,varwidth,12pt]{standalone}
\usepackage{${packages}}
\begin{document}
${header}
{ \${font_size} \selectfont
\color[HTML]{${color}}
${content}}
\end{document}]],
},
},
对于 typst 的项我们忽略,主要关注 latex 方向。
此处有一个 tpl,是生成 latex 的模板。
注意到 documentclass 被设为了 standalone,
这是为了使生成的 PDF 文件恰与公式等大,便于嵌入文件。
而前面的 preview 是为了精细裁切,border = 0pt 是为了公式边缘不留空白,
varwidth 宽度自适应,12pt 指基础字号。
然后引用了上面 packages 列表中的包,此处官方默认有五个,
基本的数学符号都在里面了。
然后按注释,你可以通过在 Markdown 文件中插入
// snacks: header start
... some latex code ...
// snacks: header end
来为你的 LaTeX 公式自定义头,
例如 \let \fa \forall 一类。
建议在写文章之前就把这些 header 定义好, 因为 Snacks 的热重载频率很高, 会在你输入头输入到一半时不断重编译文中所有公式 造成风扇狂转和 tectonic 崩溃。
然后是正文部分,设置合适的字号(\selectfont 用来确保字号生效),
接着用给公式涂上主题色(SnacksImageMath),
最后是公式正文内容。
接着我们注意到如果要考虑中文支持,我们就要考虑它使用什么工具进行编译。
据 lua/snacks/image/convert.lua,我们有:
typ = {
ft = "pdf",
cmd = {
{
cmd = "typst",
args = { "compile", "--format", "pdf", "--pages", 1, "{src}", "{file}" },
},
},
},
tex = {
ft = "pdf",
file = function,
ctx.pdf = Snacks.image.config.cache .. "/" .. vim.fs.basename(ctx.src):gsub("%.tex$", ".pdf")
return convert:tmpfile("pdf")
end,
cmd = {
{
cwd = "{dirname}",
cmd = "tectonic",
args = { "-Z", "continue-on-errors", "--outdir", "{cache}", "{src}" },
},
{
cmd = "pdflatex",
cwd = "{dirname}",
args = { "-output-directory={cache}", "-interaction=nonstopmode", "{src}" },
},
},
-- 错误处理略
},
因此是有就先调用 tectonic,没有的话就去调用 pdflatex。 显然在前者中实现 CJK 支持比后者要简单。
我们在 packages 中加入 xeCJk,同时加入 fontspec 用来辅助查找字体(tectonic 查找字体能力偏弱),
并在 tpl 中加入一行 \setCJKmainfont{Noto Serif CJK SC}(替换成你电脑上实际有的字体)。
接着,见证奇迹的时刻……由此顺利结束,have fun!