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解析, 双向文本, 排版还原, 文档处理, 文档抽取, 逆向工具, 阿拉伯语