# 用同案件已有 docx 作母版生成"全新文书"——页眉/边框继承陷阱

## 场景
同一案件已有一份成稿文书（如检察监督申请书 v9），需要再做一份**不同类型**的全新文书（如给法院的《情况反映》）。为保证字体、字号、行距、页边距、样式与申请书**同源**，最稳的做法是：把申请书 docx 当母版，克隆它的段落模板（标题段/正文段/落款段/附件段的 pPr+rPr），清空 body 后用模板段重建全文。

> ⚠️ 这套手法适用于**任何"用 A 文书作母版生成 B 文书"**，不限于《情况反映》。2026-06-25 郭同学案再次踩中：用万禹案**再审申请书**作母版生成**答辩状**，header2.xml 原样继承了"申 请 人：上海万禹实业… / 再审申请书 / 生效判决案号：（2025）沪01民终15828号"——OnlyOffice 渲染第 1 页页眉赫然是别案的当事人和案号。清空页眉法（见下）验证有效：删 header2.xml 所有 run、重新打包后页眉纯净。**判别哪个 header 文件有文字**：遍历 `word/header*.xml`，读各自 `w:t` 拼出文字，非空的那个才是要清的（母版常有 header1/2/3 三个，只有一个带内容）。

这套"克隆段落模板 + 替换 body"的手法对**正文**是对的、省心，但有一个隐蔽的坑：

## 坑：header/footer 和 pBdr 横线会原样继承，且常常张冠李戴
- 替换 `word/document.xml` 的 body 只动了正文。**`word/header1.xml`、`word/footer1.xml` 原封不动**被继承。
- 实测（2026-06-23 邹家案）：申请书 v9 的 header1.xml 写的是"**申请人（被告）… / 申请监督民事诉讼案号：… / 受理法院：…**"——这是**检察监督申请书的页眉**。套到《情况反映》（受文机关是法院本身）上，称谓完全错配（"申请监督""受理法院"对法院内部监督渠道不成立）。
- 更隐蔽的是**页眉横线**：清掉页眉文字后，OnlyOffice 渲染里页面顶端仍残留一条贯穿左右的黑线。它不是文字，是页眉段落 `pPr` 里的**段落下边框 `<w:pBdr>`**（页眉样式自带）。只删 run 文字删不掉它。

## 交付前必查（母版法专用三查）
凡是"用 A 文书作母版生成 B 文书"，OnlyOffice 渲染后**必须逐页看页眉页脚**，确认：
1. 页眉文字是否属于 B 文书的类型（不是 A 残留的称谓/案号格式）；
2. 页眉/页脚有无 A 残留的横线、单位名、修订标识；
3. 落款受文机关、抬头与 B 的收件对象一致。

vision 看第 1 页时，把"页眉区域内容"作为独立一项问出来——它最容易被正文的整洁掩盖过去。

## 修法（清空页眉 + 去横线）
默认推荐**清空页眉**（内部反映材料正文已含案号当事人，页眉非必需，留空最干净不会错配）。纯 zipfile+lxml：

```python
import zipfile, io
from lxml import etree
W='http://schemas.openxmlformats.org/wordprocessingml/2006/main'
def w(t): return f'{{{W}}}{t}'

zin = zipfile.ZipFile(src)
h = etree.fromstring(zin.read('word/header1.xml'))

# 1) 清空页眉所有 run 文字（保留空段结构，别删整段——破坏 schema）
for p in list(h.iter(w('p'))):
    for r in list(p.findall(w('r'))):
        p.remove(r)

# 2) 删 pBdr（横线的真正来源）——只删文字删不掉这条线
for ppr in h.iter(w('pPr')):
    pBdr = ppr.find(w('pBdr'))
    if pBdr is not None:
        ppr.remove(pBdr)

# 3) 去掉 pStyle 引用（页眉样式常自带下边框，连根拔）
for ppr in h.iter(w('pPr')):
    ps = ppr.find(w('pStyle'))
    if ps is not None:
        ppr.remove(ps)

new_h = etree.tostring(h, xml_declaration=True, encoding='UTF-8', standalone=True)
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zo:
    for n in zin.namelist():
        d = zin.read(n)
        if n == 'word/header1.xml': d = new_h
        zo.writestr(n, d)
open(src, 'wb').write(buf.getvalue())
```

若要**保留页眉但改成中性表述**（方案 B），把 run 文字替换成"案号：（…）… / 反映人：…"这类不带"申请监督""受理法院"的中性字段即可——但默认先问用户走 A 还是 B，别擅自保留错配页眉。

## 关掉修订模式（全新文书非修订态）
母版若是修订态（settings.xml 带 `<w:trackRevisions/>`），生成全新文书时要去掉它——新文书是全新成稿不是改稿，不该带修订痕迹：

```python
if n == 'word/settings.xml':
    s = etree.fromstring(d)
    tr = s.find(w('trackRevisions'))
    if tr is not None: s.remove(tr)
    d = etree.tostring(s, xml_declaration=True, encoding='UTF-8', standalone=True)
```

## 段落模板克隆要点（正文部分，已验证可用）
- 从母版抽各类型段落各一个作模板：标题（居中 bold sz30）、正文（首行缩进 fl480 sz24）、受文机关（顶格 bold sz24）、落款（右对齐 sz24）、附件标题（顶格 bold sz24）、附件项（fl480 sz24）。
- `mk()` 构造段落时：深拷贝模板段 → 删掉模板里的旧 run/ins/del → 删 numPr（避免继承自动编号）→ 强制字体（eastAsia=仿宋、ascii/hAnsi=Times New Roman）→ 按需 set/unset bold → 塞入新文字 run（`t.set('{http://www.w3.org/XML/1998/namespace}space','preserve')`）。
- 顶格段（受文机关、"丽水市…法院："）用 `no_indent=True` 删掉 ind，否则继承首行缩进会缩进两格。
- body 清空后把新段 `insert` 到 `sectPr` 之前（sectPr 必须保留在 body 末尾，决定页面尺寸/页边距）。
- 字体属性交付前用 zipfile 逐 run 统计核验：CJK 全仿宋、LATIN 全 Times New Roman，0 回退。
