# Scan PDF To Text Epub

> 将扫描版或图片型 PDF 书籍（尤其是中文书籍）转换为可重排的 EPUB 3，正文经过 OCR 后可搜索，并建立真实目录，同时把原书中的图、表、示意图和照片裁切后保留下来。适用于将扫描 PDF 转成文字 EPUB、修复插图丢失或图中文字混入正文的 OCR EPUB，以及反复检查 PDF 转 EPUB 的质量。不适用于以整页扫描图作为阅读内容的固定版式 EPUB。

- Skill: `soapwong/scan-pdf-to-text-epub` (Agent Skill, multi-file: 24 files)
- Install (CLI): `npx skillmds@latest add soapwong/scan-pdf-to-text-epub`
- Raw SKILL.md: https://api.skillmd.com/api/skills/soapwong/scan-pdf-to-text-epub/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: soapwong (https://skillmd.com/u/soapwong)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/soapwong/scan-pdf-to-text-epub

---


# 扫描 PDF 转文字 EPUB

生成可重排的文字 EPUB，只把真正有意义的视觉内容保留为图片。视觉复核和重复校验是必做步骤，不是可选的润色工作。

## 运行环境

优先使用工作区自带的 Python。转换环境必须提供 `pypdfium2`、`pdfplumber`、`Pillow`、`rapidocr_onnxruntime`；正式发布校验还需要 `ebooklib`、Java 和 EPUBCheck。不知道 Python 路径时，调用 `codex_app__load_workspace_dependencies` 获取。外部工具缺失时校验器会明确报告降级，但降级结果不能作为最终发布通过。

运行命令前设置以下路径：

```text
SKILL_DIR=<包含本 SKILL.md 的目录>
PYTHON=<工作区 Python 可执行文件>
```

编辑书籍配置前，先阅读 [references/config-schema.md](references/config-schema.md)。

## 工作流程

### 1. 准备扫描文件

保持源 PDF 不变，并为本书建立独立的工作目录。

```powershell
& $PYTHON "$SKILL_DIR\scripts\prepare_scan.py" $PDF --work-dir $WORK_DIR --ocr-mode auto
```

只有脚本判断内嵌文本层可用时才使用它。纯扫描文件交给脚本选择 RapidOCR。检查脚本报告的识别模式，不要因为页面看起来像扫描图就假定它没有隐藏文本层。

### 2. 生成检查材料

```powershell
& $PYTHON "$SKILL_DIR\scripts\inspect_scan.py" "$WORK_DIR\manifest.json" `
  --title $TITLE --author $AUTHOR --publisher $PUBLISHER --publication-date $DATE
