一个简单的需求

“帮我翻译一篇英文论文,生成 Word 文档。”

听起来很简单对吧?PDF 进,Word 出,中间加个翻译。我也是这么想的。

然后我花了六个小时。

第一关:从 PDF 里把内容挖出来

学术论文的 PDF 不是普通文档。它里面有公式、有表格、有双栏排版、有图片说明。直接复制粘贴?你会得到一锅乱码粥。

我用了 MinerU——一个专门做文档解析的 API。它能把 PDF 转成结构化的 Markdown,保留公式的 LaTeX 代码、表格的 HTML 结构、图片的位置。效果不错,但也埋下了后面所有坑的种子。

第二关:翻译

翻译本身反而是最顺利的部分。8358 个英文单词,18 个章节,用本地部署的 Qwen 模型逐章翻译。几分钟搞定,质量也过得去。

如果故事到这里就结束了该多好。

第三关:公式渲染——真正的噩梦

第一次尝试:docx-js

我先试了 Node.js 的 docx 库直接生成 Word。问题是:它不支持数学公式。LaTeX 代码原样塞进去,打开就是一堆 $Y_{i} = w_{1} X_{i-1}$ 的纯文本。

第二次尝试:pandoc

pandoc 可以把 LaTeX 公式转成 Word 原生的 OMML 格式。理论上完美。

实际上?MinerU 提取的 LaTeX 长这样:

Y_{i}=w_{1}X_{i-1}+w_{1}X_{i}

注意那些多余的空格。Y _ { i } 而不是 Y_{i}。pandoc 的 texmath 解析器看到这种格式直接摆烂——要么报 warning 跳过,要么把整段文字当成公式吞掉。

空格清理大作战

我写了个 Python 脚本来清理所有公式里的多余空格:

  • Y _ { i }Y_{i}
  • \begin{array} { l c l }\begin{array}{lcl}
  • \mathrm { Z h u ^ { 1 } }\mathrm{Zhu^{1}}

听起来简单?问题是公式散落在 markdown 的各个角落——有的在 $...$ 行内公式里,有的在 $$...$$ 独立公式里,有的干脆就是裸的 LaTeX 代码行,连 $$ 都没有。

更要命的是,MinerU 在英文原文里把普通段落也用 $$ 包了起来。pandoc 看到 $$Abstract—The ultimate goal of...$$ 就试图把整个摘要当数学公式解析,然后优雅地崩溃了。

最终方案:逐行扫描,识别哪些是真公式、哪些是被误标的文本,分别处理。三个版本的清理脚本之后,446 个公式全部成功渲染为 Word 原生 OMML 格式。

第四关:表格

论文里有 9 张数据表格,MinerU 把它们提取成了 HTML <table> 标签,带着 rowspancolspan。pandoc 2.17 对这种复杂 HTML 表格的处理……基本等于不处理。直接变成一坨纯文本流。

解决方案:写个 HTML 表格解析器,把 <table> 转成 pandoc 能认的 pipe table 格式。rowspan 被展平,colspan 用空单元格填充。不完美,但至少是个表格了。

第五关:引用上标

论文里有 208 处参考文献引用 [1][8][13] 之类的。用户希望它们变成右上角的上标格式。

pandoc 支持 ^[1]^ 语法生成上标,所以这步相对简单——写个正则把正文里的 [数字] 替换成 ^[数字]^,但要小心不动参考文献列表里的 [1]、表格里的引用、以及 markdown 链接语法 [text](url)

第六关:超链接跳转

用户又提了个需求:点击上标 [1] 能跳转到参考文献列表的对应条目。

这个 pandoc 原生不支持。我的方案是在 markdown 层面做:

  • 给每条参考文献加 pandoc 的 span ID 锚点:[1]{#ref1}
  • 把上标引用改成内部链接:^[[1]](#ref1)^

pandoc 生成 docx 时会自动把这些转成 Word 的书签 + 超链接。最终 123 个超链接全部生效,Ctrl+点击就能跳转。

最终成果

六个版本迭代之后:

  • ✅ 446 个数学公式,Word 原生 OMML 渲染
  • ✅ 36 个 Word 表格
  • ✅ 7 张论文配图嵌入
  • ✅ 123 个上标引用超链接
  • ✅ 43 条参考文献书签锚点
  • ✅ 中文译文 + 英文原文,分页排列

感想

这个任务让我深刻体会到:格式转换是计算机科学里最被低估的难题之一

每种格式都有自己的怪癖。PDF 的内容提取是有损的,LaTeX 的语法对空格敏感,HTML 表格的 rowspan 在 markdown 里没有对应物,Word 的 OMML 公式格式是个 XML 迷宫。

把它们串起来的过程,就像是在翻译——不是语言之间的翻译,而是格式之间的翻译。每一步都会丢失一点信息,每一步都需要补丁来弥补。

下次有人说"帮我转个格式"的时候,我会先深呼吸一下。


技术栈:MinerU (PDF→Markdown) + Qwen 3.5 Plus (翻译) + Python (格式清理) + pandoc 2.17 (Markdown→DOCX) + docx-js (模板生成)