OFD 转 PDF 专用技能
目标
直接交付可用的 PDF,不允许只讲解原理或给伪代码。转换结果必须保留:
- 页面尺寸与页面数量
- 表格线、矢量图形和文字
- 图片、二维码
Annots注释层中的水印图章(这是最容易丢失的部分)
执行步骤
- 确认输入是单个
.ofd还是目录;输出路径是否已有同名 PDF。 - 优先使用已安装的 CLI(本仓库
pip install -e .后):
ofd2pdf -i ./input.ofd -o ./output.pdf --force
ofd2pdf -i ./ofd-dir -o ./pdf-dir --force
也可:
python scripts/ofd2pdf.py -i ./input.ofd -o ./output.pdf --force
python -m ofd_to_pdf -i ./ofd-dir -o ./pdf-dir
- 单文件可省略
-o,默认写到与 OFD 同目录的同名.pdf。 - 转换后必须用下方验证命令检查矢量、图片和文字,不能只看脚本日志。
- 不要另写一套解析器;改逻辑前先读
skill/references/lessons.md。
转换原理
- OFD 本质是 ZIP,核心是
Document.xml。 Pages→ 页面Content.xml;TemplatePage→ 模板背景。- 资源来自
PublicRes.xml/DocumentRes.xml等,含字体、图片、DrawParam。 - 坐标单位毫米;PDF 点 = 毫米 ×
72 / 25.4;原点均为左上角。 - 图片位置:
Boundary原点 +CTM位移;尺寸取自CTM缩放。 - 注释层水印在
Annots,必须画在正文之上。
关键坑位
Shape.finish()之后必须Shape.commit()。- 路径命令解析:读数字与遍历命令共用同一游标,禁止一边
pop一边按旧索引遍历。 PathObject未显式Stroke="false"时应描边。- 无明确填充色时不要默认填黑。
- 红章常在
Annots,只渲Content会丢章。 - 注释
Appearance Boundary是子对象原点偏移。 - 字体:优先 OFD 内嵌;失败则系统 CJK(Win/macOS/Linux)再 PyMuPDF 内置 CJK。无
CGTransform/Glyphs时必须用系统/内置回退,否则 scrub 后的假字会在 PDF 变空白。 - 带
Alpha的注释文字要传fill_opacity/stroke_opacity。
验证清单
至少抽查:文本层发票 fixture,或用户提供的样例。
ofd2pdf -i tests/fixtures/sample_invoice_text.ofd -o /tmp/out.pdf --force
import pymupdf
doc = pymupdf.open("/tmp/out.pdf") # 或你的输出路径
page = doc[0]
print("页数", len(doc))
print("矢量对象", len(page.get_drawings()))
print("图片数", len(page.get_images()))
print("文字样例", page.get_text()[:200])
普通发票:get_drawings() 应有表格线;带图章:get_images() 非空。失败先查 skill/references/lessons.md。
失败经验优先
用户反馈不一致、图章/表格线缺失、图标变形时,不要只重跑:按 lessons.md 逐项检查并修复,新失败补写进该文件。