```

脚本会生成：

- `inspection.json`：自动视觉候选、可能的图表标题和文章日期。视觉候选包含建议框、类别、启发式置信度、发现原因和稳定编号。
- `book-config.json`：本书专用配置。新候选会作为默认禁用的视觉项写入初始配置，必须人工确认后才能启用。
- `review/all-pages/`：用于发现无图题视觉内容的全书联系表。
- `review/candidate-pages/`：带候选框、短编号、置信度、简短信号代码和 PDF 坐标网格的候选页；完整理由保留在 `inspection.json`。
- `review/history/`：重跑检查器时归档上一轮 `inspection.json`、候选页和联系表，当前目录不会混入过期候选，旧材料仍可追溯。

检测器会结合 OCR 文字掩膜后的剩余墨迹、区域密度、连通区域、投影结构、留白分割和图题模式。它不依赖图题才能发现照片、示意图或表格，但输出仍只是待复核建议。再次运行时会根据同页图题或区域重叠尽量复用原有 `candidate_id`；普通重跑不会改写现有人工 `book-config.json`。显式传入 `--overwrite-config` 时，检查器会先把原配置归档到 `config-history/`，再生成新模板。

初始配置中的 `publication.identifier` 根据源内容稳定生成且不包含源路径；`publication.modified_utc` 会保持为空。开始正式构建前，按 [配置文档的发布信息与封面章节](references/config-schema.md#发布信息与封面)填写显式 UTC 修改时间，并确认原书 ISBN 应放在 `source_identifier` 还是确实属于当前 EPUB 的 `isbn`。

### 3. 复核全书

检查每一张全书联系表，不能只看自动候选页。把检测器漏掉的示意图、照片、地图、书法或其他有意义的视觉内容补进配置；自动建议框也必须对照源页调整，不能把启发式置信度当成正确概率。第 7 步的本地工作台用于完成逐页决定和几何调整，但不能替代全书联系表总览。

每个自动检测到的视觉候选项必须采用以下一种处理方式：

- 在本地工作台中接受并完成视觉配置，或手工在 `visuals` 中启用并完成配置。
- 在本地工作台中填写理由后忽略，或把它的 `candidate_id` 记录到 `ignored_visual_candidates`，并在工作笔记中写明合理的忽略原因。

根据印刷目录和各章起始页建立真实的 `articles` 列表，确认标题、日期、起始页，以及推断或明确指定的结束页。

每个启用的视觉内容都要满足：

- `text_exclusion_boxes` 使用一个或多个二维框覆盖印刷图像、图内标签及印刷图题；只有中心点落入框内的 OCR 行会从可重排正文中排除。
- `crop` 只包含视觉内容本身，通常不包含印刷图题；EPUB 会生成干净的语义化图注。
- `anchor_y` 单独指定视觉内容在正文流中的插入位置，不能用裁切框或排除框隐式代替。
- 相邻正文句子的 OCR 行中心点必须位于所有 `text_exclusion_boxes` 之外。
- 图题应显示在图片上方时使用 `kind: table`，应显示在图片下方时使用 `kind: figure`。
- 把视觉内容附近的正文句子加入 `text_checks`，避免错误边界悄悄删掉正文。
- 如果图中的独特标签、图例或坐标数字绝不能混入正文，把它们加入 `forbidden_text`。

坐标使用候选页上标注的 PDF 点数，二维框顺序为 `[left, top, right, bottom]`。禁止裁切整页；构建器会拒绝覆盖页面面积超过 75% 的裁切框和新版文字排除框。

根 `schema_version` 只表示视觉几何格式，不表示发布信息是否完整。旧版 `schema_version: 1` 配置中的 `region: [top, bottom]` 无需迁移几何字段；程序会在内存中按全页宽排除，不会改写原配置。新配置使用 `schema_version: 2`；同一视觉项同时出现 `region` 和 `text_exclusion_boxes` 属于歧义，必须先人工选择正确边界。

这项几何兼容不等于旧配置可以直接构建。Phase 7 起每次构建都必须有 `publication.identifier` 和 `publication.modified_utc`；缺少 `publication` 或任一字段时，构建会失败并提示迁移这两个字段，不能靠保留 `schema_version: 1` 绕过。

迁移旧配置时保留原有几何设置，只补发布信息。优先用同一 manifest 重新运行 `inspect_scan.py` 并把 `--config` 指向新模板，从模板取得不泄漏路径的稳定标识，再人工填写真实的 UTC 修改时间；也可以手工添加符合 schema 的 `publication`。不要覆盖旧配置，不要自动升级几何，也不要使用动态当前时钟、文件 mtime、随机临时 ID 或假日期。完整命令和字段示例见 [旧配置兼容](references/config-schema.md#旧配置兼容)。

### 4. 配置文本清理

按需设置：

- `running_headers`：重复出现的书名或文章页眉。
- `header_patterns`：只匹配可靠页眉的正则表达式。
- `footer_patterns`：只匹配可靠页脚的正则表达式。
- `note_patterns`：识别页底来源或出处说明，并将其移动到章节标题下方。
- `replacements`：已经对照扫描页确认无误的固定 OCR 错字替换。
- `skip_pages`：空白页，或已由生成目录替代的扫描目录页。
- `front_matter`：需要按阅读顺序保留的出版说明、序言等前置内容。
- `page_offset`：印刷页码与 PDF 页序号之间的偏移量。

不要猜测性修改 OCR 文字。每一项替换都必须与源扫描页核对。

边注、脚注和页底出处要采用保守的复核边界：

- 未命中已确认的页眉、页脚或视觉排除规则时，边注或侧栏文字按普通可搜索正文保留，不只因横向位置、短文本或位于页底而静默删除；同一条 OCR 输入只应在正文中出现一次。
- 只有在源扫描页确认了明确的页底来源/出处说明后，才配置 `note_patterns`。正则应尽量使用首尾锚点，例如 `^资料来源：.*$`，避免把普通正文或边注误移走。匹配项会从正文段落移到章节标题后的 `<aside epub:type='note'>`；有日期时它位于日期之后，并同步保留在 TXT 中。
- `note_patterns` 是人工确认的文字规则，不是页底位置检测器。未配置的脚注、出处或侧栏不会自动分类；配置前应把前后相邻正文加入 `text_checks`，构建后分别检查 EPUB 的正文和 note。

这组边界的外部回归见 `tests/test_review_boundaries.py`。表格或其他视觉内容附近的正文仍使用二维 `text_exclusion_boxes` 的 OCR 行中心点命中规则：相邻正文加入 `text_checks`，绝不能混入正文的图内 OCR 加入 `forbidden_text`。已有 `tests/test_epub_geometry.py::test_v2_exclusion_boxes_keep_side_text_and_remove_visual_ocr` 覆盖这条行为，本 skill 不再重复建立同一夹具。

### 5. 生成 OCR 质量报告

```powershell
& $PYTHON "$SKILL_DIR\scripts\report_ocr_quality.py" "$WORK_DIR\book-config.json"
```

默认报告位于 `review/ocr-quality.json` 和 `review/ocr-quality.md`。先处理 high 和 medium 风险，再复核 low 风险；报告只提出问题，不会改写 OCR、配置或正文。

报告包括低置信度、乱码、缺失置信度、罕见字符、异常长数字、同页重复行和中英文直接相邻七类信号，并按风险和页码提供两种稳定复核顺序。中英文直接相邻只是低风险启发式信号，`A股`、`GDP增长` 等正确写法也可能出现，不能据此自动替换。

默认低置信度阈值为 `0.80`。需要调整复核规模时使用 `--low-confidence-threshold 0.75`；阈值和 OCR 来源行数会写入报告摘要。逐项检查 `replacements` 的实际出现次数和源行证据，零命中替换同样需要确认或删除。

跨页重复文字通常是页眉、页脚或正常复现，不属于本报告的同页 OCR 重复规则；在结构推断或人工配置页眉页脚时处理。

### 6. 生成结构建议

```powershell
& $PYTHON "$SKILL_DIR\scripts\suggest_structure.py" "$WORK_DIR\book-config.json"
```

默认报告位于 `review/structure-suggestions.json` 和 `review/structure-suggestions.md`。报告会提出目录页、章节、页码偏移、页眉页脚、空白页和非正文页候选，但不会改写正式配置。

逐项对照扫描页处理候选。可以在第 7 步的本地工作台中完成决定，也可以手工维护配置：

- 接受候选时，按 `proposed_change` 把值人工写入对应配置字段；下次运行会把它标为 `configured`。
- 拒绝候选时，把稳定的 `candidate_id` 加入 `ignored_structure_candidates`；下次运行会把它标为 `ignored`。
- 正式配置优先于旧忽略记录。候选置信度只用于安排复核顺序，不能代替人工确认。

完整结构报告可能包含目录标题、章节名和页边文字。用户书籍的报告只能保存在本机，不得提交仓库。

### 7. 使用本地复核工作台

生成视觉检查材料、OCR 质量报告和结构建议后，启动只监听本机回环地址的复核服务：

```powershell
& $PYTHON "$SKILL_DIR\scripts\review_server.py" "$WORK_DIR\book-config.json" `
  --state "$WORK_DIR\review\review-decisions.json"
```

