name: PDF大师:一键转Word/Excel
slug: pdf-to-detail-word
displayName: PDF大师:一键转Word/Excel
version: 1.1.9
summary: 一键把 PDF 转成可编辑的 Word/Excel:图表重建为可改数据的原生图表、扫描件 OCR、表格数据可反查核验;核心转换全程本地零配置,支持文本/扫描/图文混排/混合横竖版四类 PDF,附加解密/表单/签章/检索工具。
description: "将 PDF 一键转换为可编辑、保留细节的 Word/Excel 文档,并内置按需检索与反查核验的闭环。当用户请求「把 PDF 转成 Word」「PDF 转生产级细节文档」「把这份 PDF 还原成可编辑的详细 Word」「从 PDF 抽取结构化内容并生成带页码引用的 Word」「PDF 转 Excel」,以及「PDF 加解密 / 填表单 / 电子签章 / 在 PDF 中检索」时使用。支持原生文本 PDF、扫描件 PDF、图文混排 PDF、混合横竖版 PDF 四种类型,覆盖结构还原、表格抽取、图表多模态理解、语义切片、双路索引与可追溯引用。此外内置 MarkItDown 通道,可将 Word/PPT/Excel/图片(OCR)/音频/HTML/网页URL 等非 PDF 源快速转为 Markdown(LLM 友好、非像素级还原),并支持任意文档快速纯文本/Markdown 提取。一份 PDF 同时产出 Word(改字排版)+ Excel(算数汇总)双版本,打工人一次搞定。"
license: MIT
author: 薛定谔的脚本
tags:
- pdf
- word
- excel
- docx
- xlsx
- 文档转换
- OCR
- 检索增强
- 加解密
- 签章
- 发票
- 报表
- 合同
requires:
python: ">=3.10"
packages:
- pypdf
- pdfplumber
- python-docx
- openpyxl
- Pillow
- pymupdf
optional:
- sentence-transformers
- pytesseract
- pdf2image
- endesive
- markitdown
- pywin32
metadata:
author: 薛定谔的脚本
slug: pdf-to-detail-word
version: "1.1.9"
changelog: |
1.1.9 (2026-10-24) 文档措辞优化(无功能变更):
- VLM 可选增强的配置说明措辞中性化:鉴权凭据统一表述为「凭据走环境变量」,变量名指引见脚本
- displayName 与简介通俗化:卖点前置,去除「生产级」表述,tags 增加场景词
- 纯文档改动,转换逻辑、脚本与 v1.1.8 完全一致,无需重复回归
1.4.0 (2026-10-04) 精简重构(性能 + 死代码 + 异常可见化,回归全绿、准确率零下降):
- 解析复用(T1,性能核心):convert_to_docx.py 里 8 处各自 pdfplumber.open
收敛为「单次打开 + 页对象/抽取结果复用」(_pdf_scope 引用计数 + pin_pdf
全程钉住 + ctx.text()/ctx.tables() 单页二级缓存);页方向/尺寸改为
_page_geometry() 一次读成纯数据,_apply_section_orientation 不再为设尺寸
重开 PDF;页眉/页脚构建完即 close_pdf() 释放页对象,避免与 python-docx
的文档树+图片内存叠加。实测(同机同 harness 交替 A/B,各 3 次中位数):
47 页文本型 PDF 49.6s→42.4s(-14.5%)、峰值内存 222.4→207.4MB(-6.7%);
27 页图文混排 67.9s→52.0s(-23.4%);预览改用内存 Document 复用后
峰值内存再降 ~35MB(272.8→248.0MB,与基线同 harness)。
- 死代码/重复逻辑(T2):删从未被调用的 _add_page_ref() 与专用常量
PAGEREF;rFonts 写入合并为单一 helper _set_run_rfonts();
_parse_rgb/color_repr 边界分支统一到 _rgb01()(0..255 整数色不再溢出);
convert_to_docx.py 不再依赖 pypdf(pdf_crypto.py 仍需要,pypdf 保留 core)。
- 异常可见化(T3):extract_logo / _build_header logo 写入 / 页眉表格合并 /
figures_desc 解析 / OCR 识别 / 信息图判定等静默 except: pass 全部改为
warn() 进 REPORT;retrieve.py 索引缺失给可操作报错、向量路降级分支可见化
(deps_missing / bm25_only 语义不变)。
- COM 重试统一(T3):word_com_charts.py 新增 com_retry 装饰器,统一覆盖
CALL_REJECTED / 图表数据网格占用 / 文档锁文件三类瞬态失败;AddChart2 重试前
与每张图写完数据后显式收起数据网格(兼容模式下 cd.Activate() 不总稳定接管
的残留分支)。
- 依赖分层(T4):requirements.txt 分 core / optional / dev(reportlab、psutil
入 dev);SKILL.md frontmatter requires 同步分层。
- 目录与文档(T5/T6):scripts/*.bak-v1.1.6 移至 包外归档目录/;
.gitignore 补齐 5 个 *_out/ 回归产物目录;SKILL.md/pipeline.md 与实现同步。
- 1.4.0 轮次2(2026-10-04,同版本号内的交付修正):① 修 PDF 页缓存重构
遗留的调用侧未连通问题(_page_text/_page_tables 已并入 _PdfCtx,调用点
统一改 ctx.text()/ctx.tables();_PdfCtx 实例属性与同名方法冲突已加 _ 前缀规避);
② 颜色行为锁死:1 元组灰度(如 (0.349,))恢复 v1.3.2 语义——
color_repr() 返回不可解析串 → 下游 _parse_rgb 不匹配 → 按黑处理,
不得展开成 595959 灰(铁律 6:重构不得改变任何既有输出)。
新增 闭环回归脚本(未随本包发布) [8] 颜色对照 5 项断言 + 冻结期望分布
{000000:5, CC0000:1, FF3319:1},与真实基线脚本逐字节全等;日后要
"正确还原灰度"须另立 feature 单;③ 边框写入合并为单一 _set_table_borders(table, edges, ...)。
- 1.4.0 轮次4(2026-10-05,行数收官):注释/docstring 压至要点、结构去重后,
交付件行数回到基线:convert_to_docx.py 1500→1483(-17)、word_com_charts.py
541→541(±0)、retrieve.py 136→153(+17,T3 告警)——交付件小计 净增 0;
5 套回归 + 颜色对照复跑全绿(闭环回归脚本(未随本包发布) PASS 78/78)。
1.3.2 (2026-10-03) 半导体人力报告 Word「跟 PDF 差距大」诊断修复(53 页源 → 329 页产物,
四个根因逐一定位修复,终版 64 页 + 7 张原生图):
- 页脚误判膨胀(最大根因):_build_footer 把所有页底部 7% 文字无差别收集进 Word footer,
设计版式页(信息图)底部的装饰文字/数字导航被当成"每页页脚",footer 高达大半页,
正文区被挤压 → 页数成倍膨胀。修复:真页脚特征=同样文字在多数页重复出现,
新增「出现页数 >= max(3, 总页数30%)(小文档>=2、单页退回旧行为)」+「单行 >100 字符排除」
双约束;无真页脚时回退 PAGE 页码域(右对齐)。
- 装饰小图放大:62-95px 的列表图标/色块(118 图中 102 张)被统一放大 14cm 插入。
修复:图片块记录源 PDF 显示宽高(disp_w_pt/disp_h_pt),宽高均 <100pt 的装饰图跳过,
真图表按源显示宽换算 cm(pt2.54/72,上限 14.5cm),不再一律 14cm。
- 扫描页整页图跨页翻倍:14cm 宽 A4 整页图(高约 19.8cm)+ 默认 1.15 倍行距 + 段距 +
[P.X] 标记段超出一页可用高度,图被推到下一页 → 每个扫描页裂成两页。
修复:整页图收窄 12cm + 图片段落单倍行距 + 零段距,标记段压缩,同页稳定。
- 信息图页碎片化:设计版式页(环形图+数据标签)的短标签文字(8.2%、500-1000 人)
被抽取散排成竖排碎片。新增 _is_infographic_page():行数>=6 且行均长<12 且短行(<=15字)
占比>0.85 且 find_tables<=2 且含图 → 整页渲染图化(保真优先);
TOP100 类真表格页(每页 9+ 张真表)正确排除,制度/合同类长句文档永不触发。
- 页眉 logo 误抓封面图:extract_logo 取首页第一张 >20x20 图,封面页抓到全页背景图
(正方形 1546x1687),每页页眉顶着模糊封面缩略。修复:只选横条形图(宽高比>=1.2)。
- 实战配套:修复版引擎重转 + word_com_charts 按题注锚点("图 X-Y(来源:PDF 第 X 页)")
重挂 7 张核心原生图(信息图化吸收的 7 张在 Excel 有原生版对应)。
1.3.1 (2026-10-02) 用户强化指令「源 PDF 有图表,转出的 Excel 也必须要有」:
- Excel 输出规范新增图表还原铁律:源 PDF 出现图表(柱/条/折线/饼/散点等,背后是表格数据的),
转出 Excel 必须重建为 openpyxl 原生可编辑图表对象,不能只留数据表或静态截图;静态截图仅作兜底。
- 校验闭环补一条:重开 xlsx 确认 _charts 非空且系列名/数据引用正确(防「漏挂/只贴图」)。
1.3.0 (2026-10-02) 用户指令:并入 Word COM 原生图表替换路线(半导体行业人力报告 16/16
原生图实战收官沉淀):
- scripts/word_com_charts.py:锚点定位 docx 里已有静态图 → InlineShapes.AddChart2 原生图
→ COM 写入数据 → 显式系列重建,全 XlChartType(柱/条/折线/饼),含失败回填原图、
保存降级链、核对版 PDF 导出(ExportAsFixedFormat 17)
- 最大根因沉淀:AddChart2 默认仅 3 系列,ListObject 扩列后图表不会自动跟随扩展
(取 SeriesCollection(4) 报"参数无效")→ 写完数据必须显式重建系列
(改 Values/XValues 公式 + NewSeries 补足),4-5+ 系列一次通过
- 图表数据网格状态机(插入后必须立即 Activate 接管)/ zip 句柄泄漏 / 锁文件三件套
/ CALL_REJECTED 重试,全部内建自动处理
- 回归基线 COM 图表回归脚本(未随本包发布)(19 项:5/4 系列重建断言覆盖主坑;无 Word 自动 SKIP)
1.2.0 (2026-09-30) 用户指令:新增原生可编辑图表注入(吸收已删除技能 已删除的图表技能 的核心能力):
- Word:scripts/native_charts.py 手写 DrawingML 注入簇状柱形/折线图——chart part 与嵌入数据
工作簿必须 zip 落盘、[Content_Types] 注册 Override、catAx/valAx + strCache/numCache 缓存
(渲染空白三大根因),占位段落正则锚定替换;c:dispBlanksAs=gap 支持 None 缺口
- Excel:openpyxl 原生 Bar/Line 图表配方(非连续列逐列 Series + set_categories + numFmt)
- 回归基线 原生图表回归脚本(未随本包发布)(冒烟首跑即抓到嵌入簿 </c:r> 闭合标签真 bug——图表能渲染
但 Word「编辑数据」会打不开,教训:嵌入簿必须 openpyxl 重开验证)
- 修复「相关技能」对已删除技能的悬空引用
1.1.7 (2026-09-29) 对抗性测评闭环修复(5 个真实缺陷,3 个 P0):
- 修页首 10% 盲区致正文首段整段静默消失;首屏内容大表被误判为信笺头搬进页眉
- 修首行含合并单元格的表令转换直接崩溃(IndexError);文档标题被搬进页眉每页重复
- 修单级编号(1、/1.)正文条款误判为 H1;回归基线 60→73 项,独立对抗套件 66 项全绿
1.1.6 (2026-09-22) 修 OCR 可用性假阳性:额外探测系统 tesseract 二进制,缺失时诚实报「OCR 不可用」并走仅渲染图片降级
1.1.5 (2026-09-21) 版本对齐补记(无功能变更,补齐 CHANGELOG 1.1.3–1.1.5 断层)
1.1.4 (2026-09-21) 用户明确指令固化的铁律——合并表格保真:
- 源 PDF 表格是合并单元格的,产出的 Word 与 Excel 都必须还原同样的合并结构,与源 PDF 一致
(Word 用 w:gridSpan / w:vMerge;Excel 用 merge_cells)
- 表头一律用源文原文:不翻译、不改写、不擅自拆列/改列名(含源自带拼写错误也原样保留)
- 校验闭环:Word 直解析 OOXML 逐行累加 gridSpan 核对;Excel 重开核对 merged_cells.ranges
1.1.3 (2026-09-21) TRACE 测评后优化:
- 新增结构复用:convert_to_docx.py --structure-out 落盘 {typography,structure} JSON,
build_index.py --structure-in 直接复用,免去「闭环必须双跑」的 2× 解析开销
- 结构文件缺失/损坏自动回退重新抽取,不阻断主流程(TRACE 评测 E 维度优化点 P1)
- 修正 references/pipeline.md §9.2 工具脚本 CLI 签名(原 --in/--out/--query 与脚本真实 positional 接口不符)
1.1.2 (2026-09-18) 收尾 P2 尾巴("全部再检查并闭环"):
- 修混合横竖版分节切回:原只在"方向≠首页方向"时开节,横版后回竖版被锁在横版节;现为跟踪"上一节方向",凡方向变化(含切回)都开新节并设正确尺寸/方向
- 删 build_docx 中 typo['title'] 死代码(赋值未用)
- 澄清 Word 图片描述:add_picture(width=Cm(14)) 只设宽、python-docx 自动保比例,不变形(此前文档误述为"不保纵横比")
- 闭环回归脚本(未随本包发布) 新增 [6] 混合横竖版 3 页 P/L/P 分节回归
1.1.1 (2026-09-18) 闭环验证挖出的 3 个真缺陷:
- 修标题误判:编号开头的正文段落(如「1.1 为规范公司…特制定本制度。」)不再被当成标题
- 修取色:char['color'] 为 None 时回退 non_stroking_color(红批注/蓝链接原色保留真正生效)
- 修 build_chunks:跨页状态不再丢失;标题文本并入正文,标题关键词可被检索命中
- 新增 scripts/闭环回归脚本(未随本包发布)(54 项内容级核验)
1.1.0 (2026-09-18) 合并 原 PDF 工具技能:
- 吸收 4 个工具脚本:pdf_crypto / pdf_form / pdf_sign / pdf_search
- 修复 verify.py 解包崩溃(load 返 4 值接 3 值)并接上真正的双路 RRF 融合
- 修正 pipeline.md 三处文档/实现漂移(页脚域、未实现的续表合并、页码引用口径)
- frontmatter 补齐 slug/changelog/requires
1.0.3 支持 Excel (.xlsx) 输出
1.0.2 修复信笺头重复进正文、混合横竖版崩溃
compatibility: "需要 Python 3.10+, 支持 WorkBuddy / OpenClaw / Claude Code 等 AI Agent 平台运行。"
PDF转Excel和Word
Overview
构建从 PDF 到 Word 的完整自动化转换流水线,覆盖按需检索、精准读页、复杂表格抽取、多模态图像理解及反查核验的全闭环流程。最终产物是一份结构完整、页码可追溯、可二次编辑的生产级 Word 文档;同时产出带 metadata 的语义切片与双路索引,供检索、问答与闭环评测复用。此外附带 PDF 加解密、表单填写、电子签章、单文件 IR 检索四项工具能力。
本技能为自包含引擎:底层转换逻辑全部在 scripts/ 下的 Python 脚本实现
(基于 pypdf / pdfplumber / python-docx / openpyxl / Pillow / pymupdf),不依赖其他技能,可独立运行。
多模态图表理解使用内置视觉能力(将图表区域渲染为图片后用 Read 工具读取分析)。
输出格式:默认 Word (.docx);同时支持 Excel (.xlsx) 输出(v1.0.3+),适用于发票/装箱单/报表等结构化数据文档。
> ⚠️ 安全提示(仅处理可信 PDF)
> 1. pdfplumber / pypdf / pymupdf 均需解析 PDF 内部对象结构,历史上出现过解析类漏洞。仅用于处理来源可信的 PDF;来源不明的外部 PDF 建议先在隔离环境预处理。
> 2. 图表 VLM 理解需要你自配 PDF2DOCX_VLM_ENDPOINT。只填自有内网或可信服务端点,不要填来源不明的第三方地址(避免把文档内容外发给不受控服务)。凭据走环境变量,不要写进脚本或提交到仓库。
何时使用
- 用户给出一份 PDF 并要求转成 Word / 可编辑文档。
- 用户强调「保留细节」「保留页码/引用」「生产级」「可追溯到原页」。
- 文档含复杂表格、图表、脚注、页眉页脚,普通复制粘贴会丢结构。
- 用户后续会对该文档做检索、问答或质量核验(闭环评测)。
- 用户需要对 PDF 做加解密、填表单、电子签章,或在单个 PDF 内做关键词检索(补充能力,见下文)。
准备(依赖)
在任意 Python 3.10+ 环境中安装依赖:
# 一键安装(core + optional + dev 分层见 requirements.txt):
pip install -r requirements.txt
# 或按需分装 —— core(主流程 + PDF 工具四件套):
pip install pdfplumber pypdf python-docx openpyxl Pillow pymupdf
# 扫描件 OCR 可选:
pip install pytesseract pdf2image # 另需系统安装 tesseract 与 poppler
# 双路索引的向量召回可选(缺失则降级 BM25):
pip install sentence-transformers
# 电子签章的可选 PKI 数字签名:
pip install endesive
若 OCR 依赖缺失,扫描件走「图片提取 + 视觉理解」降级路径,不阻塞主流程。
运行环境(Windows / 中文文档必须)
先设 PYTHONUTF8=1 再跑任何脚本——脚本里大量中文日志/路径/正则,默认 GBK 解释器下
print 与 subprocess 编解码会报 UnicodeEncodeError,或把中文写进报告时出现乱码:
$env:PYTHONUTF8=1 # PowerShell(当前会话)
set PYTHONUTF8=1 # cmd
python scripts\convert_to_docx.py <in.pdf> <out.docx>
回归脚本(回归脚本 / 闭环回归脚本(未随本包发布))同样需要该环境变量,否则断言输出会乱码。
解释器:Python 3.10 及以上(与 frontmatter requires.python 一致)。
执行流程
Step 1:识别 PDF 类型
对每一页判定类型,整本取主导类型,逐页标注:
| 类型 | 判定依据 | 处理方式 |
|---|---|---|
| 原生文本 PDF | page.extract_text() 有效字符数充足(>50/页) |
直接结构化抽取文本与表格 |
| 扫描件 PDF | 每页文本量极低,但存在整页位图 | OCR 识别;OCR 不可用则整页渲染为图,交视觉理解 |
| 图文混排 PDF | 既有文本又有图表/图片块 | 文本走抽取,图表/复杂表格走视觉理解 |
判定逻辑见 scripts/convert_to_docx.py 的 detect_pdf_type()。
Step 2:还原文档结构
完整保留以下原始结构,映射到 Word 对应对象:
- 页码:每个 chunk 在
chunks.jsonl中标注来源页page字段;正文默认不再注入[P.X]内联标记(做 1:1 忠实副本),页级追溯由chunks.jsonl/verify.py承担。 - 标题层级:以编号结构为主识别 H1–H4(
1、/第X章→H1;1.1→H2;1.1.1→H3;1.1.1.1→H4),字号/加粗仅辅助。兼容「平级字号 PDF」(正文与各级标题同字号、仅靠编号区分层级),标题同字号 + 加粗以保持导航大纲可用。 - ⚠️ 仅凭"编号+中文"会把正文当标题:
1.1 为规范公司行政后勤管理,保障食堂…特制定本制度。这种编号开头的正文段落(编号式条款极常见)必须被否决。heading_level()走_looks_like_heading()三条约束:长度 ≤40 字、不含句末标点(。;!?)、顿号/逗号 ≤1 个。真标题(1.1 总则、2.1 适用范围)不受影响。误判后果严重:正文被当标题会让build_chunks段落归属整体错位,检索召回直接失效。 - 章节与段落:标题与其下属段落归为一体;空行用段落间距而非空段落。
- 表格:
pdfplumber.extract_tables()抽取为 Word 表格;抽取前跳过表格 bbox 内的正文文字,避免与表格对象重复。跨页长表当前为逐页独立抽取、不自动合并(已知限制,详见references/pipeline.md第 3 节及其「续表人工/半自动处理」)。 - 🚨 铁律(用户 2026-09-21 明确指令):源表格是合并单元格的,产出的 Word 与 Excel 都必须还原同样的合并结构,与源 PDF 一致。 pdfplumber 会把合并表头拆散、摊平,甚至切出碎片表(实测:发票页表头被拆成 2 个残表)——不能直接拿抽取结果出表。必须用词坐标 / 表格线重建合并网格:Word 用
w:gridSpan(横向)+w:vMerge(纵向),Excel 用merge_cells。表头与表内文字一律用源文原文(含源自带拼写错误,如 "Contry of origin"),不翻译、不改写、不擅自拆列改名。非表格的版面元素(信头、表内末行的产地行、表下备注行)同样按源位置还原,见references/pipeline.md的坐标定位法(extract_words的 y/x 判定表内/表外)。 - 图片与图表:提取为图片文件,配题注「图 N(来源:PDF 第 X 页)」;图表经视觉理解生成文字说明后入正文(理解方式见后文「图表智能理解」章节,支持 VLM 自动或 Agent 用 Read 理解);过滤 1px 装饰细线等装饰图,仅抽取内容图表(信笺头/logo 区域不计入内容图)。
- 页眉 / 页脚:信笺头抽成 Word header。
convert_to_docx.py的_build_header()会自动从 PDF 页首检测 2×2(或 n×m)信笺头表格,按源 PDF 的列宽比例复刻为带实线边框的表格(Table Grid + 显式w:tblBorders全边框),逐格还原文字/字号/字体;logo 抽入左上格(与文字同行);若检测到密级勾选行(含 □/■)则置于表下;并将页边距设成与 PDF 正文一致(扫描全页取正文边界,避免标题页居中文字干扰)。不再把页眉当内联文本丢进正文,也不再做成「logo+文本堆叠」的 1×2 表。正文侧同步剥离:extract_structure()逐页计算信笺头下边界lh_bottom(页首小表 bbox 底部 + 紧贴其下的密级行),正文文字行与内容表均按lh_bottom过滤,确保信笺头只出现在 Word header、不重复进正文。页脚自动还原源 PDF 每页底部的真实页脚文字(法律声明/版权声明等),逐行写入 Word 页脚由 Word 每页重复,字体/字号/颜色对齐源 PDF 探测结果;不再只写 PAGE/NUMPAGES 页码域(避免丢弃页脚正文)。
Step 3:语义切片与索引
按语义结构 chunk 切分,写入 chunks.jsonl(每条一行):
- 标题 + 所属段落 归为一个 chunk。
- 表格 单独结构化为一个 chunk(Markdown 表格 + 字段说明)。
- 图片 / 图表 生成摘要作为一个 chunk,附图片路径。
- 长表格 按行列字段转为结构化文本。
> ⚠️ 产物边界:chunks.jsonl 只由 build_index.py 产出,主转换命令 convert_to_docx.py 不生成它。
> 也就是说「页级可追溯 / 闭环核验」需要额外跑一次 build_index.py;只跑转换则只有 <output>_report.json 与 .preview.md。
每条 chunk 携带 metadata:
{
"doc_id": "report-2026",
"page": 12,
"section": "第三章 方法",
"title": "3.2 实验设计",
"table_no": null,
"image_desc": null,
"version": "2026-07-16",
"context": "该片段归属原文第三章方法,位于第 12 页,承接 3.1 研究问题。"
}
context 字段补充该片段在原文中的归属位置与前后文,便于检索召回后定位。
Step 4:双路索引与 Agent 调度
构建并行的两套索引(scripts/build_index.py):
- 向量索引:语义召回,处理同义/改写问题(sentence-transformers 可用时启用,否则降级为 BM25)。
- 关键词索引:精确术语、编号、字段名召回(BM25 / 倒排)。
按问题类型分发(scripts/retrieve.py):
| 问题类型 | 路径 | 负责模块 |
|---|---|---|
| 简单事实(术语/定义/数值) | 向量召回 + 关键词召回 | 检索片段 |
| 多章节对比/综合 | 先检索,再由 Agent 规划阅读路径,逐页精读 | 读取指定页 |
| 表格/图表问题 | 调用视觉工具解析对应页区域 | 抽取表格 / 看图表 |
| 需原文佐证 | 返回带页码引用(P.X)的片段 | 返回引用 |
各模块职责明确:检索片段、读取指定页、抽取表格、看图表、返回引用,互不越界。
融合方式:两路召回后用 RRF(Reciprocal Rank Fusion) 融合,无需统一量纲。向量索引缺失或与建库模型不一致时优雅降级为纯 BM25 单路,并在输出中明确标注实际使用的检索路(dual_rrf / bm25_only),不做"号称双路实则单路"的表述。
输出规范(生产级 Word)
生成的 .docx 必须满足:
- 使用命名样式(Heading / Normal / 题注 / 脚注),不直接堆砌内联格式,保证可编辑。
- 表格使用显式列宽,避免后续在 LibreOffice / Google Docs 漂移。
- 正文默认不注入
[P.X]内联页码标记(保持 1:1 忠实副本);页级可追溯由chunks.jsonl的page字段与verify.py的[P.X]引用承担。 - 图表配题注,复杂图表附视觉理解生成的说明文字。
- 页眉页脚、脚注、章节层级与原 PDF 一致。
- 字体与颜色对齐源 PDF:先用
analyze_typography()探测正文/信笺头/页脚/标题的真实字体名、字号、颜色(含黑色即None),再映射到 Word 样式;禁止写死「宋体 11」「黑体」等导致与源不符。CJK 文本必须设置w:rFonts的w:eastAsia。
- ⚠️ 颜色为None(即源 PDF 黑色)必须显式写RGBColor(0,0,0),绝不允许留空依赖样式继承:WPS 及部分 Word 主题会把「无显式颜色」的继承色渲染成蓝色,导致整篇黑字变蓝。落实三处:①set_run_font中rgb is None时兜底为黑;②Normal/Heading样式即便源色为None也显式设黑;③add_heading/add_paragraph不要用add_heading(text)直接塞文本,改为add_heading('', level)后r = h.add_run(text)再set_run_font(r, ..., 'None', bold=True)上色,避免 run 继承变蓝。
- 每个文字按其源 PDF 实际颜色上色(黑→黑、红→红、其他→其他):抽结构时为每个 block 生成runs(按词颜色分段,异色词断开,同色词合并,_word_color()取词覆盖字符的颜色众数),build_docx对每段seg用其seg_color调set_run_font上色;绝不用body字体探测的众数色统一覆盖所有文字。这样源 PDF 里的红色批注/强调、蓝色链接、绿色标注等都能原色还原,不被染黑。
- ⚠️ 取色必须回退non_stroking_color:pdfplumber 对非嵌入式色彩空间的 PDF 不填char['color'](值为None),真实颜色在char['non_stroking_color']。若只看color,这类 PDF 的红字/蓝字会全部被当"黑色"写死000000。统一走char_color(ch):color缺失即回退non_stroking_color;color_repr()另支持灰度标量与 CMYK 四分量。验证手法:造一份红字 PDF,检查输出 docx 的 run 色是否为红色(实测应为CC0000一类)而非000000。 - 表格视觉规范(中文制度文档标准排版):
- 单元格文字:水平居中 + 垂直居中(WD_ALIGN_PARAGRAPH.CENTER+WD_ALIGN_VERTICAL.CENTER)。
- 表格内字号比正文小 1–1.5pt(推荐 9pt vs 正文 10.5pt,匹配 PDF 紧凑感)。
- 表头行:加粗 + 浅灰底色(如#F2F2F2)区分于数据行;用_set_cell_shading(cell, 'F2F2F2')实现。
- 纵向合并后合并单元格同样设垂直居中。
输出规范(生产级 Excel,v1.0.3+)
对于发票/装箱单/报表等结构化数据文档,支持输出 Excel (.xlsx) 替代或补充 Word。
何时用 Excel 输出
- 文档核心内容是表格数据(明细、数量、金额、规格等),需要后续计算/筛选/透视。
- 需要真数值单元格(非文本),支持 SUM 公式、排序、筛选。
- 需要公式闭环(合计行 = SUM(数据区)),可交叉验证。
Excel 输出规范
🚨 图表还原铁律(用户 2026-10-02 强化指令):源 PDF 里出现图表,转出的 Excel 必须包含对应的原生可编辑图表,不能只留数据表或静态截图。 凡 PDF 图表背后是表格数据(柱/条/折线/饼/散点等),都要用 openpyxl 的 BarChart / LineChart / PieChart 等重建为原生图表对象(配方见下方「原生可编辑图表 · Excel 侧」);低清/截断图按已核验表格数据重建。静态截图仅作兜底、不算达标。校验:重开 xlsx 确认 _charts 非空且系列名/数据引用正确(防漏挂或只贴图)。
- 🚨 合并结构与源一致(用户铁律,2026-09-21):源表格里横向/纵向合并的单元格,Excel 必须用
merge_cells还原同样的合并区(实测样例:Marks 块 A5:B7、CIF 横跨 G5:H5、C/No 纵向合并 A8:A15、双层表头 A41:A42/B41:F41)。表头用源文原文。校验:重开文件核对merged_cells.ranges数量与坐标。 - 真数值单元格:数量/金额/单价等用
int/float写入,不用文本;配number_format控制显示格式(如#,##0/0.00/0.00000)。 - SUM 公式闭环:合计行用
=SUM(range)而非硬编码数值,可在 Excel 中实时重算验证。 - 表格边框:显式
Border(left,right,top,bottom)每格设置,不用默认无边框。 - 表头区分:加粗 + 浅灰底色(
PatternFill);合计行加粗 + 浅黄底色。 - 图片嵌入不变形:
add_image()只设img.width,img.height按原始像素比例自动计算(用 PIL 读原始尺寸),绝不同设宽高强制拉伸。 - 多页合并为单 sheet:用灰色分隔条行(
PAGE N)隔开不同页面区域,比多 sheet 更适合打印/审阅。 - 页脚页码:
oddFooter.center.text = '&P / &N pages'。 - 非表格内容不丢:标题/买方信息/地址/签名/Insurance 声明等,作为文本行或嵌入图片放在表格区域上方/下方。
Excel 校验规范
- 算术预检:构建前用 Decimal 精确校验每行
qty × price = amount,合计 = Σ明细,不过就中止。 - 逐格校验:重新打开 xlsx,逐格比对数据/标签/公式,检查表头、表体、合计行、空单元格、合并格。
- PDF 数字多重集比对:把 PDF 全文提取的所有数字 token 与 Excel 所有单元格数字做多重集比对,业务关键数字零缺失。
评测与反查核验(闭环)
最终交付前执行 scripts/verify.py,覆盖:
- 答案正确性:关键事实是否来自原文 chunk。
- 检索命中率:Top-K 是否包含相关 chunk。
- 页码准确性:引用
[P.X]是否落在真实页。 - 表格解析准确度:表格行列无错位、无漏格。
- OCR 无漏写:扫描件逐页比对,无整段缺失。
- 图表理解准确度:图表摘要与视觉内容一致。
核验输出 verify_report.json,列出每条引用的页码、命中 chunk 与实际检索路(dual_rrf 双路融合 / bm25_only 降级单路),支持可追溯与闭环回归。
> 前置条件:verify.py 依赖 build_index.py 先产出 index_dir(含 chunks.jsonl / index.json,向量库可选)。未建索引直接跑 verify 会缺文件报错。
转换效果预览(无需 Word 也能看「长啥样」)
转换脚本结束时会额外生成 <output>.preview.md:把 Word 的页眉、标题层级、正文与所有表格
以 Markdown 镜像出来。打开它即可在不启动 Word 的情况下核对结构、层级与表格是否正确,
避免「只给命令不给效果图」的盲区。交付时 Agent 也会直接预览生成的 .docx 本身。
运行诊断与错误报告(不再静默跳过)
早期版本对图表/图片/扫描页的异常用 except: pass 直接吞掉,图片多的文档会「无声失败」、
图表没出来也查不到原因。现已改为:
- 所有异常进入运行报告,并在控制台打印
[警告]/[错误](含页码、图序号、原因); - 末尾打印汇总:页数 / 表格(成功,失败) / 图表(成功,失败) / 扫描页 / OCR 可用否 / 警告与错误条数;
- 同时写出
<output>_report.json(含tables / table_fail / figures / figure_fail / scans / ocr_available / warnings / errors)。
排查清单:图表没出来 → 看 report 里 figure_fail 与 errors;表格错位 → 看 table_fail;
扫描件无文字 → 看 ocr_available(需装 pytesseract + tesseract)。
图表智能理解(文字说明)
PDF 里的图表/图片会被抽取为图片并写入 figures/,同时生成 figures_manifest.jsonl
(每条含 page / idx / path / width / height / caption)。文字说明有两种注入方式:
- 自动(配 VLM):设置环境变量
PDF2DOCX_VLM_ENDPOINT(服务需鉴权时,凭据同样以环境变量传入,变量名见 scripts/convert_to_docx.py 的环境变量读取处),
脚本自动调用并写出figures_desc.jsonl,生成 Word 时把说明追加进题注
「图 N-M(来源:PDF 第 N 页):<说明>」。 - Agent 理解(无 VLM):Agent 用 Read 工具逐一查看
figures/fig_*.png,
把说明写成desc.jsonl(每行{"page":N,"idx":M,"desc":"..."}),再运行下方第 5 条命令写回。
该注入脚本按题注匹配、幂等追加说明,无需重跑整条流水线。
原生可编辑图表(Word/Excel,v1.2.0 XML 注入 / v1.3.0 COM 替换)
把结构化数据变成原生可编辑图表(非贴图):Word 里双击可改数据,Excel 里改引用即变。
典型场景:表格转出后补趋势图/对比图;PDF 中低清/截断图表按已核验表格数据重建。
Word 路线选择(先选路,再动手)
| 路线一:XML 注入(v1.2.0) | 路线二:COM 替换(v1.3.0) | |
|---|---|---|
| 脚本 | native_charts.py |
word_com_charts.py |
| 前置环境 | 无 Word 也能跑(跨平台) | 仅 Windows + 本机装有 Word(pywin32) |
| 使用方式 | 文档中预先埋占位段落,从零注入 | 把已存在的图片(如转换产出的截图)锚点定位后原地替换 |
| 支持类型 | 簇状柱 / 折线(手写 XML 可扩展) | 全部 XlChartType(柱/条/折线/饼…) |
| 典型场景 | 生成期注入:正文预埋 [[CHART_1]] token |
事后替换:PDF 原有图表的截图换成真图 |
Word 路线一:XML 注入(scripts/native_charts.py,手写 DrawingML)
⚠️ 本机 python-docx 实为旧 docx v1.2.0 别名,无 docx.chart / add_chart API,必须在 zip 层手写 OOXML。
from native_charts import inject_charts
specs = [dict(
token="[[CHART_1]]", # 文档中预先插入的占位段落文本(整段将被替换为居中图表)
type="bar", # bar=簇状柱形 | line=折线(带标记)
title="图表标题", cats=["2021届", "2022届"],
series=[dict(name="大专", values=[6000, None], color="9E9E9E")], # None=数据缺口
width_cm=15.5, height_cm=8.5)]
inject_charts("out.docx", specs) # 返回注入数
🚨 「渲染空白」三大根因(缺一必白屏,逐条自检):
1. word/charts/chartN.xml 与 word/embeddings/bookN.xlsx 必须作为 zip 成员真正写入(python-docx 的 relate_to 不会落盘);
2. [Content_Types].xml 必须注册 Override:chart=application/vnd.openxmlformats-officedocument.drawingml.chart+xml、嵌入簿=...spreadsheetml.sheet;
3. chart XML 须含 c:catAx/c:valAx(互指 c:axId)且写 strCache/numCache 缓存。
其他要点:
- 占位段落替换正则锚定到直接包含 token 的 <w:p>:r"<w:p\b[^>]*>(?:(?!</w:p>).)*?TOKEN.*?</w:p>",否则跨段落误吞正文;
- c:dispBlanksAs val="gap" 让 None 呈现缺口;series numCache 中 None 直接省略 <c:pt>;
- 嵌入簿单元格闭合标签是 </c> 不是 </c:r>(属性 r 不是标签名——曾因此图表可渲染但 Word「编辑数据」打不开工作簿);
- wp:docPr id 用 900+ 避免与既有图片冲突;rId 取 document.xml.rels 现有最大值递增;
- 系列色对齐源图:c:spPr/a:solidFill/a:srgbClr。
Word 路线二:COM 替换(scripts/word_com_charts.py,v1.3.0+,需本机 Word)
把 docx 里已有的静态图(锚点定位)原地替换为原生可编辑图表,数据源是 xlsx 区域。
源自半导体行业人力报告实战(53 页 PDF → Word 16 张原生图一次收官 16/16)。
from word_com_charts import replace_images_with_charts
specs = [dict(
name="涨薪率按城市",
anchor={"prev": "图 3 主要城市涨薪率"}, # 目标图前一非空块文本(包含匹配)
chart_type=51, # 51 簇状柱 | 52 堆积柱 | 57 横条 | 65 折线 | -4120 饼
title="主要城市涨薪率(%)", width_pt=400, height_pt=240,
colors=["1F3864", "ED7D31", "C00000", "70AD47", "5B9BD5"],
legend=True,
source=dict(cats="涨薪率!B19:G19", # 类别区域(单行/单列,sheet 名必带)
series=[{"name": "全国", "ref": "涨薪率!B20:G20"},
{"name_ref": "涨薪率!C20", "ref": "涨薪率!C21:G21"}]))]
report = replace_images_with_charts("out.docx", specs, xlsx_path="data.xlsx")
# report = {"total", "ok", "failed", "chart_parts"};任一图失败自动回填原图并继续
CLI:python scripts/word_com_charts.py doc.docx --specs specs.json --xlsx data.xlsx --render-pdf 核对版.pdf
🚨 COM 路线四大坑(脚本已内建处理;本段与「常见陷阱」是唯一完整清单,改动前必读):
- AddChart2 默认仅 3 系列,扩列不自动跟随(实战最大根因):数据表 Resize 到 4-5 列后
图表仍只绑前 3 列,取SeriesCollection(4)报 com_error"参数无效"(scode -2146827284)。
必须在写完数据后显式重建系列:现有系列逐个改Values/XValues公式
(sheet 名用cws.Name动态取并套单引号)+NewSeries补足 + 多余Delete,
最后断言Count == nser;饼图(-4120)仅 1 系列豁免。全文档 N 张图只有 >3 系列的会炸,
≤3 系列全成功——这个"恰好吻合"模式就是此坑的指纹。 - 图表数据网格状态机:AddChart2 成功后 Word 弹出内嵌数据网格,未被"接管"
(cd.Activate()+ 写数据)前,下一次 AddChart2 报"网格已打开"/"参数无效"(同 scode)。
铁律:插入后立即 Activate 写数据;Excel 进程全程不 Quit,保存后再统一退。 - zip 句柄泄漏 = Word 保存"文件许可权错误"真根因:Python 读 docx 的句柄不带
FILE_SHARE_WRITE,全程阻塞 Word 写盘。zip 必须 with 即用即关。 - 导出核对版 PDF:
ExportAsFixedFormat(path, 17)(17=wdExportFormatPDF,Excel 才是 0),
路径必须反斜杠(正斜杠报"无法保存,只读文件")。
其他要点:
- 锚点支持
{"prev"|"next"|"prev_any"|"next_any": 文本}(包含匹配);替换按 ordinal 倒序
保证定位稳定;目标必须是含a:blip的静态图(已是图表则拒绝替换,防重复运行误删); - Word 忙抛 CALL_REJECTED(-2147418111),脚本用重试包装(60 次 × 0.3s);
- 异常退出残留三件套:
~$xxx.docx锁文件 + 隐形 WINWORD + 僵尸"Microsoft Word 中的图表 -
Excel" OLE 实例——开工前脚本查锁文件;残留进程用
tasklist //v | grep -iE "winword|excel"(看窗口标题)+taskkill //F //PID精清; - 验证方法论:矢量图表在导出 PDF 里没有 image 对象(pdfplumber 的 images 是页眉徽标),
且大图会排到标注文字的前一页——用 fitz 渲染页面 PNG 亲眼目检,
别拿 images/curves 计数下结论(详见「常见陷阱」末两条)。
Excel 侧:openpyxl 原生图表(非连续列逐列 Series)
非连续数据列(如大专/本科/硕士/博士在 B/C/F/I 列)逐列 Series(Reference(ws,col,r1,r2), title="名") 追加,再 chart.set_categories(Reference(...)) 统一挂类目;chart.y_axis.numFmt="#,##0";系列色 s.graphicalProperties.solidFill="RRGGBB"(折线改 line.solidFill + s.marker 填色);折线必须 s.smooth=False。
闭环核验模式(实测 424 项中图表相关 12+ 项)
- zip 级:chart/book 部件存在、CT Override 计数、document.xml 无
[[CHART残留; - 结构级:chart XML 含 catAx/valAx/externalData;
- 数值级(关键):提取 numCache 缓存值多重集 == 规范数据源对应列(防「图对了数错了」);
- 嵌入簿可开:openpyxl 重开 bookN.xlsx 核对表头/数据(仅查图表缓存不够——见 changelog 1.2.0 的真 bug);
- 兼容冒烟:python-docx 能重新打开注入后的 docx。
回归:# 原生图表注入回归(未随本包发布)(路线一)+ # COM 图表回归(未随本包发布)(路线二,无 Word 自动 SKIP),均 VERDICT 收尾。
补充能力(PDF 加解密 / 表单 / 签章 / 单文件检索)
除 PDF→Word/Excel 主流程外,本技能附带 4 个独立工具脚本,处理 PDF 本身的读写与安全操作。
以下用法均经实测验证(2026-09-18 冒烟回归)。
1. scripts/pdf_crypto.py — PDF 加解密(依赖 pypdf)
- encrypt:加密 PDF。
--user-pw打开口令,--owner-pw所有者口令(缺省同用户口令),--preset full|readonly权限预设(readonly = 仅可打印 + 无障碍提取),--rc4用 RC4 而非默认 AES-128。 - decrypt:用
--password去除加密。 - info:查看是否加密。
- 典型场景:对外发送敏感 PDF 前加密;回收带口令的 PDF 后解密归档。
python scripts/pdf_crypto.py encrypt input.pdf enc.pdf --user-pw 口令 [--owner-pw 权限口令] [--preset readonly]
python scripts/pdf_crypto.py decrypt enc.pdf dec.pdf --password 口令
python scripts/pdf_crypto.py info enc.pdf
2. scripts/pdf_form.py — 表单填写(依赖 pymupdf)
list:列出 PDF 内所有 AcroForm 字段(页/名称/类型/当前值)——必须先跑这步核对字段名。fill:按 JSON 文件 批量写入(--data fields.json),字段名须与 PDF 中field_name完全一致;未匹配的字段名会 warn 列出。- 典型场景:批量填写固定格式申请表/审批单。
python scripts/pdf_form.py list input.pdf
echo '{"name":"张三","date":"2026-09-18"}' > fields.json
python scripts/pdf_form.py fill input.pdf filled.pdf --data fields.json
3. scripts/pdf_sign.py — 电子签章(依赖 pymupdf,PKI 可选 endesive)
stamp:视觉签章——把签章图叠到指定页指定矩形坐标。坐标是必填:--x --y --w --h(PDF pt,1pt=1/72 inch,原点左下角),另有--page(默认 1)、--rotate、--opacity(0~1)。
⚠️ 没有九宫格锚点(如--pos bottom-right),必须自己算坐标。A4 为 595×842 pt。sign:视觉签章 + 可选 PKI 数字签名(需--cert/--key/--pin;未装 endesive 则仅完成视觉签章并提示)。- 典型场景:给已定稿 PDF 盖公章/签名图。
# A4 右下角盖 120x120pt 的章(留 100pt 边距)
python scripts/pdf_sign.py stamp input.pdf signed.pdf \
--image seal.png --page 1 --x 375 --y 80 --w 120 --h 120 --opacity 0.9
4. scripts/pdf_search.py — 单文件 IR 检索(依赖 pdfplumber)
- 免建索引检索:直接对单个 PDF 做 BM25 关键词检索,返回命中页与片段。支持
--topk(默认 5)、--chunk-lines(默认 3);query 传.txt则逐行作为多条查询。 - 与主流程的区别:
build_index.py/retrieve.py面向多轮问答 + 双路召回 + 闭环核验(需建库);pdf_search.py面向一次性快速查找(无需建库、无需 sentence-transformers,开箱即用)。 - 典型场景:只想快速定位某份 PDF 里提到某关键词的页。
python scripts/pdf_search.py input.pdf "关键词" --topk 5
补充能力的安全与边界
- 加解密口令不要写进脚本或提交到仓库;优先命令行交互或环境变量传入。
- 电子签章:
stamp是视觉盖章(无密码学效力);需法律效力请走sign的 PKI 路径,并确保私钥保管安全。 - 表单填写:先
list看字段名再fill,避免字段名不匹配静默失败。 - 这 4 个脚本不改动源文件,一律输出新文件。
脚本用法
# 1) 端到端:PDF -> 生产级 Word(顺带产出 _report.json 与 .preview.md)
python scripts/convert_to_docx.py input.pdf output.docx
# 2) 构建语义切片 + 双路索引(供检索/问答/核验)—— verify.py 的前置
python scripts/build_index.py input.pdf index_dir
# 3) 双路检索 + 路由(给定问题,返回带引用的 chunk)
python scripts/retrieve.py index_dir "实验设计的样本量是多少?"
# 4) 闭环反查核验(依赖第 2 步产出的 index_dir)
python scripts/verify.py index_dir questions.txt
# 5)(可选)把图表说明写回已生成的 Word(无 VLM 时由 Agent 用 Read 理解后调用)
python scripts/inject_figure_desc.py output.docx desc.jsonl
# 6) 补充能力:加解密 / 表单 / 签章 / 单文件检索(均为实测参数)
python scripts/pdf_crypto.py encrypt input.pdf enc.pdf --user-pw 口令
python scripts/pdf_crypto.py decrypt enc.pdf dec.pdf --password 口令
python scripts/pdf_form.py list input.pdf
python scripts/pdf_form.py fill input.pdf filled.pdf --data fields.json
python scripts/pdf_sign.py stamp input.pdf signed.pdf --image seal.png --page 1 --x 375 --y 80 --w 120 --h 120
python scripts/pdf_search.py input.pdf "关键词"
高效闭环(结构复用,避免二次解析)
转换与建索引本会对同一 PDF 各跑一遍 analyze_typography + extract_structure(2× 解析开销)。
若既要生产级 Word、又要页级追溯/闭环核验,用 --structure-out / --structure-in 让两步复用同一份结构:
python scripts/convert_to_docx.py input.pdf out.docx --structure-out structure.json
python scripts/build_index.py input.pdf index_dir --structure-in structure.json
convert_to_docx.py 把 {typography, structure} 落盘为 JSON;build_index.py 读到则跳过抽取直接切分建索引。
任一环节结构文件缺失/损坏会自动回退重新抽取,不影响产物。
转换还会额外产出:<output>_report.json(运行诊断)、<output>.preview.md(结构预览)、
<output>_images/figures/(抽取出的图表图片与 figures_manifest.jsonl 清单)。
非 PDF 源与快速 Markdown 转换(MarkItDown 通道)
本技能除 PDF 主流程(自有引擎)外,内置 MarkItDown 通道,用于把 非 PDF 源 或「只需快速转 Markdown」的需求转成 Markdown。
与自有引擎的分工
- PDF → 生产级 Word/Excel(保细节 + 检索反查闭环):走
scripts/convert_to_docx.py等自有引擎(见上文主流程)。需要像素级/结构级还原、页码可追溯时的唯一选择。 - 非 PDF 源 / 快速 Markdown 提取:Word/PPT/Excel/图片(OCR)/音频/HTML/网页URL → Markdown,走 MarkItDown 通道。输出是 LLM 友好的线性 Markdown,不做像素级版式还原(无合并单元格、无精确坐标),适合检索/RAG/快速阅读,不适合当生产级可编辑文档。
- PDF 两种都有:PDF 若只需快速抽文本/Markdown,也可直接用本通道(
markitdown x.pdf -o x.md),不必跑完整生产级流水线;但要保细节/可追溯仍走自有引擎。
何时用 MarkItDown 通道
- 源文件不是 PDF(Word/PPT/Excel/图片/音频/HTML)。
- 用户只要「转成 Markdown / 提取纯文本」,不要生产级 Word。
- 网页/URL、YouTube、ZIP 归档转文本。
- 图片 OCR 提取、音频转写。
安装
pip install 'markitdown[all]' # 全格式
# 或按需:pip install 'markitdown[pdf,docx,pptx,xlsx]'
命令速查
markitdown document.pdf -o output.md # 文件转 Markdown
markitdown https://example.com/docs -o d.md # 网页/URL
markitdown image.jpg -o o.md # 图片 OCR(需 Tesseract)
markitdown audio.mp3 -o t.md # 音频转写(需 audio-transcription 扩展)
# 批量(内置脚本)
python scripts/markitdown_batch_convert.py docs/*.pdf -o markdown/ -v
支持格式
| 格式 | 能力 |
|---|---|
| Word (.docx) | 标题/列表/表格/链接 |
| PowerPoint (.pptx) | 幻灯片文本/结构 |
| Excel (.xlsx) | 多表 → Markdown 表格 |
| 图片 | OCR + EXIF(需 Tesseract) |
| 音频 | 语音转写(需 audio-transcription 扩展) |
| HTML | 结构保留 |
| 网页/URL、YouTube | 抓取/转写 |
| CSV/JSON/XML/ZIP/EPUB | 可读 Markdown |
Python API 速查
from markitdown import MarkItDown
md = MarkItDown()
print(md.convert("document.pdf").text_content)
OCR 前置依赖
# Windows:从 https://github.com/UB-Mannheim/tesseract/wiki 安装 Tesseract
# macOS:brew install tesseract
参考
references/markitdown_usage.md:详细示例(CLI / Python / 集成模式)references/markitdown_reference.md:完整 API 参考与格式细节
相关技能
- 本技能为自包含实现:
scripts/引擎已封装文本/表格抽取(pdfplumber)、OCR(可选 pytesseract)、Word 生成(python-docx)、PDF 加解密/表单/签章/检索(pymupdf)等能力,无需安装其他技能。 - 多模态图表理解依赖运行环境的视觉读取能力(Read 工具查看渲染后的图表图片)。
- 与
已删除的图表技能的关系(v1.2.0 更新):该技能已从本机删除,其核心的手写 DrawingML 原生图表注入能力自 v1.2.0 起并入本技能(scripts/native_charts.py,见「原生可编辑图表注入」章节)。需要图表在 Word/Excel 中为「原生可编辑对象」时直接用本技能,不再依赖外部技能。
常见陷阱
- 🚨 合并表被
extract_tables()摊平/拆碎(用户铁律场景,2026-09-21) → pdfplumber 对含合并单元格的表(发票/装箱单类)会把合并表头拆散甚至切出多个碎片表。源合并 → 产物合并:Word 用w:gridSpan/w:vMerge重建,Excel 用merge_cells重建。重建后必须校验: - Word:直解析 OOXML,对每个
<w:tr>按其中<w:tc>出现顺序累加 gridSpan(无则计 1),总和 =tblGrid的 gridCol 数。⚠️ python-docx 的cell(ri,ci)/row.cells在含 gridSpan 的行会按逻辑列重映射读错单元格,不能用它做网格校验。 - Excel:重开核对
merged_cells.ranges(数量+坐标),非锚点 MergedCell 的.value只读(只能写锚点),但 border/alignment 可对区域内每格循环赋值。 - 跨页长表被
extract_tables()切成多块 → 当前脚本不自动合并(已知限制)。需要时按references/pipeline.md第 3.1 节人工/半自动处理:表头一致且中间无正文才合并,否则保持独立表格。统一抽取易产生幻影列/空行,关键文档建议用「已核验的干净表格数据」覆盖。 - 扫描件 OCR 把图表当文字 → 图表区域单独走视觉理解,不进 OCR 文本。
- 标题层级误判(平级字号 PDF) → 正文与标题同字号时,靠编号结构识别;勿仅靠字号阈值,否则 H1 计数可能为 0。
[P.X]引用与实际页偏移 → 抽取与生成使用同一分页基准(pdfplumber 页码)。正文已默认不注入[P.X]内联标记(忠实副本),追溯走chunks.jsonl/verify.py;若需正文内联标记(v1.4.0 已删除从未被调用的_add_page_ref),在build_docx()段落分支自行追加小字号灰色 run 即可。- 只跑转换就想做闭环核验 →
chunks.jsonl只由build_index.py产出,必须先建索引再verify.py,否则缺文件。 - 表格列宽用百分比导致 Google Docs 漂移 → 用显式厘米宽度。
- 字体写死导致与源不符 → 先
analyze_typography()探测真实字体/颜色,再映射样式。 - 正文/标题变蓝字 → 源 PDF 黑色文字探测为
None,若set_run_font/样式不显式写RGBColor(0,0,0),或add_heading(text)/add_paragraph(text)直接塞文本使 run 继承空色,WPS / 部分 Word 主题会把继承色渲染成蓝色。应对:①set_run_font中rgb is None兜底黑色;②Normal/Heading样式即便源色为None也显式设黑;③ 标题用add_heading('', level)后手动add_run+set_run_font上色。 - re 原始字符串
\u不转义 → 判断 CJK 用显式区间'\u4e00' <= ch <= '\u9fff',勿用r'[一-鿿]'。 - 取行字号用
line.startswith(c.text)→ 多字符行永远取不到,应逐词重组行后按组内字符求字号。 - 混合横竖版 PDF 必崩 →
_build_header()跨全部页算页边距,横版页 x1 超过竖版页宽度 → 负值右边距 →Cm(负数)抛ValueError。修复(v1.0.2):按同方向页组分别计算边距 + 负值钳位 0.5cm;build_docx()检测方向变化时自动插入分节符(WD_SECTION_START.NEW_PAGE)+ 设置页面尺寸/方向 + 复制页眉页脚到新节。 - ✅ 分节切回已修复(v1.1.2):原实现只在「方向 ≠ 首页方向」时开新节,导致 portrait→landscape→portrait 末尾段被锁在 landscape 节里。现改为跟踪「上一节方向」,凡与上一节方向不同(含横版切回竖版)都开新节并设正确尺寸/方向。
闭环回归脚本(未随本包发布)的[6]已固化 3 页 P/L/P 分节回归。 - Excel 图片变形 →
openpyxladd_image()同时设img.width和img.height会强制拉伸。正确做法:用 PIL 读取原始像素尺寸,只设img.width,img.height = int(width * orig_h / orig_w)按比例自动计算。 - Word 图片(澄清,非 bug) →
convert_to_docx.py用add_picture(path, width=Cm(14)):只设宽度、不传高度,python-docx 会按源纵横比自动算高度,不会变形。代价是所有图统一 14cm 宽——源图小于 14cm 会被放大、源图极宽会较高。若需"只缩不放/按源像素限宽",改_add_picture_fit()即可,但默认行为已保比例。 - 页眉 logo 被丢弃 → 黑字无标识 → 通用脚本已内置
extract_logo()(取首页 >20×20 图片裁剪为 logo.png)并嵌入_build_header()左上格,且有if logo_path:容错(提取失败跳过而非报错)。不要退化成「只放信笺头文字、丢 logo」的页眉;从本 skill 派生的定制脚本务必复刻此逻辑。 -
信笺头重复进正文(v1.0.2 前的老 bug,已修) → 根因有二:① 正文文字行跳过阈值
top < h*0.10与页眉检测区域h*0.15不一致,信笺头表下半部(约 11% 处的「文件名称/版本」行)漏进正文;②page.extract_tables()从不过滤信笺头表,致其每页作为内容表重复进正文。修复:逐页计算lh_bottom(页首小表 bbox 底部,含 y1<h*0.30 防误判大表 + 紧贴其下 □/■ 密级行),文字行与内容表均按其过滤。派生脚本若复刻本逻辑,切勿再退回固定 0.10 阈值。 -
🚨 页首 10% 被当成信笺头盲区 → 正文首段整段静默消失(v1.1.7 修复) → 旧版在未检测到信笺头表时仍默认
lh_bottom = h*0.10,凡top落在页首 10% 的正文行一律按信笺头丢弃。页边距 2cm(≈6.7% 页高)的常规文档,紧随页首标题之后的第一个正文段落必然落在该带内 —— 实测「1.1 为规范公司行政后勤管理,保障食堂…特制定本制度。」整段消失,chunks里没有它、检索也召回不到。修法:只有在确实检测到信笺头表时才有排除下边界;无信笺头表时按「文字信笺头行」精确匹配剥离,不做位置盲删。 - 🚨 内容表被误判为信笺头表(v1.1.7 修复) → 旧版
_build_header只用「表格 top < 页高 15%」判定信笺头,extract_structure却用「top<15% 且 bottom<30%」,两处阈值不一致;且都没限制表的大小。正文首屏的内容大表(4×6 装箱单等)会被误判:既被搬进 Word 页眉、又在正文里被过滤掉(内容静默消失且在每页页眉重复)。修法:新增共享_detect_letterhead_table(),统一判据并追加「行数 ≤4 且列数 ≤4」的大小约束。 - 🚨 合并单元格表导致
_build_header抛IndexError,整篇转换失败(v1.1.7 修复) → 旧版列宽只取首行单元格的右边界:首行含横向合并时边界被去重少算,col_w长度 <n_cols→col_edges[i+1]越界崩溃。实测页首 4×6 装箱单表转换直接失败。修法:列边界改取全表所有单元格 x0/x1 的并集,并按坐标定位列号、按跨度还原横向合并(cell.merge),不再用enumerate的序号当列号。 - 🚨 文档标题被搬进 Word 页眉(v1.1.7 修复) → 旧版
extract_letterhead()只按位置取行(top < 页高 10%),会把页首的文档标题也当成信笺头:标题被写进页眉、正文里再也找不到它、且每页页眉重复。修法:新增_letterhead_text_lines(),只取页首连续短行并在遇到标题/长句/句末标点时截断;并要求该行在后续页复现才判定为信笺头(单页文档无法核验 → 保守留在正文)。既无信笺头也无 logo 时页眉留空,不再回填正文、也不放「(信笺头)」占位符。 - 🚨 单级编号的正文条款被误判为 H1(v1.1.7 修复) → v1.1.1 给多级编号分支加了
_looks_like_heading()约束,单级分支(1、/1.)漏加,于是「5. 备注:本条款自发布之日起施行…。」「1、为规范公司…,特制定本制度。」这类正文条款被判成 H1,段落归属整体错位、检索失效。修法:单级分支同样过一遍标题性约束。 - 🚨 COM 原生图替换:4-5 系列报"参数无效"(v1.3.0 实战最大坑) → AddChart2 默认图表只带 3 个系列,ListObject 扩列不会让图表跟随扩展(取
SeriesCollection(4)即炸);全文档表现为"恰好只有 >3 系列的图失败、≤3 系列全成功"——这个模式就是此坑指纹。修法:写完数据后显式重建系列(改 Values/XValues 公式 + NewSeries 补足 + 断言 Count==nser),见「原生可编辑图表」路线二;COM 图表回归脚本(未随本包发布)已固化 5/4 系列回归。 - 🚨 COM 路线第二张 AddChart2 报"网格已打开" → 图表数据网格状态机:插入成功后 Word 弹内嵌数据网格,未被接管(
cd.Activate()+ 写数)前下一次插入必炸(同一 scode,极易误诊为"插入失败")。铁律:插入后立即 Activate 写数据;Excel 进程全程不 Quit,保存后再统一退。 - Word 保存报"文件许可权错误" → 八成是 Python 侧 zip/docx 句柄未关(无 FILE_SHARE_WRITE,全程阻塞 Word 写盘)。zip 必须 with 即用即关;另查异常退出残留三件套:
~$xxx.docx锁文件 + 隐形 WINWORD + 僵尸"Microsoft Word 中的图表 - Excel" OLE 实例,tasklist //v看窗口标题后taskkill //F //PID精清。 - Word 的 ExportAsFixedFormat 报错 → ExportFormat 用 17(wdExportFormatPDF;Excel 的同名方法才是 0);路径必须反斜杠(正斜杠报"只读文件");dynamic dispatch 偶发 AttributeError 时用 EnsureDispatch 或重试。
- 验证图表是否渲染成功,别拿 pdfplumber 的 images/curves 计数 → 矢量图表在导出 PDF 里没有 image 对象(images=1 是页眉徽标),且 400pt 大图在窄页上会排到标注文字的前一页(按标注文本定位页码会查错页)。正确姿势:fitz 渲染页面 PNG 亲眼目检;
extract_text出现轴刻度/标题文字是"图表在渲染"的关键线索。
Resources
scripts/(转换主流程)
convert_to_docx.py:端到端转换引擎(类型识别 → 结构还原 → Word 生成),含运行报告、Markdown 预览与图表清单;支持混合横竖版 PDF 自动分节;v1.4.0 起同一 PDF 单次打开解析(解析复用见references/pipeline.md§2.6)。inject_figure_desc.py:把图表智能理解的说明按题注写回已生成的 Word(幂等,无需重跑流水线)。native_charts.py:原生可编辑图表注入引擎(Word 手写 DrawingML + 嵌入数据工作簿;Excel 配方见「原生可编辑图表」章节)。word_com_charts.py:Word COM 原生图表替换引擎(v1.3.0+)——锚点定位已有静态图原地换成原生可编辑图,全 XlChartType,4-5+ 系列自动重建;需 Windows + 本机 Word。build_index.py:语义切片 + 双路索引构建。retrieve.py:双路检索与问题路由。verify.py:闭环反查核验(双路 RRF 融合,向量缺失自动降级为 BM25 并标注)。
scripts/(PDF 工具,v1.1.0 并入)
pdf_crypto.py:PDF 加解密与权限设置。pdf_form.py:AcroForm 表单读取与填写。pdf_sign.py:视觉签章 + 可选 PKI 数字签章。pdf_search.py:单 PDF 关键词检索(免建索引)。
Excel 输出(无独立脚本,方法论内置)
PDF→Excel 转换复用本技能的「已核验干净数据覆盖」原则:先用 pdfplumber 提取并逐格核验表格数据,再用 openpyxl 构建真数值单元格 + SUM 公式 + 图片嵌入(PIL 保宽高比)的单 sheet Excel。详见上方「生产级 Excel 输出规范」章节。
references/
pipeline.md:流程细节、启发式阈值与字段说明(按需加载)。
回归基线(scripts/回归脚本 + 闭环回归脚本(未随本包发布),非交付件,禁止清理)
合并 v1.1.0 时建立的回归测试,改脚本后应重跑以确认链路未破:
主链路回归脚本(未随本包发布):造一份含信笺头+标题+表格+页脚的小 PDF,跑通
convert_to_docx → build_index → retrieve → verify全链路,断言四步 rc=0、
六项产物存在、verify_report.json内容合法。工具回归脚本(未随本包发布):对 4 个工具脚本(crypto/form/sign/search)逐条跑 CLI 并断言产出文件。-
闭环回归脚本(未随本包发布):内容级闭环核验(不只是"能不能跑",而是"产物对不对")。
78 项断言覆盖字体/字号/颜色还原(含红字CC0000)、信笺头与页脚文字、表格数值、
检索 Top-1 命中含金额的表格 chunk、降级标记bm25_only等,以及[7]内容保真回归:
页首正文段不得被静默丢弃、文档标题不得被搬进页眉、合并单元格表不得崩溃、
单级编号正文句不得误判为标题、多页文字信笺头须进页眉且正文不重复。
这是 v1.1.1 三个真实缺陷的唯一有效捕获手段,任何取色/切分/标题逻辑改动后必跑。 -
原生图表回归脚本(未随本包发布):原生图表注入回归(v1.2.0)——占位替换、chart/book 部件落盘、
CT 注册、轴结构、numCache 值断言、嵌入簿 openpyxl 重开、docx 可重开。
首跑即捕获嵌入簿</c:r>闭合标签真 bug。
任何native_charts.py改动后必跑。 -
COM 图表回归脚本(未随本包发布):COM 替换路线回归(v1.3.0)——锚点未命中/锁文件两条负向路径、
5 系列簇状柱 + 4 系列折线替换(>3 系列重建主坑)、c:ser 计数、numCache 值与源一致、
嵌入簿 openpyxl 重开、docx 重开,共 19 项断言。
无 pywin32 / 本机无 Word 时打印 SKIP 并以 0 退出。
任何word_com_charts.py改动后必跑。
# 主链路回归(未随本包发布) # 主链路回归
# 工具脚本 CLI 回归(未随本包发布) # 4 个工具脚本回归
# 内容级闭环回归(未随本包发布) # 内容级闭环(78 项)
# 原生图表注入回归(未随本包发布) # 原生图表注入回归(v1.2.0)
# COM 图表回归(未随本包发布) # COM 替换路线回归(v1.3.0,无 Word 自动 SKIP)
# 均以 VERDICT: PASS / FAIL 收尾,可直接用作 CI 断言
已知环境事实(2026-09-18 实测):本机 sentence-transformers 未安装,故
build_index.py 会明确报告 deps_missing 并降级为纯 BM25 单路;retrieve.py /
verify.py 的输出会标注 bm25_only。这不是故障,是预期降级。若要启用双路语义召回:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple sentence-transformers
# 注意:首次使用需联网下载 paraphrase-multilingual-MiniLM-L12-v2(约 470MB)
若模型拉取失败,build_index.py 会报 model_unavailable 并给出 HF_HOME 提示,
不会静默伪装成"未启用"(这是 v1.1.0 修的行为,旧版会把三类失败吞成一种)。