# @mindmax/pdf-markdown：开源的 PDF 转 Markdown 库，Node 与浏览器通用

URL: https://mindmax.me/zh/blog/pdf-markdown/
Last verified: 2026-11-09

> @mindmax/pdf-markdown 是 Apache-2.0 协议的 PDF 转 Markdown 库，零运行时依赖，在 22 份公开文档上回归测试。它解决什么、拒绝做什么、还有哪些缺陷没修，这篇都写清楚。

## 我们开源了 PDF 转 Markdown 引擎，连它还做不好的地方也一起公开了

2026年11月9日 · 阅读需 8 分钟

[MindMax Team](https://mindmax.me/zh/about/)

打造 MindMax

**@mindmax/pdf-markdown** 是一个开源的 PDF 转 Markdown 库，Apache-2.0 协议，支持 Node 18+ 和浏览器。
它没有任何运行时依赖，不发任何网络请求，并在 22 份公开文档（共 612 页）上跑回归，120 条断言都由人对照
PDF 逐条核过。它还附带一份「目前仍然做错的案例」清单，我们希望你来一起把它们修掉。

`

npm i @mindmax/pdf-markdown

`

[PDF 转 Markdown 页面](https://mindmax.me/zh/tools/pdf-to-markdown/)背后跑的、MindMax 各端里用的，就是同一个包，
不是某个私有版本的演示缩水版。

## PDF 转 Markdown 为什么总出错[​](https://mindmax.me/#pdf-转-markdown-为什么总出错)

PDF 里没有「段落」，只有放在坐标上的字形。读者看到的一切结构——分栏、标题、表格、阅读顺序——
都得由转换器反过来猜。大多数糟糕的输出，都是同样几类猜测猜错了。

- **分栏被横着读。** 双栏、三栏的论文，输出成左栏半句接右栏半句。

- **页眉页脚混进正文。** 每一页，页眉、页脚、页码都插在段落中间。

- **该是表格的不是，不是表格的反成了表格。** 真表格塌成一行行空格；行间公式或窄栏却被当成表格。

- **标题靠字号猜。** 一个大号符号变成 `#` 标题；折成两行的标题变成两个标题。

- **你的原文被二次解释。** PDF 里的 `$`、`*`、`_`，或者行首的 `#`，转出来的 Markdown 渲染后变了样。

如果你把 PDF 喂给大模型或检索系统，这些就不是排版问题：被切在两栏之间的一句话，没人能检索到；
一个多余的标题，会把一节切成两节。

## 它怎么处理[​](https://mindmax.me/#它怎么处理)

引擎拿 pdf.js 已经给出的文字层，从中重建结构。

| 问题 | 引擎的做法 |
| --- | --- |
| 分栏 | 检测栏缝（含三栏页），按栏阅读；整宽的块把页面切成若干带。 |
| 页眉、页脚、页码 | 有 /Artifact 标记的 PDF 优先用标记；没标记的靠跨页的几何重复识别。 |
| 段落 | 按基线把字形合成行，再按行距和缩进合成段；跨行断开的连字符单词重新接上。 |
| 标题 | 按相对「本页正文」的字号、编号和间距判断；折行的标题合回一个。 |
| 表格 | 按几何画出表格，并拒绝把公式、窄栏当成表格。 |
| 超链接 | 只在 PDF 里真有链接注释时恢复，不凭空捏造。 |
| 特殊字符 | 只转义来自 PDF 的字符，不动引擎自己写的 Markdown 语法，所以你的 $5 还是 $5。 |

另有两个设计选择，比看上去重要。

**天生是本地的。** 这个包不发任何网络请求；在浏览器里，文件不会离开当前标签页。所以你可以放心让它处理
合同、未发表的论文和内部报告。

**输出是可定位的。** 库同时导出 `buildPdfTextMap`，记录每一页、每个文字块落在最终字符串的哪个位置。
阅读器在 PDF 里划一句话，能按偏移量原样取回同一句，而不是去搜文本再祈祷它只出现一次。
如果你在做引文、高亮或带出处的 RAG，这一层事后很难补。

数字：gzip 后 25.1 KB，约 4,100 行 TypeScript，170 条测试，语句覆盖率 97.3%。

## 它拒绝做什么[​](https://mindmax.me/#它拒绝做什么)

与其让你自己撞上边界，不如我们先说清楚。

- **不做 OCR。** 扫描页没有文字层。库会报告这一页是扫描件或空页，不会悄悄去调模型。

- **不还原公式。** PDF 存的是定位好的字形，不是公式，`x²` 到手是 `x2`。库会检测出现这种情况的页并报告页数。

- **不搬图片。** 图留在 PDF 里。

- **不上传。** 任何内容都不离开本机。

## 公开测试，缺陷也公开[​](https://mindmax.me/#公开测试缺陷也公开)

22 份语料里，16 份是 CC BY，5 份是美国联邦政府作品，1 份是中国政府官方文件，都可以再分发，所以每份文档
转出的 Markdown 原样放在仓库里，任何人都能整套复跑。每份文档有各自的断言清单，120 条全部通过。

同时有 **10 份文档里共 17 项已知失败检查**。它们被标成「预期失败」，所以 CI 保持绿色，缺陷却一直可见。
[完整结果表和缺陷清单](https://mindmax.me/zh/open-source/pdf-markdown/benchmark/)由测试运行直接生成，页面上没有一个手写数字。

最可能影响你的几条：

- **两端对齐或拉开字距的中文**，字与字之间可能被插进多余的空格。

- **字体缺少 Unicode 映射**时，数字会变成 `�`、全角空格会变成 `!`，而体检并不总能把这一页标出来。

- **杂志式排版**：侧栏文字可能被读进正文或列表。

- **表格旁边的正文栏**可能被并进表格，表头行也可能和相邻段落合并。

- **标题层级可能倒挂**：封面占掉了 1–5 级，编号小节也不一定被认成标题。

以上每一条，在仓库里都对应一份带失败断言的文档。这也正是我们全部的贡献方式。

## 一起把它做好[​](https://mindmax.me/#一起把它做好)

如果某份 PDF 在你这里转得不好，你能给我们的最有用的东西是：**那份文档（或一份公开的类似文档），加一句话说明
正确的结果应该是什么样。** 它会成为一个带失败断言的语料用例：测试变红，代码改到变绿，用例永远留在
仓库里。附带 PDF 的反馈，远比一大段文字描述更有价值。

- **反馈转换效果差：**[提交语料用例](https://github.com/woodpeng/mindmax-pdf-markdown/issues/new?template=corpus_case.yml)

- **动手修一个：**读 [CONTRIBUTING](https://github.com/woodpeng/mindmax-pdf-markdown/blob/main/CONTRIBUTING.md)，从上面五条里挑一条，发 Pull Request。目标是把语料扩到 40 份。

- **提问或提议：**[Discussions](https://github.com/woodpeng/mindmax-pdf-markdown/discussions)

- **先试试看：**[浏览器里的 playground](https://woodpeng.github.io/mindmax-pdf-markdown/)，不用安装。

## 我们的承诺[​](https://mindmax.me/#我们的承诺)

下面几条都写在仓库里，以仓库原文为准，不是我们在这里另说一套：

- **安全问题报告，72 小时内首次响应**（[SECURITY](https://github.com/woodpeng/mindmax-pdf-markdown/blob/main/SECURITY.md)）。

- **贡献者会被提拔。** 合并 3 个 PR 或被采纳的语料用例，就可以申请 triager 权限（[GOVERNANCE](https://github.com/woodpeng/mindmax-pdf-markdown/blob/main/GOVERNANCE.md)）。

- **项目不依赖我们。** 所有者失联 90 天，维护者可以 fork 继续。

- **MindMax 自己依赖同一个包。** App 的修复，会先在这里发版。

有两点要如实说明：仓库还很年轻；发布凭证目前由一位所有者持有，90 天规则就是为这种情况准备的。

## 接下来[​](https://mindmax.me/#接下来)

这个引擎是第一块，不是最后一块。支撑我们 [Markdown 转 Word](https://mindmax.me/zh/tools/markdown-to-word/)、LaTeX 转 Word、
ChatGPT 转 Word 的 Word 导出引擎，是下一个开放候选。开放之前要做完三件事：断开对内部 core 的依赖、
自带测试和往返样例、定下我们愿意长期保持的公开 API。现在先宣布，不给日期。已经开放了什么、边界在哪里，见
[开源页](https://mindmax.me/zh/open-source/)。

## 常见问题[​](https://mindmax.me/#常见问题)

### @mindmax/pdf-markdown 可以商用吗？[​](https://mindmax.me/#mindmaxpdf-markdown-可以商用吗)

可以，Apache-2.0。MindMax 应用本身是闭源的，引擎不是。

### 能处理扫描件吗？[​](https://mindmax.me/#能处理扫描件吗)

不能，而且它会明说。没有文字层的页会被报告为扫描件或空页，不会送去 OCR 模型。MindMax 桌面端另有一个
由用户手动发起的 OCR 步骤。

### 会把我的 PDF 发到别处吗？[​](https://mindmax.me/#会把我的-pdf-发到别处吗)

不会。这个包不发任何网络请求，在浏览器里文件只留在标签页内。

### Node 和浏览器都能跑吗？[​](https://mindmax.me/#node-和浏览器都能跑吗)

能，Node 18 及以上，以及浏览器。你传入已打开的 pdf.js 文档，所以它不会再打包第二份 pdf.js。

### 能用在 RAG 或大模型流程里吗？[​](https://mindmax.me/#能用在-rag-或大模型流程里吗)

这正是主要用途之一：分栏按顺序读、页眉页脚已去掉，`buildPdfTextMap` 还能给你回到原页的偏移量，用于引文。

### 怎么反馈某份转得不好的 PDF？[​](https://mindmax.me/#怎么反馈某份转得不好的-pdf)

提交一个[语料用例](https://github.com/woodpeng/mindmax-pdf-markdown/issues/new?template=corpus_case.yml)，
附上文档（或公开的等价文档），再用一句话描述正确的输出。

比起读这篇文章，你可能更想直接读代码，入口在[仓库](https://github.com/woodpeng/mindmax-pdf-markdown)。
如果清单里的某个缺陷你也被它折磨过，我们很乐意和你一起修掉它。

**标签：**

- [产品札记](https://mindmax.me/zh/blog/tags/notes/)
- [本地优先](https://mindmax.me/zh/blog/tags/local-first/)
