万用套 Word 模板(Universal Word Template · v1.1)
把「模板格式」和「正文内容」彻底解耦的通用引擎:先学习任意模板的全部非内容要素,
再把它套用到另一份文档的内容上,产出与模板版式逐项一致的新文档。不依赖任何特定模板——
保时捷 SOP、制度文件、红头文件、简历、报告封面…… 只要是 docx,都能学、都能套。
核心心智模型
- 模板 = 格式载体:只取它的"骨架"(页面/页眉页脚/字体/段落/标题样式/编号/表格/颜色/封面目录结构),
不取它的正文文字(封面标题、公司名等"占位性文字"如何处置见下方"内容边界")。 - 目标文档 = 内容载体:只取它的"血肉"(正文段落、章节文字、表格数据),丢弃它自己的格式。
- 产物 = 模板骨架 + 目标血肉。
使用场景(触发后按此执行)
场景 A:先给范本,后给内容(两段式)
- 用户给一份「模板/范本」docx → 立即运行
extract_template.py,把格式抽成template_profile.json并回报已学到的要素清单。 - 用户再给一份「内容」docx → 运行
apply_template.py套用,产出新文档并审计。
场景 B:同时给两个文件(一段式)
- 用户一次上传两个 docx,并指明哪个是模板、哪个是内容。
- 依次执行 extract → apply → verify。
前提条件(第一步,必须先问,不可跳过)
在开始任何抽取/套用之前,必须先询问用户用哪个办公软件打开、编辑最终文档:
> 请问你最终用 WPS 还是 Microsoft Office (Word) 打开、编辑这份文档?
原因与影响:
- 字体:WPS 对中文字体的 fallback 与 Office 不同(WPS 更宽容宋体/黑体/仿宋;Office 缺字体时走 Calibri/等线)。
套用时需据此决定中文字体映射(例如 Office 下 仿宋_GB2312 可能需回退到 仿宋)。
- 分页/行距渲染:两者行高算法有差异(WPS 与 Word 的 exact 行距对中文渲染的像素级不同),
审计脚本需按目标软件判断"可接受差异"。
- 兼容模式:WPS 对某些 Word 域(如 TOC 域、PAGEREF)的刷新行为不同。
询问方式用 AskUserQuestion,选项给 WPS / Microsoft Office (Word) 两个(可加"两者都要兼容")。
端到端流程
[触发 + 用户提供模板(±内容) + 目标软件已确认]
▼
[1. 抽取] python scripts/extract_template.py <模板.docx> -o template_profile.json
提取:页面/边距/纸张/分栏/页眉页脚/默认字体/段落缩进行距/标题样式树/
编号规则/表格样式与边框/主题色/封面与目录结构
▼
[2. 套用] python scripts/apply_template.py <内容.docx> <template_profile.json> -o <输出.docx>
保留内容段落/表格数据,重建所有格式骨架
▼
[3. 审计] python scripts/verify_output.py <输出.docx> <template_profile.json> --target wps|office
逐项断言:页面/字体/行距/标题/编号/表格 与模板一致(目标软件相关项按对应容差)
▼
[4. 交付] 输出 docx + present_files;提示用户用确认的软件打开复核
内容边界(关键:什么算"格式"、什么算"内容")
抽取时必须明确区分,避免把模板的正文文字错套到目标文档:
| 要素 | 归属 | 处理 |
|---|---|---|
| 页面尺寸/边距/页眉页脚距离 | 格式 | 抽取 |
| 页眉/页脚文字(如公司名、Classification、页码) | 格式(可占位) | 抽取,但标记为"页眉页脚",套用时原样带入或按用户要求改 |
| 默认字体/字号/颜色 | 格式 | 抽取 |
| 标题样式(Heading 1/2/3 的字体/编号/颜色) | 格式 | 抽取 |
| 正文段落文字 | 内容 | 来自目标文档 |
| 封面标题/公司名/logo | 边界 | 默认带入模板的封面结构,文字提示用户确认(公司名/标题常需替换为目标文档的) |
| 表格结构(列数/边框/表头样式) | 格式 | 抽取;表头文字视为模板占位,单元格数据来自目标 |
| 段落间距/缩进/对齐/行距 | 格式 | 抽取 |
封面/页眉等"半内容半格式"要素:默认照模板结构带入,但把可变的文字(公司名、文档标题、编号、日期)
列为 placeholders,套用后明确列出这些占位符让用户确认或替换。
工具脚本(skill 自带,引擎用 file 定位 skill 目录,可独立跑)
| 文件 | 用途 |
|---|---|
scripts/extract_template.py |
抽取任意 docx 的格式骨架 → template_profile.json |
scripts/apply_template.py |
把格式骨架套到目标 docx 上 → 新 docx |
scripts/verify_output.py |
审计产物与模板格式一致性(分 wps/office 容差) |
scripts/word_utils.py |
共享工具:python-docx 封装、字体/段落/表格读写、样式树遍历 |
依赖:python-docx(在隔离 venv 内安装,勿污染全局)。字体名、行距等一律用 EMU/Pt 精确值,不靠视觉猜测。
格式抽取清单(extract 必须覆盖,越全越好)
- 页面:纸张 w/h、四边距、页眉页脚距离、分栏数、
differentFirstPage、页码起始/格式。 - 节(section):多个 section 各自设置(模板常有封面节+正文节)。
- 默认字体:Normal 样式的 ascii/hAnsi/eastAsia、字号、颜色、字符间距。
- 样式树:所有段落/字符样式名及其字体/段落格式,重点是 Heading 1..N、Title、正文。
- 段落级:默认段落 spacing(before/after)、line spacing(exact/多倍)、首行缩进/左缩进、对齐、widow/orphan。
- 编号:numPr(编号定义、缩进、格式
%1.等)、多级列表关联的样式。 - 页眉页脚:每节每类型(first/default/even)的段落文字、字体、表格、logo 图片、域(页码/TOC)。
- 表格:表格样式、总宽/对齐/布局(fixed/autofit)、边框(单/双线/粗细/颜色/space)、
单元格边距(tblCellMar)、列宽(tblGrid + 每格 tcW)、表头行(底纹 fill + 加粗 + 垂直对齐 + 段落/字体)、
正文单元格(段落格式含精确行距 + 字体)。抽取时用"第一行是否带底纹/整行加粗"判定表头,
避免把普通首行误当表头。 - 颜色/主题:theme color(accent1..6)、主题字体。
- 封面/目录:封面字段布局、目录是否为 TOC 域或静态行+页码。
套用策略(apply 关键规则)
- 不动内容:目标文档的正文段落文字、表格单元格数据、图片内容原样保留;只重设其格式属性。
- 重建骨架:删除目标文档的页面/样式/页眉页脚,按
template_profile.json重建。 - 样式映射:目标文档正文的"标题感"段落(粗体/居中/编号开头)映射到模板对应 Heading 样式;
无法判定的保留为正文但套用正文样式。 - 占位符回填:封面/页眉中的
placeholders用目标文档实际文字(标题、公司名、日期)回填;
目标文档没有的,保留模板占位并提示。 - 字体回退:按目标软件(wps/office)应用
font_fallback映射,避免目标机器缺字体。 - 表格套用:模板表数 == 目标表数 → 按位置一一对应;否则按列数最近匹配。列数一致才套列宽(跨列合并格跳过)。
表头/正文单元格的字体、段落(含精确行距)一并套用;单元格数据文字原样保留。
技术要点(写脚本时注意,python-docx 的坑)
- 单位:
w:pgSz/pgMar、w:spacing(@before/@after)、w:ind、tblCellMar、exact 行距都是 twips(1/20 pt);
w:sz/w:szCs是 半点(1/2 pt);行距w:line在lineRule=auto时是 1/240 行倍数。换算勿搞混。 - Length/Twips 继承 int:
section.page_width返回Twips(继承 int,内部存 EMU),
取 twips 必须hasattr(x, "twips")先于isinstance(x, int),否则int(x)会错误返回 EMU。 - 样式对象属性不全:
CharacterStyle/_NumberingStyle无font/paragraph_format,一律hasattr保护。 - 行距 float:
style.paragraph_format.line_spacing可能是 float(倍数)或Length(精确),
写入 XML 属性时须str()转换;抽取时用{"multiple": x}/{"exact_twips": n}区分。 w:orient:直接读原始枚举字符串(landscape/portrait),不要str(枚举)生成ORIENT.LANDSCAPE (1)。- OOXML 子元素顺序:
w:tblPr/w:tcPr/w:pPr/w:rPr的子元素须按 schema 顺序插入(WPS 比 Word 严格),
用_set_child_ordered统一处理。 - 标题 run 字体:套用段落 run 字体时,标题段应取标题样式字体、正文段取 Normal/默认字体,勿一律套 Normal。
禁止事项
- 不许把模板的正文文字硬塞进目标文档(模板正文不是内容)。
- 不许用视觉/截图猜测格式——一律读 docx XML 精确值(EMU/Pt/十六进制色)。
- 不许跳过"询问 WPS/Office"这一步直接开跑。
- 不许在全局环境 pip install;依赖装进隔离 venv。
- 套用后必须跑 verify 审计并回报通过/差异项,不得只交付不校验。
校准日志
- v1.0.0(2026-09-02):初版,从
porsche-sop-formatter抽象出通用「抽取→套用→审计」三阶段架构,
去保时捷专用化,支持任意 docx 模板,加入 WPS/Office 前置确认与字体回退。 - v1.1.0(2026-09-02 晚):补全表格格式抽取/套用/审计(边框/单元格边距/列宽/表头底纹+加粗+垂直对齐/
单元格段落精确行距+字体),表格套用从占位实现落地。修复 v1.0 单位换算 bug(w:spacingtwips→pt 错 20 倍)、
标题 run 错套 Normal 字体、w:orient非法枚举串。全部 XML 写操作改按 OOXML schema 顺序插入(WPS 兼容)。
新增scripts/内word_utils.py的表格工具与主题色解析。端到端:带表格模板 26/26、无表格 12/12 PASS。