拆解 PDF 翻译引擎:表格纹丝不动、长语言自动缩放、图片文字 AI 重绘背后的工程

很多人以为 PDF 翻译是"把文字抽出来翻译再塞回去"。真要这么干,版式必崩。实际上,专业 PDF 翻译引擎的核心目标只有一个:不改任何坐标,只替换字符串。要做到这一点,得先理解 PDF 到底怎么存文字。

一、先搞懂:PDF 里的文字不是"文本",是"绘制指令"

PDF 不是 Word 那种"文字+样式"的结构化文档,它是内容流(Content Stream)+ 资源(字体/图片)+ 页面树。文字在 PDF 里是一串绘制操作符,比如:

复制代码
BT              % Begin Text
/F1 12 Tf       % 设置字体 F1,字号 12
1 0 0 1 100 700 Tm   % 文本矩阵:定位到 (100, 700)
(Hello World) Tj     % 显示文字
ET              % End Text

关键就在这条 Tm(文本矩阵):它用 6 个数 (a b c d e f) 定义了文字的绝对位置、缩放、旋转(Hello World) 只是个字符串参数。

所以"保版式"的本质动作极其简单:保持 Tm 等坐标指令一字不动,只把 Tj/TJ 里的源语言字符串替换成翻译后的字符串。 表格、图片、公式的位置全由坐标指令决定,文字字符串一换,它们天然"纹丝不动"。这就是为什么整篇机翻会乱------多数普通工具是"抽文本→重排版→生成新 PDF",坐标全丢了;而保版式工具走的是"原地改串"。

二、解析层:提取文字块 + 几何信息

翻译前要先"看懂"PDF。主流用 pdfminer.six(细粒度解析)或 PyMuPDF(fitz,速度快)。以 PyMuPDF 为例:

python 复制代码
doc = fitz.open("paper.pdf")
page = doc[0]
blocks = page.get_text("dict")["blocks"]
# 每个 span 带: text, bbox(x0,y0,x1,y1), font, size, color

这一步拿到的是带 bbox 的文字块树(block → line → span)。bbox 就是后面回填时定位用的"锚点"。表格在 PDF 里本质是绝对坐标的线条 + 文字,解析层只要不碰线条绘制指令,只标记文字 span,回填时就只动文字。

三、回填层:字体与编码是真正的坑

字符串换好了,但还有两个雷:

  1. 字体覆盖 :源文档可能只嵌了 Latin 字体,目标语言(如中文、阿拉伯文)字形缺失,渲染出来是空白或方块。解决方式是映射目标字体 + 子集化(subsetting) ------只把用到的字形嵌入,生成 ABCDEE+MyFont 这样的子集字体,并通过 ToUnicode CMap 把字符编码正确映射到 Unicode,保证翻译后还能被选中、搜索。
  2. 编码改写 :直接改 content stream 的 Tj 字符串时,字符需按目标字体编码重写,否则显示错乱。

也就是说,回填不是"字符串替换"就完事,而是重建一段干净的文本绘制指令 ,沿用原 Tm 坐标,换字体引用和字符串。

四、长语言缩放适配:用字宽度量动态压缩

德文、俄文、芬兰文往往比英文长 30%~50%。直接替换,文字会溢出单元格或重叠。工程做法是先量再缩

python 复制代码
w_src = sum(font.char_widths[ch] for ch in src) * size   # 源文字渲染宽度
w_dst = sum(font.char_widths[ch] for ch in dst) * size   # 翻译后宽度
if w_dst > w_src * 0.95:
    scale = (w_src * 0.95) / w_dst
    # 方案 A:等比缩小字号(改 Tf 的 size)
    # 方案 B:用 TJ 数组插入负字距(kerning)压缩间距
    # 方案 C:换行重排(仅在允许时)

核心依据是字体度量(font metrics / glyph widths) ------每个字形在 1/1000 em 下的标准宽度。算出渲染宽度后,要么缩小字号,要么用 TJ 操作符在字符间插入负间距做字距压缩,把总长压回原单元格。这就是"换字不换版"还能不挤的核心算法。

五、扫描件 OCR:从位图里"捞"出文字和坐标

上面的前提是 PDF 有可选文字。如果是扫描件 (文字是图片像素),content stream 里根本没有 Tj 字符串,只有一张图。这时候要上 OCR:

  1. 预处理:二值化、降噪、倾斜校正(deskew),提升识别率;
  2. 版面分析(Layout Analysis):用检测模型(DB / EAST / PSENet,或 LayoutLM 这类深度模型)把页面切成文本块、表格区、图片区,并给出每个文字块的 bbox;
  3. 文字识别:传统+LSTM 的 Tesseract,或 PaddleOCR(DB 检测 + CRNN 识别)、EasyOCR。识别网络本质是 CNN 提取特征 → RNN/Transformer 建模序列 → CTC/Attention 解出字符。

OCR 出来的不是纯文本,而是**"文字 + 坐标"**对。有了坐标,后面就能复用第三、四节的回填逻辑------只不过源"字符串"是从图片里识别来的。

⚠️ 注意:OCR 精度天花板取决于原图清晰度。模糊扫描件,再强的模型也救不回 100%。

六、图片内文字的 AI 抹除与重绘

最难的是图片里的文字也要翻译(带字幕的图表、漫画、海报)。这不是改 PDF 指令能解决的------文字是像素。完整管线是:

复制代码
1. 文字检测(Text Detection):DB/EAST 框出图片里文字区域 → 得到 mask
2. OCR 识别:读出原文字
3. 翻译:得到目标语言文本
4. 图像修复(Inpainting / 抹除):
   用修复模型,以 mask 为遮罩,把原文字区域"擦掉"并补全背景纹理。
   传统用 PatchMatch / 扩散,现代用 LaMa(傅里叶卷积)或
   Stable Diffusion Inpainting(潜空间扩散 + mask 引导)。
   输出 = 一张"干净、无字"的背景图。
5. 重绘(Render):在干净背景上,用目标语言字体把翻译文字
   渲染回原位置(对齐原文本框,必要时等比缩放字号)。

开源里的 manga-image-translator 就是这套管线的代表:检测 → OCR → 翻译 → inpainting → 重绘,一气呵成。"AI 抹除再生成新图"说的就是第 4 步的 inpainting------它不是 PS 橡皮擦,而是用生成模型"脑补"出被文字遮挡的背景,让结果看起来像图片本来就没有那行字。

工程上更轻量的做法(多数 PDF 翻译器采用):不真改图片,而是在原图上方叠加一个透明文字层,把翻译文字渲染上去、原文字用白色块盖住。好处是快、可逆;坏处是放大后能看到"盖戳"痕迹。真·inpainting 重绘更干净但更慢、更吃算力。

七、工程架构总览

复制代码
┌─────────────┐   ┌──────────┐   ┌──────────┐   ┌──────────────┐
│ 解析层       │ → │ 翻译层    │ → │ 渲染层    │ → │ 图像层        │
│ pdfminer/    │   │ DeepL/   │   │ 重写      │   │ OCR +        │
│ PyMuPDF      │   │ 大模型API │   │ content   │   │ Inpainting   │
│ 提取 bbox+   │   │ 文本块    │   │ stream    │   │ 检测/识别/    │
│ 文字树       │   │ 级翻译    │   │ 保留坐标  │   │ 抹除/重绘     │
└─────────────┘   └──────────┘   └──────────┘   └──────────────┘
  坐标锚点          语义对齐        换字不换版      图片文字兜底

四层各司其职:解析层负责"不动",翻译层负责"准",渲染层负责"缩",图像层负责"图里的字"。漏掉任何一层,都会出现你见过的格式灾难。

八、落到现实:一个已产品化的实现长什么样

理论讲完,看一个真实封装好的在线服务更直观------pdftranslator.org 这类"保版式 PDF 翻译"产品,底层跑的正是上面这四层管线,可以拿它来反向验证本文的判断标准:

  • 翻译完表格不动、图片位置不跑 → 说明渲染层做的是原地改写 content stream(第三节),而非抽文本重排;
  • 德文/俄文等长语言翻译后不溢出 → 说明渲染层接入了字宽度量 + 动态缩放(第四节);
  • 扫描件也能翻 → 说明图像层前置了 OCR 管线(第五节);
  • 图片内文字能直接出翻译图 → 对应的是 检测 → inpainting 抹除 → 重绘 链路(第六节)。

也就是说,一款翻译器翻完版式纹丝不动,就证明它在"换字不换坐标"而不是重排版;而它把四层管线封装成"上传即翻"的在线服务,恰好是本文架构图从论文到工程的一次落地。对一个想读懂 PDF 翻译引擎的人,这种已产品化的实现是最好的对照样本。

九、总结

PDF 翻译的"换字不换版"不是玄学,而是一套精密工程:

  • 保版式 = 改 Tj 字符串、保 Tm 坐标;
  • 不溢出 = 字体度量算宽度、动态缩字号/压字距;
  • 扫描件 = OCR 把像素变"文字+坐标";
  • 图内字 = 检测→识别→翻译→inpainting 抹除→重绘。

没有银弹。源文档越复杂、扫描越糊,天花板越低。但理解了这套链路,你就能判断一个工具到底是在"原地改串"还是"抽文本重排"------前者才是不乱的根。

相关推荐
IT_陈寒4 小时前
Vue的响应式什么时候会失灵?这个坑我踩了
前端·人工智能·后端
Mark-Wang5 小时前
2026大厂AI Agent高频面试题Top50:题目+参考答案+追问陷阱
人工智能
五度易链-区域产业数字化管理平台5 小时前
WorkBuddy 实战:将带 MD5 签名的第三方 API 封装为 MCP 服务(企业模糊搜索接口案例)
大数据·人工智能·mcp
怪奇云呼军5 小时前
知识库也会注入指令?闪电智能VoiceAgent 如何防住 Prompt Injection
人工智能·python·算法·云计算·音视频
xushichang123_5 小时前
企业降本刚需下,云上模型蒸馏与轻量化部署怎么选?AWS“大模型教研+小模型推理” 路径
大数据·人工智能
EasyDSS5 小时前
开会不用装App:私有化音视频系统EasyDSS即时视频会议,浏览器点开就能聊,AI帮你写纪要
人工智能·音视频
阿里云大数据AI技术6 小时前
基于 EMR Serverless Ray 实现 Qwen 模型批量推理实践
人工智能·算法·agent
ZJU_统一阿萨姆6 小时前
【算子开发】环境搭建与第一个CUDA程序
开发语言·人工智能·系统架构
老登为啥喜欢吹AI6 小时前
继DSH之后!Codex Harness 也开源了!Rust 核心、三层架构、人机协作
人工智能