命令会输出并打开一个 `http://127.0.0.1:<端口>/` 地址。页面序列、扫描页画布和复核面板分别用于：

- 接受或带理由忽略视觉候选，新增人工视觉，并拖动或精确填写裁切框、文字排除框和插入锚点。
- 接受或带理由忽略结构候选，新增、更新或删除章节起点。
- 查看并记录 OCR 风险、正文保留检查和图内文字禁止泄漏检查。
- 使用撤销恢复最近决定；撤销历史在关闭并重新打开工作台后仍保留。

决定默认写入独立的 `review-decisions.json`，不会直接改写 `book-config.json`。状态文件记录原配置摘要；原配置发生变化后，旧决定不会被静默重放。所有页面内容和决定只通过本机服务传输，不依赖外部网络。

复核完成后点击“导出”，把合并后的配置写到新的目标，例如 `$WORK_DIR\book-config.reviewed.json`。导出会保留原配置中的未知字段并保护书籍输入文件；已有目标必须显式允许覆盖。旧版 `schema_version: 1` 只有在明确选择“升级旧版配置”后才转换为新版几何字段。相同输入和决定的连续导出应字节一致。

后续命令使用最终确认的配置：

```powershell
$CONFIG = "$WORK_DIR\book-config.reviewed.json"
```

