TranslateBooksWithLLMs PDF翻译能力解析:标题、表格与字体如何被保留
【免费下载链接】TranslateBooksWithLLMsTranslate full-length books and documents with Ollama, OpenAI-compatible, Gemini, Mistral, DeepSeek, Poe or OpenRouter. Preserves formatting. Resumes where you left off. No file size limits.项目地址: https://gitcode.com/gh_mirrors/tr/TranslateBooksWithLLMs
TranslateBooksWithLLMs 是一款用大语言模型(LLM)整本翻译书籍与文档的桌面工具,支持的格式包括 EPUB、SRT、DOCX、PDF 和 TXT。它的 PDF 翻译能力采用“重排式输出”:原文 PDF 被解析成段落与结构单元,交给大模型翻译后,再生成一份新的、排版重排的 PDF——标题、表格、字体三大要素在这条流水线中被尽量保留。📄
PDF翻译是如何一步步工作的
整条流水线分三步,核心代码集中在 src/core/pdf/ 目录下:
- 提取:extractor.py 用 PyMuPDF 把 PDF 拆成段落列表,同时记录每段的样式(标题级别、列表、表格单元格)、格式(颜色、字体族、加粗)、背景色块与表格结构,统一存入 content.py 中的
PdfPlainContent数据模型。 - 翻译:段落直接复用项目的纯文本翻译管线 plain_text_pipeline.py,天然支持断点续翻与并行分块。
- 重建:builder.py 把译文段落拼装成 HTML + CSS,通过 PyMuPDF 的 Story/DocumentWriter 渲染成与原文同页宽的新 PDF。
需要特别说明:它保留的是结构属性(标题、表格、字体、颜色、色块、图片锚点),而不是原始版式——页面上的每一行都可能在新的页面上重新流动。这也是为什么多栏排版只算“尽力而为”。
标题识别:字号比例三级判级
标题识别是提取器里最“聪明”的一环,逻辑在 extractor.py 的_block_style函数中:
- 先算正文基准字号:统计全文所有保留文本块的字符加权主字号,作为“body size”。
- 再按字号比例判级(阈值定义见 extractor.py):
| 判定结果 | 条件 |
|---|---|
| heading1 | 字号 ≥ 正文 1.6 倍,且不超过 200 字符 |
| heading2 | 字号 ≥ 正文 1.3 倍 |
| heading3 | 字号 ≥ 正文 1.15 倍 |
| 加粗兜底 | 整段加粗、不超 80 字符、不超过 2 行且不以句末标点结尾 |
还有一个容易忽略的细节:标题块拆分。当同一个文本块内相邻行的字号比值达到 1.15 倍(SIZE_SPLIT_RATIO),或主色明显不同,就会强制拆成多个子块——这样“眉题 + 大标题 + 副标题”三种层次能各自获得正确的样式,而不会糊在一起。
到了重建阶段,builder 会把三类标题映射为<h1>/<h2>/<h3>,分别以 20pt、16pt、13pt 的无衬线粗体输出,标题的层级感在译文 PDF 中完整再现。
表格重建:连“只有横线”的表格也能认出
表格检测独立在 layout.py 中,识别两类来源:
- 全边框表格:直接调用 PyMuPDF 自带的
page.find_tables(),保留行列数 ≥ 2 的表格。 - 横线网格表格:这是为现代网页打印 PDF 专门做的适配——无头 Chrome/Skia 打印时会把每个单元格的
border-bottom画成一小段独立填充矩形。检测器从page.get_drawings()中收集这些细横线段,把共享相同列边界的横线聚成“网格”,再结合表头底色或表格上方文字确定顶边,最后用显式行列模式精确抽出每个单元格。
抽出表格后发生两件事:
- 每个非空单元格成为独立翻译单元,样式标记为
"cell",空单元格记为占位,保证译文表与原表行列一一对应(见 content.py 的PdfTable结构)。 - 表头识别与底色保留:表头行的背景色、各列的相对宽度都被记录,重建时表头单元格加粗并保留底色,列宽按比例换算为绝对 pt 宽度还原。
字体保留:名称、标志位与 Type3 三重解析
字体相关逻辑集中在 styling.py,目标是把 PDF 里五花八门的字体名归类为三个通用族:serif(衬线)、sans-serif(无衬线)、monospace(等宽)。
解析策略按优先级:
- 字体名关键词:命中
mono/courier/menlo…判等宽,sans/helvetica/arial/calibri…判无衬线,serif/times/georgia…判衬线; - PyMuPDF 字形标志位:名称无法识别时,回退到 span flags(位 4 = 衬线、位 8 = 等宽、位 16 = 粗体、位 2 = 斜体);
- Type3 字体穿透:浏览器打印的 PDF 常用
Type3 (16 0 R)这种引用名,FontResolver会顺着 FontDescriptor 找到真实字体名(如IBM-Plex-Sans-Bold),从而正确识别粗体与字体族。
除此之外,每个段落还会记录文字颜色、整段加粗/斜体、背景色块(带左侧色条的 callout 提示框)。builder 在重建时把这些信息转成内联 CSS,正文整体字体族则取全文段落的主导值(default_font_family),写进样式表的body规则——所以即使个别段落字体未知,整本书的“字体气质”也保持一致。
使用指南与已知限制
用命令行翻译一份 PDF 只需一行:
python translate.py -i paper.pdf -tl French输出会自动命名为paper (French).pdf,详见 docs/CLI.md。Web 界面中则直接拖入.pdf即可,翻译任务中途暂停后可随时从断点继续(见文章开头截图)。
⚠️ 新手使用前的几个提示:
- 仅支持文字型 PDF:扫描件会明确报错(不做 OCR),带密码的 PDF 在上传时即被拒绝。
- 原文版式不保留:输出是重排的新 PDF,多栏、脚注等复杂排版是尽力而为。
- 句内格式不保留:加粗、颜色只按“整段”粒度保留,句子中间的局部强调会丢失。
- 暂不支持润色:
--refine对 PDF 会给出警告并忽略,详见 docs/BACKLOG.md。
如果你想了解这项能力的设计取舍,项目保留了完整的规划文档:BLUEPRINT_PdfSupport.md(基础能力)与 BLUEPRINT_PdfFormattingA.md(表格、色块与字体保真),回归测试则在 tests/test_pdf/ 中覆盖了解析与重建的往返一致性。
【免费下载链接】TranslateBooksWithLLMsTranslate full-length books and documents with Ollama, OpenAI-compatible, Gemini, Mistral, DeepSeek, Poe or OpenRouter. Preserves formatting. Resumes where you left off. No file size limits.项目地址: https://gitcode.com/gh_mirrors/tr/TranslateBooksWithLLMs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考