国网双层PDF制作
把投标文件(Word / 标书)变成国网平台"可直接上传的彩色二值双层PDF",并在制作前先检查。
全部纯本地离线,不联网、不上传任何文件、不调远程 API。
解决什么痛点
- 手工做双层PDF容易超容量:WPS"高清"导出≈300dpi,300 多页会到 150MB+,直接超过单包 100Mb 上限。
- 书签容易丢:手工流程要"导出书签→转图片型PDF→再导入",多转一次 PDF 书签就断。
- 做完才发现编号写错:文件名写"标02"、文档里却写"标03",PDF 都做完才看出来,整份要重做;而错编号一旦上传就是实质性错误。
- 不清楚到底符不符合要求:容量、页码连续性、页面方向、图片分辨率,逐条手工核对很费时。
核心能力
| 能力 | 说明 |
|---|---|
| 招标条款全量扫描 | 八分类输出条款原文:文档 / 图片 / 分辨率 / 容量 / 双层PDF / 方向 / 色彩 / 签章 |
| Word 制作前体检 | 抓分标编号矛盾、张冠李戴的公司名、漏签漏盖、模板残留、方向错误、日期逻辑 |
| 一键流水线 | 体检 → 图片审计 → 按显示尺寸压缩 → 导出PDF(带书签) → 整页渲染+叠文字层 → 自检 |
| 成品自检 8 项 | 页数 / 体积 / 图像层ppi / 文字层可复制 / 书签 / 未加密 / 横向页 / 页码从1连续 |
| 扫描件补层 | 证书、合同等扫描件做成可检索 PDF(纯离线 OCR,可选) |
| 环境自检 | 换机器先跑,明确缺什么依赖、Word/WPS 是否可用 |
使用前提
- Windows + Microsoft Word 或 WPS(用于导出带书签的 PDF)
- Python 3.9+;必需依赖
PyMuPDF、Pillow、python-docx - 可选依赖
pywin32(Windows 调 Word 导出)、rapidocr-onnxruntime(扫描件 OCR 补层) - 不需要联网、不需要 API Key
数据与隐私
全程本地运行,不联网、不上传任何文件、不调用远程接口,投标文件内容不离开本机。
已知限制
- 需要 Windows + Word/WPS;无 Word 时可改用
soffice --headless --convert-to pdf,书签与页码需人工确认。 - 内置国网条款表是实证值,不同批次/平台可能不同,每次仍应用扫描脚本核对本项目实际条款。
- 体检存在正常误报(采购单位名、开户行名、"(签字)"空白会标 ⚠),需回原文确认。
- OCR 为可选,需额外安装且耗时明显更长。
作者与联系
- 作者:安朋康|348760700@qq.com
许可
MIT
🔴 铁律:先检查,再制作
顺序不可颠倒——先查招标文件、再体检 Word,都没问题才转 PDF。
0 环境自检(env_check) 确认依赖与 Word/WPS 可用
1 查招标文件(采购公告优先) 明确容量 / 格式 / 页码要求
2 体检 Word(_docx_precheck) 抓矛盾与废标隐患,有 🔴 立即停
3 制作双层PDF(bid_pdf_pipeline,内置 1+2,🔴 自动中止)
4 成品自检 页数/体积/ppi/文字层/书签/加密/方向/页码
为什么:PDF 做完才发现"分标编号写错""文件名与文档不一致",整份要重跑(压缩+渲染 3~4 分钟);
错编号一旦上传就是实质性错误。而体检只要几秒。
步骤 1~4 已合成一键流水线,内置强制拦截。
确认警告无害后可加 --force 继续;跳过体检用 --no-precheck(不推荐)。
0、环境自检(换机器或首次使用时先跑)
python scripts/env_check.py # 人类可读
python scripts/env_check.py --json # 机器可读
必需依赖:PyMuPDF、Pillow、python-docx。可选:pywin32(Word 导出)、RapidOCR(扫描件补层)。
🔴 必须用装有这些依赖的那个解释器。本机若用 venv,就是 venv 下的 python.exe,
直接用系统 python 会报 ModuleNotFoundError。后续所有命令都用同一个解释器;
bid_pdf_pipeline.py 内部用 sys.executable 调子脚本,会自动继承,无需逐个指定。
无 Word/WPS 时(Linux / 未装 Office):路线 A 的步骤"导出 PDF 并生成书签"会失败,
改用 soffice --headless --convert-to pdf,并手工确认书签与页码连续性。
关于可选依赖(按需安装,不必预装):
- 路线 A(整份响应文件,绝大多数场景)只需必需依赖,不需要 OpenCV/RapidOCR。
- RapidOCR 仅在路线 B(单张扫描件补文字层)才需要,按需
pip install rapidocr-onnxruntime。
不预装可避免技能包膨胀数百 MB、安装变慢;env_check.py会把它标为"可选",缺失不阻断。
一、Step 1:查清楚招标要什么
🔴 格式要求主体常在「采购公告」里,专用文件只是补充;通用文件.doc 往往只有一句"详见采购公告"。
python scripts/_scan_format_reqs.py <招标文件目录>
八分类输出条款原文:A文档 B图片 C分辨率 D容量 E双层PDF F方向 G色彩 H签章。
条款表、出处陷阱、必问三数、容量与 ppi 对应关系见 references/招标格式条款与核查.md。
二、Step 2:Word 制作前体检(必做)
python scripts/_docx_precheck.py <输入.docx> --tender <招标文件目录> --report 体检.md \
--company <本公司全称> --legal <法定代表人姓名> \
--exclude-companies "招标人A,代理机构B"
检查五类:A 身份一致性 / B 内容完整性 / C 模板残留 / D 格式合规 / E 日期逻辑。
判定:有 🔴 → 禁止转 PDF,先改 Word;只有 ⚠ → 人工确认后继续。
身份类参数按需传入,不填即跳过(脚本不含任何写死的公司名,换公司/换地区直接可用):
- --company 本公司全称 → 未出现则报 🔴;不传则跳过并给 ℹ 提示。
- --legal 法定代表人 → 不传则跳过。
- --exclude-companies 招标人/代理等无需告警的主体,逗号分隔。
⚠ 报"出现需确认的公司名"时,必须回原文看上下文再判,别直接判张冠李戴
(采购单位名、开户行名都是正常命中)。判别经验与实战案例见
references/踩坑清单与体检调教.md。
三、两条制作路线(先判断)
| 场景 | 对象 | 路线 |
|---|---|---|
| A. 整份响应文件(商务标/技术标 docx)⭐常用 | 一个 docx | 整页渲染图像 + 原生文字层 |
| B. 单张扫描件(证书/合同/发票) | 图片或图片型 PDF | 图片 + OCR 文字层 |
🔴 整份投标文件走 docx→PDF 路线,不是给扫描件补 OCR 的路线。
四、Step 3:一键流水线(路线 A)
python scripts/bid_pdf_pipeline.py <输入.docx> <输出目录> \
--ppi 200 --quality 88 --name 标02商务 --limit 100 \
--tender <招标文件目录> --company <本公司全称> --legal <法定代表人姓名>
内部:体检 → 图片审计 → 按显示尺寸压缩 → Word COM 导出 PDF(生成书签) → 整页渲染 + 复制书签 + 叠文字层 → 自检报告。
实测:312 页 / 300 图 / 全程约 150 秒。
参数决策
| 参数 | 推荐 | 说明 |
|---|---|---|
--ppi |
200(清晰优先,默认)/ 150(官方推荐)/ 96(最省) | ⚠ 同时控制"图片压缩"和"整页渲染",必须同一个值 |
--quality |
78~88 | JPEG 质量 |
--limit |
平台上限,默认 100 | 自检判超标 |
--skip-compress |
图片已够小时 | 跳过压缩 |
--force / --no-precheck |
谨慎使用 | 忽略 / 跳过体检 |
> ppi 必须统一:若压缩 150、渲染 200,图片区只有 150ppi 信息量,渲染再高只是放大糊图。
五、Step 4:成品自检(流水线自动输出)
| 检查项 | 判定 |
|---|---|
| 体积 | ≤ 平台上限 |
| 图像层 ppi | ≥96 |
| 文字层 | 可抽取、可复制 |
| 书签 | 存在(目录可跳转) |
| 加密 | 否(平台要求勿设密码) |
| 横向页 | 0(招标要求纵向) |
| 页码 | 首页为"1"且连续(自动识别页脚页码并校验尾页−首页+1=总页数) |
实测样例:312 页 / 72.5MB / 全 200ppi / 9940 字符 / 46 书签 / 未加密 / 0 横向页 / 首页页码"1" → ✅ 通过。
扫描件单独体检:python scripts/dualpdf.py -i <目录> --check -r --csv 体检清单.csv
六、脚本索引(scripts/)
| 脚本 | 作用 |
|---|---|
env_check.py |
⭐环境自检(首次使用 / 换机器先跑) |
bid_pdf_pipeline.py |
⭐一键流水线(含体检,路线 A 推荐入口) |
_docx_precheck.py |
Word 制作前体检(矛盾 / 废标隐患) |
_scan_format_reqs.py |
招标文件格式条款全量扫描(八分类) |
_docx_img_audit.py |
图片审计(原始像素 / 显示尺寸 / 有效 ppi / 各档位体积估算) |
_docx_compress_imgs.py |
按显示尺寸重采样 --ppi --quality --gray |
_docx_to_pdf.py |
docx→PDF,打印质量 + 生成书签(需 Word/WPS) |
_make_dual_layer_pdf.py |
整页渲染 + 复制书签 + 叠文字层 |
dualpdf.py |
扫描件→双层PDF(OCR),--check 体检 |
文字层默认用原生矢量文字(100% 准确、约 80 秒),而非 OCR(315 页约 68 分钟且有错字)。
七、交付提醒
- 源文件在工作范围外(如用户本地目录)时只读不写,产出落到用户指定的工作目录。
- 交付前核对分标编号:文件名、文档内编号、招标文件三者一致。
- 平台写"容量以平台设置为准"时,必须向用户确认实际数字再定 ppi 档位。
- 自检超标时的降级顺序:200 → 150 → 96,不要一上来就用最低档。
- 保留中间产物(压缩后的 docx、单层 PDF),换档位时不必从头跑。
八、故障排查
| 现象 | 处理 |
|---|---|
报 ModuleNotFoundError |
用错解释器;改用在装依赖的那个(常是 venv),先跑 env_check.py |
| Word 导出失败 / 卡住 | 确认已装 Word 或 WPS;无 Office 时改用 LibreOffice 导出 PDF |
| 成品体积超上限 | 降 ppi 重跑:200 → 150 → 96(中间产物可复用,不必从头) |
| 文字层为空 | 确认源 docx 是文字版而非整页图片;走 OCR 路线需装 RapidOCR |
| 书签丢失 | 确认只转一次 PDF(本流水线自动继承);勿中途从 Word 重新转 |
| 体检报 🔴 但自认无误 | 回原文确认;确属误报可加 --force 放行 |
| 页码自检提示"未检测到" | Word 里未插入页码,需在页脚插入连续页码后重做 |
| 图片偏糊 | 提高 --ppi(并确认压缩与渲染用同一个值) |
参考文件
references/招标格式条款与核查.md— 条款全表、出处陷阱、必问三数、容量与 ppi 对应references/踩坑清单与体检调教.md— 九个已踩的坑、体检五类、调教要点、⚠ 判别经验