如果没有使用工作台，而是直接手工完成原配置，则把 `$CONFIG` 指向该文件。

### 8. 构建

```powershell
& $PYTHON "$SKILL_DIR\scripts\build_epub.py" $CONFIG
& $PYTHON "$SKILL_DIR\scripts\review_visuals.py" $CONFIG
```

构建结果必须同时包含 EPUB 和配套 TXT。检查每一张生成视觉联系表；如果标签、边框、图例或相邻正文靠近裁切边缘，还要单独打开该图片检查。

正式构建必须包含稳定的 `publication.identifier` 和显式 `publication.modified_utc`。封面可以从源页裁切，也可以读取本地 PNG/JPEG；构建器会生成 `cover-image` 声明，并在 `visible: true` 时把 `cover.xhtml` 放入书脊。封面不进入正文 `visuals`，源页封面允许覆盖整页。

构建器会根据 OCR 行框保守恢复单栏、双栏、跨栏文字、窄侧栏、首行缩进、印刷标题和跨页续句，并把栏内视觉绑定到相同的纵向区段。布局证据不足时应降级为单栏；任何布局分类都不得删除、复制或改写 OCR 行。发现复杂版式时，除检查最终阅读顺序外，还要确认同一段文字在 EPUB 和 TXT 中只出现一次。

反复调整配置并重新构建，直到所有图片完整，且没有夹带无关正文。

最终配置确认后连续构建两次，并用 `Get-FileHash -Algorithm SHA256 $EPUB` 比较结果。相同源文件、配置和修改时间必须生成相同的 EPUB 字节；配套 TXT 也应保持一致。

### 9. 反复校验

平时调整配置时运行内部结构和正文校验：

```powershell
& $PYTHON "$SKILL_DIR\scripts\validate_epub.py" $CONFIG
```

每次修改图片边界或文本清理规则后都要运行校验，最终重建后再运行一次。校验不通过时不得报告完成。

校验器会检查：

- EPUB ZIP 文件顺序和完整性。
- XML/XHTML 解析、清单、书脊、链接、导航和元数据。
- 视觉内容数量、图注、替代文本、PNG 有效性，以及是否存在整页图片。
- 每个检测候选项是否已嵌入或明确忽略。
- 章节标题和日期。
- EPUB 正文与配套 TXT 是否一致。
- 必须保留的文字、禁止泄漏的图内文字和源页面抽样文字。

校验入口始终会调用外部状态模块；未提供 EPUBCheck 路径时只报告降级，不会伪装成通过。发布前用同一个入口显式运行 EPUBCheck 和 `ebooklib`，并把稳定 JSON 报告保存在本书工作目录或用户指定的输出目录：

```powershell
& $PYTHON "$SKILL_DIR\scripts\validate_epub.py" $CONFIG `
  --java $JAVA `
  --epubcheck-jar $EPUBCHECK_JAR `
  --external-report "$WORK_DIR\external-validation.json" `
  --require-epubcheck
```

EPUBCheck 缺失、超时或返回 warning/error/fatal 时不能当作通过。`--require-epubcheck` 是发布门槛：EPUBCheck 和 `ebooklib` 必须同时通过，任一检查跳过、失败或出错都会阻断发布；`ebooklib` 缺失时报告仍明确记为 `skipped`。Calibre、Apple Books 或 Kindle 只属于可选兼容性抽检，不能替代 EPUBCheck。

## 质量门槛

只有同时满足以下条件才算完成：

1. 正文可选择、可搜索、可重排。
2. 阅读内容不使用整页扫描图。
3. 原书中真正的图、表、示意图和照片都以本地裁切图片保留。
4. 坐标轴、图例和表格单元格文字不会散落成正文段落。
5. 视觉内容相邻的正文句子仍可搜索。
6. 导航使用真实的章节或文章标题。
7. 生成图片至少经过两轮视觉检查：先看总览，再单独检查高风险裁切。
8. 当前 EPUB 的标识、修改时间、来源标识和封面声明正确，不泄漏本地路径。
9. 相同输入连续构建的 EPUB SHA-256 一致，配套 TXT 内容不变。
10. 最终重建后的内部校验、EPUBCheck 和第二个独立解析器检查均通过；未执行的工具明确标记为跳过。

除非用户要求删除，否则保留中间页面、OCR 数据、配置、联系表和校验材料。它们是以后修正问题所需的审计记录。

