umerjavaidkh/rtldoc
GitHub: umerjavaidkh/rtldoc
rtldoc 是一个基于矢量字形坐标的几何优先 PDF 解析器,专为阿拉伯语 RTL 复杂版式文档重建正确的阅读顺序与双向文本而设计。
Stars: 0 | Forks: 0
# rtldoc — 针对复杂版式 RTL PDF 的几何优先解析器
专为阿拉伯语教育出版打造:教师指南、学生教材、练习册——
并验证了其泛化能力,不仅限于此,还适用于英文 SEC 文件、政策文档
以及各类报告。对于这类页面,Docling、Marker 和基于 VLM 的解析器都会
返回一些*看起来*像文本但实际上悄悄出错的内容。
## 1. 为什么通用解析器在这类文档上会失败
原生数字化的、RTL(从右向左)复杂版式的 PDF(阿拉伯语教师指南是
其中最难的版本,但同样的失败模式也会发生在任何排版密集的
出版物上)堆叠了通用解析器从未针对其进行调优的问题:
| # | 失败模式 | 通用解析器的做法 |
|---|---|---|
| 1 | **RTL 阅读顺序** | 每个阅读顺序模型——Docling 的、Marker 的、LayoutLMv3 的——绝大部分都是在 arXiv、发票和商业报告上训练的。它们会优先输出左列。而在应当优先输出右列的地方,这会导致文档顺序被悄无声息地完全破坏。 |
| 2 | **文本层中的呈现形式** | ME InDesign 导出时通常会将字形映射到 `U+FE70–FEFF` / `U+FB50–FDFF` 而不是基础字母。直接提取会得到 `ﺍﻟﻬﺪﻑ`——视觉上完全一样,但在词汇层面上毫无用处。Tokenizer、embeddings 和 BM25 全部失效。 |
| 3 | **Lam-alef 连字反转** | 最隐蔽的一个 bug。`لا` 在数据流中是一个字形。如果提取器在 bidi(双向文本)重排序*之前*将其分解,这两个字母就会翻转:`التلاميذ` → `التالميذ`。这就是 [PyMuPDF #2199](https://github.com/pymupdf/PyMuPDF/issues/2199),它影响了大约三分之一的阿拉伯语词汇,并且产生的文本能通过所有的人工抽查,因为它渲染出来的依然是阿拉伯语。 |
| 4 | **一页包含两个文档** | 教师备注列和学生页面传真可能共享同一页面。将它们线性化为一个流,会把答案放错到不相干的问题旁边。基于此构建的 RAG 索引会自信地使用不匹配的键来回答问题。 |
| 5 | **由矢量图承载的语义** | 有色面板代表“阅读段落”;带编号的标签代表“练习 N”,而这个编号是将练习与其答案关键联系起来的唯一要素——它存在于绘制层中,而每个以 Markdown 为优先的解析器都会将其丢弃。 |
| 6 | **无边框的表格结构** | 使用行阴影而不是网格线的财务报表(见下文 META 10-K 第 83 页)在矢量层中根本没有可见的网格——大多数表格结构检测器(包括我们的)都需要*某种*几何特征作为切入点。 |
Docling 在这里失败并不是因为它不好,而是因为它是一个
*通用*工具:它运行布局模型 + 阅读顺序模型 + 表格结构,
这些都是针对某种数据分布调优的,而当前页面与该分布相去甚远。你无法通过微调来解决 #2 和 #3 问题——这些是编码 bug,而不是感知问题。
## 2. 领域现状(2026 年中)
- **端到端 VLM 解析器目前主导着排行榜。** PaddleOCR-VL-1.6 报告称其 [在 Real5-OmniDocBench 上以 0.9B 参数量取得了 93.19% 的整体 SOTA 成绩,击败了 Qwen3-VL-235B 和 Gemini-3 Pro](https://arxiv.org/pdf/2606.03264)。HunyuanOCR 报告称其 [在 OmniDocBench 及其畸变捕获变体上以 1B 参数量取得了最高分](https://arxiv.org/pdf/2511.19575)。
- **但模块化 pipeline 在整体结构上依然胜出。** ABot-OCR 报告对此直言不讳:[结合了专用布局检测和文本识别模块的 pipeline 解析器依然保持着最高的总成绩,代价是需要多阶段编排](https://arxiv.org/pdf/2605.27978)。
- **从业者指南建议按页面类型进行路由,而不是看排行榜。** 一份 2026 年的对比总结道,[公开的入围名单仅用于筛选候选者,你应当在正式推出前进行一次固定的页面类型对比测试](https://instavar.com/blog/ai-production-stack/OCR_SOTA_Feb_2026_Open_Document_AI_Leaderboard)。
- **没有人为阿拉伯语教材做基准测试。** [OmniDocBench](https://github.com/opendatalab/OmniDocBench)——标准的公开基准测试,包含 1,651 页,10 种文档类型——仅涵盖英文、简体中文和英中混合内容。**RTL/阿拉伯语的覆盖率为零。** RTL 教育出版领域在任何公开排行榜上都完全没有代表性,这正是为什么本仓库自带了一套带有金标签的评估集(第 5 节),而不是仅仅引用别人的数据。
实际结论是:**对于原生数字化的语料库,VLM 是一个错误的默认选择。**
它将已经包含完美字形坐标的页面栅格化,在长阿拉伯语段落上
产生约 2–5% 的幻觉,每页的成本高出 100–1000 倍,且无法被
审计。应将其用作*审查者*,而不是*提取器*。
## 3. 对比矩阵
下文比较的是两种不同的事物,并将它们保持明显的
分离,以免夸大任何一方:
- ✅/⚠️/❌ 标记代表**结构性/有据可查的事实**——训练数据
分布、已知的 bug、已发表的局限性。这些并非来自
我们亲自运行竞争工具的结果。
- 第 4 节中的 bug 和第 5 节中的数据是**本仓库实际运行
并验证过的内容**,并展示了具体的输入/输出。这是你
无需盲目相信的部分。
| 能力 | rtldoc | Docling | Marker | 通用 VLM (GPT-4V/Gemini/Claude-vision) | 原生 `page.get_text()` |
|---|:---:|:---:|:---:|:---:|:---:|
| RTL 阅读顺序 | ✅ 基于几何(x 轴递减),不存在可能出错的数据分布 | ❌ 基于 LTR 训练的阅读顺序模型¹ | ❌ 同类模型,相同的训练偏差¹ | ⚠️ 通常正确(读取图像),但不保证,不可审计 | ❌ 信任 PDF 自身的 bidi/生成器顺序,通常是错误的 |
| Lam-alef / 连字完整性 | ✅ 连字在重排序期间保持原子性(`primitives.py`, `TEXT_PRESERVE_LIGATURES`) | ❌ 继承了 PyMuPDF/pdfium 的分解顺序² | ❌ 同上 | ✅ 读取像素,不会遇到此类 bug | ❌ 直接遭遇 PyMuPDF #2199 问题² |
| 呈现形式去变形 | ✅ `arabic.deshape`,仅作用于两个阿拉伯语呈现块 | ⚠️ 取决于后端文本提取 | ⚠️ 取决于后端文本提取 | ✅ | ❌ |
| 基于矢量规则的表格结构 | ✅ 对于面板/标签,**无需任何可见边框**即可工作;✅ 从描边*或*填充的规则线中恢复完整网格(见第 4 节) | ✅ TableFormer,基于模型 | ✅ 基于 Surya,基于模型 | ⚠️ 通常正确,不可审计,按页计费 | ❌ 根本没有结构 |
| **完全没有规则线**的表格结构(仅阴影) | ✅ 通过列对齐检测恢复(第 4a 节)——在 META 第 83 页的无边框季度报表上得到验证;数值密度防护使其不会作用于对齐的*文本* | ⚠️ 基于模型,可能捕捉到也可能捕捉不到——在此未测试 | ⚠️ 同上 | ⚠️ 可能会正确读取(视觉),不可审计 | ❌ |
| 跨列语义链接(练习 ↔ 答案键) | ✅ `_link_activities`,特定于出版商但真实有效 | ❌ 完全没有这种概念 | ❌ | ❌ | ❌ |
| 可审计性(为什么会输出这样的文本) | ✅ 每次转换都是一个命名的、可检查的函数;`rtldoc audit` 标记低置信度页面 | ❌ 黑盒模型推理 | ❌ | ❌ | N/A(它什么也没做) |
| 单页成本(原生数字化) | ✅ ~3-8ms,无需 GPU,无需 API | ⚠️ CPU 可行,有模型推理开销 | ⚠️ 同上 | ❌ 贵 100-1000 倍³ | ✅ 免费 |
| 非 RTL(英文)文档 | ✅ 已在本仓库中验证——见第 5 节 | ✅ 这是他们的主场 | ✅ 同上 | ✅ | ⚠️ 顺序通常没问题,但没有结构 |
| 输出中的图像表示 | ✅ 提取到文件 + 几何距离最近的说明文字作为 alt 文本,通过 PDF xref 去重 | ✅ | ✅ | N/A(它本身就是图像) | ❌ 完全丢弃 |
¹ 此类阅读顺序模型绝大部分是在 arXiv/商业文档语料库上训练的——这是已记录的训练分布偏差,而不是我们通过亲自运行 Docling/Marker 验证出的结论。
² [PyMuPDF issue #2199](https://github.com/pymupdf/PyMuPDF/issues/2199)——在 bidi 重排序之前分解 lam-alef 会翻转这两个字母;这会影响任何建立在相同提取后端上的工具,大约三分之一的阿拉伯语词汇会受此影响。
³ 每页的 VLM 成本以及在长阿拉伯语段落上 2-5% 的幻觉率是第 2 节中引用的标准公开权衡,并非本仓库直接基准测试得出的结果。
**在下面的定量评估中并未运行 Docling 和 Marker。** 仅 Marker 就
需要拉取 torch + transformers + surya-ocr——实际首次运行
需要 2-5GB 空间和 10 多分钟——为了保持
测试工具易于复现,在此过程中特意跳过了它。适配器接口
(`eval/adapters.py`)对每个解析器来说只是单个函数;
添加其中任何一个只需约 15 行代码的 PR,而不是重新设计。
## 4. 在真实文档上测试时发现并修复的 Bug(本仓库,本次会话)
这些是对 5 份真实 PDF(一份 239 页的阿拉伯语教师指南、
两份 SEC 10-K 文件和两份英文报告)运行 rtldoc 得出的
具体、可复现的发现——而不是假设的失败模式。
**这里的每一个问题都是*潜在的、错误地被普遍化的假设*,而不是
一次性的拼写错误**——这一点值得明确指出,因为本次测试的核心目的
就是确保解析器不会暗中只在一本书上有效。
| Bug | 发生位置 | 症状 | 根本原因 | 修复 |
|---|---|---|---|---|
| 整页背景吞噬了页面 | `primitives.py` | 某一页(阿拉伯语,第 88 页)坍缩成了一个混乱的文本块 | 一个全出血的背景色调(大于页面本身)被归类为了内容“面板” | 排除任何面积大于等于页面 60% 的填充——这是一个比例测试,而不是针对特定颜色/书籍的规则 |
| 列间距不可见或产生幻影 | `layout.py` | 双栏页面被报告为单栏(间距被全宽页眉擦除)或 4 栏(列表缩进噪声被误读为间距) | 一维墨水投影忽略了 y 轴的持久性 | 二维检查:只有当间隙在约 96% 的内容高度上都是空的时才算数,而不仅仅是在某个位置存在 |
| 跨列合并段落 | `layout.py` | 教师列和学生列的文本逐行交错 | 段落聚类在确定列之前就已经运行 | 首先按列拆分,然后在每一列内进行聚类 |
| 表格网格不可见 | `primitives.py` | 一个 6×3 的评分标准表格(第 150 页)被渲染成了断开的段落碎片 | `extract_page` 仅捕获了*填充的*矩形;这位出版商将表格边框绘制成了**描边线** | 同时捕获描边类型的绘图;将分割的线段合并为行/列边界 |
| 跨区域文本重复 | `pipeline.py` | 同一段落在一页的输出中出现了两次 | 两个重叠的区域可能各自独立声明拥有同一行重建后的文本 | 在渲染之前,将每一行精确地分配给一个区域(包含度最高的所有者) |
| **由于默认为 RTL 导致英文文本损坏** | `geobidi.py` | 在一个*纯英文*的 10-K 页面上:行尾的 `(MAUs),` 变成了 `,( ... (MAUs` 出现在了*下一*行的开头 | bidi 重建首先将每一行按从右到左的顺序排序,而一个“未定”的中性字符(没有*下一个邻居参与扫描的行尾标点)默认为了“R”——即整个模块暗中假设每个文档都是 RTL 的 | 在应用任何 RTL 特定的重排序*之前*,检查每一行是否确实包含 RTL 字符;纯 LTR 行只需升序排序 |
| 审计启发式算法误报普通散文 | `pipeline.py` | 一份英文 10-K 的审查率达到了 37%——远高于其他任何文档 | `needs_review` 要求每页有 ≥2 个文本块;合法的单段落散文页面触发了它 | 根据提取出的总字符数进行评分,而不是块数量 |
| 图像被悄然丢弃 | `pipeline.py` | 阿拉伯语页面上的某张照片在 Markdown 输出中的任何地方都没有表示 | `to_markdown` 跳过了任何文本为空的块,而图形从来就没有文本 | 提取到文件(通过 PDF xref 去重,因此页面中重复的 logo 不会被保存 N 次),附加几何距离最近的块作为 alt 文本说明,即使在跳过提取时也始终输出一个占位符 |
这里的每一个问题都可以独立检查:确切的修改前后文本
存在于生成本仓库的对话历史记录中,而修复内容
就在带编号的提交记录中。
### 4a. 无边框表格恢复(之前评测中最难的缺口,现已填补)
评测人员指出的那个本应由“轻量级并行解析器”覆盖的缺口——无边框表格(使用阴影或空格来分隔列、没有绘制网格线的财务报表)——结果发现*无法*通过路由到另一个工具来解决:在 META 10-K 第 83 页上运行 pdfplumber 的文本对齐表格模式,将整个页面(包括散文)撕碎成了一个 77×16 的网格,从单词中间将其截断。因此,改为在 rtldoc 内部进行修复(`layout.detect_borderless_tables`),其依据是唯一明确的信号:
- **跨行重复出现的列对齐**——短单元格的右边缘
聚类成列,并且只有当有 ≥3 行支持时,该列才算数,因此
换行的散文行永远不可能凭空发明一列;
- **数值多数防护**——对齐的单元格必须主要是
数值型的,这正是将数据表与阿拉伯语选择题选项、
答案键或仅仅是碰巧排成一线的双栏列表区分开来的关键;
- **网格填充防护**——网格必须实际上是被*填充过的*,这可以拒绝
编号练习或图像版权页面上碰巧在
几个地方对齐的杂乱数字。
已在整个语料库中进行了验证:在以前提取为散文碎片的约 27 个 GOOGL 页面和约 20 个 META 页面上恢复了真实的表格,在 **零** 个
阿拉伯语页面上触发(最初的两个误报——一个练习和一个
版权页——正是添加数值和填充防护旨在
拒绝的内容),并保持所有 239 个阿拉伯语页面的文本内容在字节上完全一致。
## 5. 实测性能(5 份真实文档,本仓库)
| 文档 | 页数 | 耗时 | 页数/秒 | 图像(去重后) | 审计复查率 |
|---|---:|---:|---:|---:|---:|
| 阿拉伯语教师指南 (`BilArabi_TG07.pdf`) | 239 | 23.5s | 10.2 | 415 | 2.1% |
| GOOGL 10-K(2016 财年) | 162 | 2.7s | 60.0 | 1 | 3.7% |
| META 10-K(2017 财年) | 144 | 5.5s | 26.2 | 5 | 8.3% |
| 公司合规政策 (`rag_document.pdf`) | 12 | 0.74s | 16.2 | 36 | 8.3% |
| WHO 报告 (`rag_document_2.pdf`) | 52 | 8.6s | 6.0 | 31 | 7.7% |
没有 GPU,没有外部 API 调用,单个 CPU 核心大部分时间处于空闲状态。“审计
复查率”是 `rtldoc audit` 自身的置信度标记(第 7 节)——即它认为
应该再看一眼的页面比例,而不是外部的质量评分。
这些数字比本仓库的最初版本快了约 10–25%:性能分析
显示每页约 70% 的时间花在了 PyMuPDF 的原生文本提取上,每页被调用了
**两次**(调用 `get_text("dict")` 获取 spans + 调用 `get_text("rawdict")` 获取
逐字符的 bidi)。由于 rawdict 是 dict 的严格超集,现在一次提取
即可同时满足两者——在上述五份文档的全部 609 页中验证了输出字节完全一致。值得记住的教训是:瓶颈在于
原生调用次数,而不在于任何 Python 层面的布局/bidi 循环,这些循环
合计起来占用的运行时间还不到 10%。优化 profiler 指出的
地方,而不是看起来开销很大的地方。
## 6. 设计,以及为什么每个选择都是正确的
```
┌──────────────────────────────────────────────────────────────┐
│ 0 TRIAGE text-layer density → born-digital | scan │
├──────────────────────────────────────────────────────────────┤
│ 1 PRIMITIVES glyphs+bbox+font+colour | vector fills | │
│ (primitives.py) placed images ← lossless, no pixels │
├──────────────────────────────────────────────────────────────┤
│ 2a REGIONS panels, chips, and table grids read off │
│ (layout.py) the DRAWING layer at exact coordinates │
│ 2b CV FALLBACK HSV tint segmentation + RLSA smearing │
│ (cvfallback.py) only when the author drew nothing │
├──────────────────────────────────────────────────────────────┤
│ 3 READING ORDER RTL recursive XY-cut: vertical cuts │
│ (layout.py) ordered x-DESCENDING, column-gutter-aware │
├──────────────────────────────────────────────────────────────┤
│ 4 GEOMETRIC BIDI rebuild logical order from glyph x-coords │
│ (geobidi.py) ← per-line RTL/LTR detection, not assumed │
├──────────────────────────────────────────────────────────────┤
│ 5 ARABIC REPAIR deshape · ligature-safe · mirror · harakat│
│ (arabic.py) │
├──────────────────────────────────────────────────────────────┤
│ 6 SEMANTICS style-signature map (label once/series) │
│ (pipeline.py) + cross-column activity linking │
│ + figure extraction & geometric captions │
├──────────────────────────────────────────────────────────────┤
│ 7 AUDIT per-page confidence → route ~3% to a VLM │
└──────────────────────────────────────────────────────────────┘
```
### 核心支撑理念:根据几何结构逐行重建 bidi
每个解析器都是使用关于生成者意图的启发式方法,从*字符流*(一个
为渲染而非阅读编写的流)中恢复逻辑顺序的。
PyMuPDF 应用它自己的 bidi 处理过程。pdfium 则完全没有。两者都在猜测。
字形坐标不是猜测。如果一个字形在同一
基线上位于更靠右的位置,那么在阿拉伯语中它就会排在前面。因此 `geobidi.py` 抛弃了字符串:
1. 将字形分组到基线中
2. **检查该基线是否确实包含任何 RTL 字符**——
纯 LTR 行按升序排序并保持原样;这是第 4 节中的修复,
确保整个模块在非阿拉伯语文档上也能保持正确
3. 对于 RTL 行:按 **x 轴降序** 排序 → 轻松获得逻辑上的阿拉伯语顺序
4. 找到最大长度的 LTR 连续片段(拉丁字母、数字)并将它们重新按升序排序 → 正确的
bidi L1/L2 解析,这就是修复 `العالم2021 في` → `العام 2021 شهدت` 的关键
5. 按位置解析中性字符(bidi 规则 N1)并镜像括号(规则 L4)
6. 根据测量到的字形间距插入断行符,而不是流中的空格
此过程约 3ms/页,具有确定性,无需模型,无需字典,并且
在同一语料库中的 RTL 和 LTR 文档上均能保持正确——它是根据
页面上实际存在的内容决定方向,而不是基于对书籍的假设。
### 使用样式签名代替布局模型
一本教材*系列*大约有十几种 `(字体, 大小, 颜色, 粗细)`
组合。运行一次 `rtldoc styles`,在 JSON 文件中为这十二种组合打上标签,然后
该系列中的每一页都会以确定性的方式进行排版:
- 在被标记的系列上准确率约为 100%,而布局模型约为 92%
- 零推理成本,零 GPU
- 完全可审计——你可以指出触发的规则
- 20 分钟的人工标注工作分摊到 500 多页上
这之所以有效,是因为出版语料库是同质的。对于异构的网络 PDF,这将是一个糟糕的
主意。但在当前情况下,这是正确的选择。
### 输出单元是一个 *Activity*,而不是一个页面
`_link_activities` 按照阅读顺序沿着其所在的列向下传播每个带编号的标签,将练习 + 评分标准 + 答案键配对为一个可检索的对象——这是唯一能让课程规划或辅导 RAG 真正发挥作用的分块方式,而且
这是任何通用解析器都无法产生的,因为这种链接是
跨列的且特定于出版商的。
## 7. 评估测试工具
`eval/` 是一个从头开始构建的对比工具,而不是对其他人
数据的包装:
- `eval/metrics.py` — 归一化编辑距离(文本保真度)、阅读顺序
评分(基于内容匹配的贪婪算法,而不是 ID 匹配,因此不需要两个解析器
以相同的方式对页面进行分段),以及表格单元格匹配评分
- `eval/adapters.py` — 每个解析器对应一个函数(`naive_pymupdf`, `pdfplumber`,
`rtldoc`);注册一个新解析器只需向 `ADAPTERS` 中添加一个函数
- `eval/gold/gold.json` — 人工标注的金标准页面,恰好涵盖了
第 1 节和第 4 节中描述的情况:阿拉伯语单栏、阿拉伯语双栏、阿拉伯语
带边框表格、英文散文、英文带边框表格、英文
**无边框**表格(现已恢复,第 4a 节)、目录页、图形/说明文字页
- `eval/gold/images/*.png` — 用于标注的渲染参考图像(被 gitignored 忽略,见第 9 节)
```
python eval/run_matrix.py eval/gold/gold.json
```
**状态:金标准文本的转录正在进行中,尚未完成。** 未标注
的页面会被跳过,而不会被记为失败——目前的矩阵输出是
设计上尚不完整的。数据将在标注完成后添加至此,而
不是提前发布后再撤回。
## 8. 已知的局限性(坦诚以待,尚未修复)
- **无边框表格**现在*确实能*(第 4a 节)通过列对齐检测到,但
推断出的行/列边界并不是完美像素级对齐的:多行单元格
偶尔会合并两个源行,并且纯数值触发的机制意味着
纯*文本*的无边框表格(无数字)仍然会被遗漏。数据
能被恢复并正确关联到列;但网格几何结构是
近似的。
- **孤立的变音符号**:某些 PDF 字体会将 harakat 字形作为其
自身微小的独立 span 发出,且未与其基础字母连接;这些在少数
页面上会显示为单字符的噪声块。
- **尚未接入 OCR/VLM 回退机制**,对于 `audit()`
标记的大约 3-8% 的页面——路由 hook 存在,但第二轮模型调用尚未接入。
- **样式映射是针对每个出版商的,这是设计使然**(第 6 节)——不同的系列
需要自己标记的 `styles.json`,而不是新的代码,但该标注
步骤是一项真实存在的、非零的成本。
- **Docling/Marker 不在定量矩阵中**——在第 3 节中进行了
架构层面的推理,并未在此进行面对面的基准测试(成本权衡,
见第 3 节的脚注)。
## 9. 用法
```
pip install -e .
# 将你自己的 PDF 放入 book/ —— 不会提交,见 .gitignore
# 1. 对书籍样本中的样式进行普查,每个系列一次
rtldoc styles book.pdf --pages 80-100 --out styles.json
# → 编辑 styles.json,将每个 signature 映射到一个 role:
# "MyriadArabic-Bold|12.0|1a2f5c|B": "passage_title"
# 2. 解析
rtldoc parse book.pdf --style-map styles.json --json out.json --md pages/
# 图像被提取到 pages/images/,并通过 PDF xref 去重
# 3. 找到需要人工或 VLM 的页面
rtldoc audit book.pdf | jq '.review_rate, .pages[] | select(.needs_review)'
```
Python:
```
from rtldoc.pipeline import parse_document, to_markdown, save_images
results = parse_document("book.pdf", style_map=json.load(open("styles.json")))
```
**关于 `book/` 和 `eval/gold/images/` 的说明**:两者均被 gitignored。用于
开发和验证此解析器的测试 PDF(一本商业阿拉伯语教材、
SEC 文件、一份公司政策文件、一份 WHO 报告)均为第三方
受版权保护的材料,仅在本地用于测试,并未在此
仓库中重新分发。将你自己的 PDF 放入 `book/` 中即可运行上述所有内容。
### Docker
无需设置 Python 环境,不会产生任何冲突:rtldoc 的实际
运行时依赖仅仅是 **PyMuPDF 和 numpy**——两者都提供了预构建的
wheels,因此该镜像不需要编译器,也完全不需要系统库。
`opencv-python-headless`(仅用于尚未接入的扫描页面回退)和
`arabic-reshaper`/`python-bidi`(仅用于本地测试固定程序
生成器)是*本仓库其他部分*的实际依赖项,但 CLI
本身并不导入它们——因此它们是 `pip install rtldoc[cv]` /
`rtldoc[dev]` 的附加包,并未内置在镜像中。
```
docker build -t rtldoc:light .
```
本地验证(`docker images`)为 **399MB**——主要是 `python:3.12-slim`
基础镜像本身。与 Docling 或 Marker 镜像相比,
后者在你的代码运行之前就已经包含了 torch 和模型权重。
使用卷挂载针对你自己的 PDF 运行它:
```
docker run --rm \
-v "$(pwd)/book:/data:ro" \
-v "$(pwd)/out:/out" \
rtldoc:light parse /data/yourfile.pdf --md /out/pages --json /out/book.json
```
`ENTRYPOINT` 是 `rtldoc`,因此任何子命令的工作方式都是相同的:
`docker run --rm -v "$(pwd)/book:/data:ro" rtldoc:light audit /data/yourfile.pdf`。
在编写本节之前,已在容器内针对阿拉伯语 PDF 和英文
10-K 进行了端到端验证——而不仅仅是一个看起来没问题的 Dockerfile。
## 10. 路线图
**近期**
- 为约 3-8% 被标记的页面接入 OCR/VLM 审计回退机制,使用
矢量提取的文本作为约束,以便模型进行纠正而不是重新阅读
- 收紧无边框表格的网格几何(多行单元格合并;从
数值型表格扩展到纯文本的无边框表格)——检测本身
现已发布,见第 4a 节
- 完成金标准文本转录并发布完整的评估数据
- 样式映射学习:对整本书中的签名进行聚类并提出
标签建议,这样人工只需要确认而无需亲自输入
**中期**
- 支持 Harakat 感知的 CER,因为在
教学语料库中元音错误比字母错误更重要,而标准的 CER 会将其掩盖
- 将标注后的数据集作为 `arabic-textbook-bench` 发布——它目前并不存在,
海湾地区的教育科技市场需要它,而 OmniDocBench 自身的覆盖缺口(第 2 节)
证实了没有其他人填补这一空白
**值得考虑**
- `Region` 接口特意设计为与出版商无关。对于第二家出版商,
应该只需要一个新的样式映射,而不需要新代码。如果它需要新的代码,那么这个
抽象就是错误的,应该到那时再修复,而不是现在。
标签:PDF解析, 双向文本, 排版还原, 文档处理, 文档抽取, 逆向工具, 阿拉伯语