Skip to content
Sirfetch-dPublic

About

Deterministic, non-LLM PDF-to-Markdown conversion for Chinese standards documents

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

std2md

简体中文 | English

确定性、非 LLM 的中文标准文件 PDF → Markdown 转换工具。

转换器不绑定任何单一标准体系:GB 国家标准、QB/SB 行业标准,以及其他中文标准与 法规类文件,走的都是同一套通用的 PDF、版面与 OCR 处理流程。

日常单个文件转换使用 standard_to_markdown.py;需要质量报告、页码选择或 OCR 控制时使用进阶转换器 pdf_to_markdown_auto.py。

设计原则

  • 转换流程中不使用 LLM。
  • 优先采用通用的 PDF/OCR/版面方法,而不是针对特定文档的配置档。
  • 不为了让测试通过而加入食品专用、特定标准专用或样本专用的词典。
  • 宁可把 OCR 的不确定性暴露在报告里,也不静默地把它变成看似合理的错误文本。

环境要求

1. Python 3.10 或更高版本

本项目以 Python 3.10 作为最低支持版本,类型注解使用了 X | None、list[...] 等较新的写法。已在 Python 3.10.5 与 3.13.7 上完整跑通测试。

python3 --version   # 需要 >= 3.10

2. Tesseract OCR(系统依赖,非 pip 包)

文字层完好的 PDF 不依赖 OCR,但只要出现扫描件、字体映射损坏等情况,转换器就会 回退到 Tesseract。它是命令行程序,必须单独安装,并且必须包含简体中文与 英文语言包(chi_sim 与 eng)。

安装方式:

# macOS (Homebrew)
brew install tesseract tesseract-lang

# Debian / Ubuntu
sudo apt-get update
sudo apt-get install -y tesseract-ocr tesseract-ocr-chi-sim

# Windows:下载 UB Mannheim 版安装包
# https://github.com/UB-Mannheim/tesseract/wiki
# 安装时在 "Additional language data" 中勾选 Chinese (Simplified)

安装后确认语言包齐全——这一步很容易被忽略,缺 chi_sim 会导致中文 OCR 直接失败:

tesseract --version
tesseract --list-langs | grep -E '^(chi_sim|eng)$'
# 期望输出两行:chi_sim 与 eng

3. Python 依赖

全部依赖声明在 requirements.txt:

包 用途 是否必需
PyMuPDF 提供 fitz,负责文字层提取、坐标与表格发现 必需
pytesseract Tesseract 的 Python 封装 必需
Pillow OCR 图像预处理与调试叠加图工具 必需
opencv-python OCR 图像预处理:纠偏、去噪、自适应二值化、表格线检测 可选,建议安装
numpy 同上,与 OpenCV 配合 可选,建议安装
pytest 运行 tests/ 下的回归测试 仅开发需要

关于可选依赖:转换器在运行时检查 cv2 与 numpy 是否存在,缺失时会跳过上述 预处理变体。因此没有它们 OCR 仍能运行,只是少了几层兜底,在扫描质量较差的文件上 识别效果可能变差,tools/eval_ocr_samples.py 的质量门也可能因此不达标。

requirements.txt 使用版本下限而非精确锁版本,因为转换器只依赖 PyMuPDF、 pytesseract、Pillow 的稳定 API。实际验证环境为:全新虚拟环境安装后得到 PyMuPDF 1.28.2 / pytesseract 0.3.13 / Pillow 12.3.0 / opencv-python 5.0.0.93 / numpy 2.2.6,tests/ 全部 77 项通过。

安装(推荐使用虚拟环境)

macOS / Linux

git clone https://github.com/Sirfetch-d/std2md.git
cd std2md

# 1. 创建虚拟环境
python3 -m venv .venv

# 2. 激活虚拟环境
source .venv/bin/activate

# 3. 升级 pip(可选,但建议)
python -m pip install --upgrade pip

# 4. 安装依赖
pip install -r requirements.txt

Windows(PowerShell)

git clone https://github.com/Sirfetch-d/std2md.git
cd std2md

python -m venv .venv
.venv\Scripts\Activate.ps1

python -m pip install --upgrade pip
pip install -r requirements.txt

激活成功后,命令行提示符前会出现 (.venv)。之后所有命令都在该虚拟环境中执行, 退出用 deactivate。

