在政务办公类系统里,wangEditor 已经是很常见的富文本编辑器了,但真正做起来就会发现一个痛点:很多公文、通知、红头文件都是以 PDF 形式存在的,而 wangEditor 本身只能处理 HTML 内容,PDF 根本没法直接拖进去编辑。用户表面上提的是“导入 PDF”,实际上背后还跟着一连串需求:要能把公文的正文文字稳定提取出来、要能自动清理页眉页脚和页码、要能按阅读顺序插入编辑器、最好解析完还能一键切到只读模式去核对原件。
这篇文章就是围绕“给 wangEditor 加一个导入 PDF 解析政府公文内容”这个真实场景展开的。我会把技术边界、解析原理、Django 后端的接口实现、wangEditor 端的接入方式,以及我在实操里踩过的坑一步步讲清楚。内容偏向有基础但没做过类似功能的开发者,如果你正在给编辑器接 pdf.js、想用 Django 做 PDF 解析接口,或者被 wangEditor 的 customUpload、只读模式折腾过,这篇文章可以直接帮你少走弯路。
1. 先搞清边界:为什么 PDF 不能直接进 wangEditor
刚接到这个需求时,很多人第一反应是“做个导入按钮,选完 PDF 后把文件塞进编辑器不就行了”。这个想法其实方向就错了。要打通链路,必须先搞清楚 wangEditor 到底在编辑什么。
1.1 wangEditor 只认 HTML,不认 PDF 文件
wangEditor 从 v4 到 v5,核心都是一个基于 contenteditable 的富文本编辑区。你在页面上看到的所有加粗、标题、段落,最终都会序列化成一段 HTML 字符串,比如 <p>正文内容</p>。你调 editor.getHtml(),拿到的就是这段 HTML;编辑器把内容渲染出来,依赖的也是这段 HTML。
PDF 在这里是一个完全不同的东西。PDF 是版式文档,它记录的是“哪个字符放在页面哪个位置”,而不是“这篇文章有哪些段落、哪些是标题、哪些需要加粗”。也就是说,PDF 文件本身没有一个通用的“正文结构”,不能像 DOCX 那样被编辑器直接解析成文档对象模型。PDF 想进 wangEditor,只能走一条路:先用解析工具把 PDF 里的文本内容抽出来,再转成 HTML 片段插进编辑器。
我在项目里给用户演示的时候,经常打一个比方:PDF 是一张已经印刷好的报纸,你没办法把一个编辑框直接“装进”这张报纸里继续写字,只能把报纸上的字先敲成电子文稿,再粘贴到编辑框里。这个“敲成电子文稿”的动作,就是我们技术侧要解决的 PDF 解析。
1.2 PDF 的内部形态和普通文档完全不同
PDF 的存储结构跟文本文件是两回事。你打开一个 PDF 文件,里面看到的是一堆对象和绘制指令,比如 BT ... Tj ... ET 这种文本绘制操作符,每个字符经常带着一个坐标位置。PDF 阅读器之所以能按顺序显示文字,是因为它根据这些坐标把字形一个个画在页面上。
这就带来很多用户感知不到、但开发时必须处理的麻烦:
- PDF 里的文字顺序不一定是阅读顺序。如果 PDF 是从某个排版软件导出的,文本块顺序可能和视觉顺序不一致。
- 两栏排版的公文(比如附件里的表格说明),按文件内部对象顺序抽取文本时,左栏和右栏内容会交叉混在一起。
- 页眉、页脚、页码对阅读者来说是辅助信息,但 PDF 解析时就是普通文本,会混进正文章节里。
- 扫描件 PDF 本质上是一张张图片,里面根本没有文本层,常规解析工具抽出来的是空内容或乱码,必须走 OCR。
所以,一个“能打开 PDF 并复制文字”的工具,并不等于一个“能正确解析公文内容到编辑器”的工具。只调一个现成 SDK 拿到一串 text 就完事,后面一定会在页面效果上翻车。
1.3 解析放在后端而不是前端的三个理由
PDF 解析存在两种常见做法,一种是在前端直接用 pdf.js 把文本抽出来,另一种是把 PDF 上传到后端解析。我这次选的是后端解析,原因很直接:
第一,公文内容经常涉及格式二次加工,比如要识别标题、落款、发文字号,还要做页眉页脚过滤。这些规则写在后端可以统一维护,前端只是把解析结果展示出来。一旦改成前端解析,每次调整策略都要发版,浏览器兼容性也会成为新问题。
第二,政务类项目很多是内网部署,浏览器环境未必都是最新版本,pdf.js 在某些老版本浏览器里的兼容问题够你排查一整天的。而 Python 后端装的解析库跑在服务器上,和浏览器无关,只要把最终文本回传前端就行。
第三,同一个 PDF 往往不只会导入 wangEditor,还要给其他系统复用。解析能力做成独立后端服务后,不管前端是 wangEditor、Vue 还是移动端 H5,都能通过同一个接口拿到解析文本。
方案定下来,整个技术链路就很清晰了:wangEditor 页面前端选择本地 PDF,把文件上传到 Django 后端;Django 调用 PDF 解析库提取文本并清洗;解析结果以 JSON 格式返回;前端拿到 text 后调用 wangEditor 的 API 插入编辑区。接下来我把每个环节拆开来讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. PDF 解析的核心:顺序、页眉过滤与脏数据
后端解析这一步是整个需求的关键,也是最容易反复返工的地方。这里不建议用那种简单把整页文本一次性导出的 API,因为公文这种版式文档一旦出现双栏、页眉、页码,一次性导出的文本就是一团乱麻。我们要先拿到每个文本行的坐标,再自己做排序和过滤。
2.1 用 PyMuPDF 拿坐标,按阅读顺序重排
我选的是 Python 的 PyMuPDF 库,import fitz。这个库速度快,能拿到文本块、行、字级别的位置信息,非常适合做版面分析。核心思路是:遍历每一页,拿到页面里的所有文本行及其包围盒 bbox,然后把行按 (y0, x0) 排序,y0 是行顶部的 y 坐标,x0 是行左侧的 x 坐标。这样做能最大程度还原人的阅读顺序。
直接上一个可以跑通的基础版本:
python复制import fitz
def extract_pdf_text(path: str) -> list[str]:
doc = fitz.open(path)
ordered_lines = []
for page in doc:
# 页面上的文本块都带 bbox,结构是 dict
blocks = page.get_text("dict")["blocks"]
page_lines = []
for block in blocks:
if block["type"] != 0: # 0 表示文本块,图片块直接跳过
continue
for line in block["lines"]:
y0 = round(line["bbox"][1], 1)
x0 = round(line["bbox"][0], 1)
text = "".join([span["text"] for span in line["spans"]]).strip()
if text:
page_lines.append((y0, x0, text))
# 这一页内部先按行排序,跨页之间也按页序追加
page_lines.sort(key=lambda item: (item[0], item[1]))
ordered_lines.extend([item[2] for item in page_lines])
doc.close()
return ordered_lin