需要跑测试时

pip install -r requirements-dev.txt

开启提交守卫(每个 clone 只需一次)

git config core.hooksPath .githooks

该钩子会拒绝提交源 PDF、.docx 中间产物、生成的 auto-md/ 输出、本地环境文件, 并在安装了 gitleaks 时扫描暂存内容里的凭据。扫描器用 brew install gitleaks 安装;未安装时只保留路径守卫。确需提交的样例文件仍可用 git add -f 加入,单次 提交也可用 git commit --no-verify 绕过,但两者都应在提交信息里说明原因。

需要注意这是兜底而非保证:gitleaks 匹配已知的凭据形态并做熵检测,形状特殊 或低熵的密钥仍可能漏过。推送前请自行过一遍暂存内容。

准备源文件

本仓库不包含任何标准 PDF,Standards/、Laws/、auto-md/、local/ 均被 git 忽略。请自行合法获取源文件并放入 Standards/:

mkdir -p Standards
cp /path/to/"GB 7718-2025 食品安全国家标准 预包装食品标签通则.pdf" Standards/

本文档的示例都使用 Standards/、auto-md/ 这类相对路径,请先 cd 到仓库根目录再执行。 也可以用绝对路径从其它目录调用脚本,例如 python /path/to/std2md/standard_to_markdown.py "/abs/path/file.pdf"。

运行方法

方式一:简单入口(推荐日常使用)

把一个标准 PDF 转成同目录同名的 .md:

python standard_to_markdown.py "Standards/GB 7718-2025 食品安全国家标准 预包装食品标签通则.pdf"

输出:

Standards/GB 7718-2025 ....pdf -> Standards/GB 7718-2025 ....md (text)

括号里是该文件实际走的路径,text 表示文字层,ocr 表示 OCR 回退。

方式二:进阶转换器

带质量报告、自动识别文字层/OCR,并跳过前置页:

python pdf_to_markdown_auto.py --standard --report -o auto-md/standards \
  "Standards/GB 7718-2025 食品安全国家标准 预包装食品标签通则.pdf"

完整转换(不跳过任何页):

python pdf_to_markdown_auto.py --full --report -o auto-md "Standards/example.pdf"

只调试指定页:

python pdf_to_markdown_auto.py --standard --report --pages 4,6 -o auto-md/ocr "Standards/example.pdf"

实验性的慢速 OCR 重试模式(在困难扫描件上多试几种预处理):

python pdf_to_markdown_auto.py --standard --report --ocr-deep-retry -o auto-md/ocr "Standards/example.pdf"

常用参数:

参数 说明
--standard 标准正文模式:跳过第一个「1 范围」页之前的前置页(封面、目次、前言等)
--full 全文模式:包含 PDF 的每一页
--report 在每份输出旁写出 .report.md 质量报告
--pages 调试模式:只转换指定页,1 起算,如 3 或 3,5-7
-o / --out-dir 指定输出目录
--ocr-cache-dir 指定持久化 OCR 缓存目录
--no-ocr-cache 关闭持久化 OCR 缓存
--ocr-deep-retry 对困难扫描页尝试更慢的 OCR 预处理变体

--standard 与 --full 互斥,不指定时默认为 --standard。

需要区分的是:文字层与 OCR 之间的选择才是自动的——转换器先用 text_layer_is_trustworthy 判断该 PDF 的文字层是否可信,可信则走文字层解析, 不可信才回退到 OCR。输出的括号里会标明实际走了哪条路(text 或 ocr)。

方式三:OCR 回归评测

小型 OCR 样本集,附带质量门,指标回退时会以非零状态退出:

python tools/eval_ocr_samples.py

文字层生产基线集,会重跑所有受信任文字层 PDF 的 --standard 与 --full 两种 模式,包含 GB 2760 这类大表格压力样本:

python tools/eval_text_samples.py

需要探索性诊断时可以加 --no-fail 只看汇总而不让命令失败。

注意:这两个脚本依赖本仓库不包含的本地语料。

它们各自内置了一份固定的样本清单,清单里的 PDF 全部位于 Standards/,而本仓库 按设计不提交任何 PDF。脚本遇到缺失文件时直接抛出 FileNotFoundError 退出, 不会跳过该样本。因此刚 clone 下来的仓库直接运行这两个命令会失败,这是预期行为, 不是环境装错了。

  • tools/eval_ocr_samples.py 需要:GB T 31121-2014、QB T 2076-2021、GB T 29602-2013
  • tools/eval_text_samples.py 需要清单内 10 份 GB/GB T 文件,含 GB 2760-2024(约 5244 行表格的压力样本)

要用它们做质量门,请自行准备清单中对应的 PDF 放入 Standards/。如果只想转换自己的 文件,完全可以忽略这两个脚本,直接用「方式一」或「方式二」。

方式四:渲染 OCR 低置信度坐标叠加图

python tools/render_ocr_coordinate_overlay.py auto-md/ocr-eval/example.report.json \
  --pdf "Standards/example.pdf" -o auto-md/debug-overlays/example

输出 PNG 页面叠加图、index.html 与 overlay_summary.json 到指定目录。这类生成物 请一律放在 auto-md/ 下。

输出说明

使用 --report 时,每次转换会写出两个报告文件:

  • .report.md — 人类可读的质量报告
  • .report.json — 结构化的机器可读指标,供 eval_ocr_samples.py 等工具消费

JSON 结构由 tools/quality_metrics.py 中的 QualityMetrics 定义。

OCR 缓存默认写在 auto-md/.ocr-cache/。首次处理扫描件时 OCR 较慢,之后同一页会 命中缓存。缓存键包含转换器版本,因此升级后预期会重新识别。

质量指标

OCR 低置信度坐标

低置信度坐标记录同时包含源坐标与归一化坐标:

  • bbox_raw — PDF 页面坐标,用于溯源调试
  • bbox_norm — 归一化到固定 0..10000 逻辑画布的坐标,使不同页面、不同 PDF 之间的位置可直接比较
  • was_clamped — 归一化坐标越出画布并被夹到边界时为 true
  • clamped_coordinate_count — 被夹取的诊断记录总数

夹取与归一化坐标只是诊断信号,用于指导人工复核、可视化与质量遥测,不得用来 静默修正 OCR 文本。

文字层断裂

文字层报告还包含 text_fracture_issues,用于标记未解决的换行断裂,例如悬空的条款 引用(按4.)、标准号引用(GB5009.)或不完整的条款引用(按4.3.的要求)。 生产环境的文字层基线应保持该值为 0。

跨页表格续接

文字层报告包含以下字段:

  • logical_tables — 安全跨页合并后的 Markdown 表格块数
  • table_continuations_detected — 检测到的续接组(含已合并与明确未解决)
  • table_continuations_merged — 在 Markdown 序列化前合并的兼容邻页表格组
  • table_continuations_unresolved — 结构上合理但因表头不匹配而保留待人工复核的续接
  • repeated_table_headers_removed — 合并片段中被移除的等价重复表头行

合并后的表格会附带一段 HTML 溯源注释,列出其来源页。续接判定综合使用了表格标识、 页边几何、列数与归一化表头。携带行号或条目元信息的表题会作为独立子表边界保留。

参考基线

在文字层模式下(见 AGENTS.md 的回归检查):

  • GB 28050:210 行 Markdown 表格,0 个表宽问题,0 个单元格审计问题
  • GB 7718:13 行 Markdown 表格,0 个表宽问题,0 个单元格审计问题

项目结构

  • tools/ — 评测器、共享辅助模块与诊断工具
  • tests/ — 确定性回归测试
  • standard_to_markdown.py — 简单入口,在源文件旁写出同名 .md
  • pdf_to_markdown_auto.py — 进阶转换器,支持报告、页码选择与 OCR 控制
  • Standards/ — 本地中文标准 PDF 语料,默认被 git 忽略(不提交 PDF)
  • Laws/ — 可选的本地法规类 PDF 语料,同样默认忽略
  • auto-md/ — 生成的 Markdown、报告、OCR 缓存与调试图,永不提交
  • local/baselines/ — 两份手工整理的 Markdown 质量基线,仅本地保留
  • local/manual-intermediates/ — 人工比对过程中产生的 DOCX 与草稿副本
  • local/duplicate-pdfs/ — 不属于规范语料、但需保留的重复源 PDF

开发与测试

改完代码后先做语法校验:

python -m py_compile standard_to_markdown.py pdf_to_markdown_auto.py

跑单元测试:

python -m pytest tests/ -q

改动 OCR 相关逻辑时,先用指定页做定向检查,再跑整份文件:

python pdf_to_markdown_auto.py --standard --report --pages 4,6 -o auto-md/ocr \
  "Standards/GB T 31121-2014 果蔬汁类及其饮料(含第1号修改单)-现行有效.pdf"

判断 OCR 改动时,必须同时看 .report.md 的指标和 .auto.md 的实际内容。 审计计数变低但文本变成"看似合理却是错的",这种情况不算改进。

常见问题

TesseractNotFoundError: tesseract is not installed Tesseract 未安装,或不在 PATH 中。按上文安装后用 tesseract --version 确认。 若装在非标准路径,可通过设置 pytesseract.pytesseract.tesseract_cmd 指定。

OCR 报 Failed loading language 'chi_sim' 缺简体中文语言包。tesseract --list-langs 里必须有 chi_sim。macOS 装 tesseract-lang,Debian/Ubuntu 装 tesseract-ocr-chi-sim。

ModuleNotFoundError: No module named 'tools' 出现在把 pdf_to_markdown_auto.py 单独复制到仓库外运行的时候。该脚本依赖同仓库的 tools/ 包,请连同整个仓库一起使用,不要只拷贝单个脚本文件。

运行 tools/eval_ocr_samples.py 或 tools/eval_text_samples.py 报 FileNotFoundError 预期行为,不是环境问题:这两个脚本使用内置的固定样本清单,而本仓库不提交 PDF。 详见上方「方式三」的说明。只想转换自己的文件时可以忽略它们。

OCR 效果差 / 评测质量门不达标 先确认 opencv-python 与 numpy 已安装(pip show opencv-python numpy), 缺了它们会跳过纠偏、去噪等预处理变体。仍不理想时可加 --ocr-deep-retry 做慢速重试。

扫描件处理很慢 属正常现象:OCR 逐页识别,首次运行还需生成缓存。后续同页会命中 auto-md/.ocr-cache/。调试时优先用 --pages 只跑关键页。

运行时出现 The 'fitz' API is deprecated ... Use 'import pymupdf' instead 这是 PyMuPDF 1.28 及以上版本打印的弃用提示,不影响当前功能,可以忽略。转换器 目前使用 import fitz 这一旧名称以兼容较老的 PyMuPDF。待确认所有受支持版本都 提供 pymupdf 模块名后,会统一切换。

版本控制策略

  • 只跟踪转换器代码与轻量级项目文档。
  • 生成的 Markdown 报告、OCR 缓存、调试裁图、本地基线、本地环境文件与人工中间 产物一律不入库。
  • 标准 PDF 默认作为本地测试语料;确需加入时应当是有意为之。
  • 手工整理的基线与其它本地草稿放在各自的 local/ 子目录下。

许可与免责声明

本仓库只包含工具,不附带任何标准文件。

仓库中不提交 PDF 源文件、不提交转换出的 Markdown,也不提交手工整理的基线。 Standards/、Laws/、auto-md/、local/ 均被 git 忽略,只存在于本地工作副本。

  • GB 国家标准、QB/SB 行业标准及同类法规文本的版权归各自发布机构所有。在中国大陆, GB 标准由中国标准出版社出版。请合法获取源文件,并遵守其附带的使用条款。
  • 请勿提交、上传或再分发转换出的标准正文。转换产物仅供本地个人研究与互操作用途; 是否可以再发布其中任何部分,应由权利人判断,而不是由本工具决定。
  • 本项目与中国国家标准化管理委员会(SAC)、国家卫生健康委员会(NHC)或其他任何 标准机构均无隶属、认可或关联关系。
  • OCR 与版面重建都是启发式的。输出可能包含错误,低置信度计数少也不等于内容正确。 凡是要依赖的内容,请与权威来源文件核对。

许可证

本项目基于 MIT 许可证 发布。

MIT 许可证仅覆盖本仓库的源代码,不覆盖任何标准文件,也不覆盖由标准文件转换 得到的文本。

About

Deterministic, non-LLM PDF-to-Markdown conversion for Chinese standards documents

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages