# 诉讼文书的修订态技法（contract-editor 库在合同以外文书上的复用）

ContractEditor 库不止用于合同——审/改诉讼文书（监督申请书、起诉状、答辩状等）同样适用。本文记录 2026-06-23 邹家民事诉讼监督申请书审改中验证过的几招，都用 OnlyOffice x2t（Doro/Maggie 实际引擎）渲染核对过。

## 1. 大段改写用「整块 del+ins」，不用字符级 diff（markup 可读性铁律）
`tracked_replace` 是字符级 diff（CJK 每字一 token + difflib）——**补字/小改**（错别字、补一个"在"字、称谓换词）用它，markup 干净。
但**大段改写**（整句重写、换论证）若用字符级 diff，新旧文本大量字符重合，markup 会交错成一团（"未经~~及~~法庭审理""一百二十八条~~切~~国家机关"），Doro 在 OnlyOffice 看修订态根本读不下去。**接受修订后的最终文本虽正确，但修订态不可读 = 不合格交付**（Doro 有格式洁癖，看的就是 markup）。
- **正解**：对整句/整段改写，做「整块删 + 整块插」——`[<w:del>旧整句</w:del>][<w:ins>新整句</w:ins>]`，markup 显示为一条删除线旧句紧跟一条下划线新句，清清楚楚。
- 实现：复制 `tracked_replace` 的定位逻辑，但不跑 difflib，直接 `_mk_del(old_text)` + `_mk_ins(new_text)` 整块插。判据：**新旧文本相似度高、改动跨度大 → 整块；纯增删几个字 → 字符级**。

## 2. 称谓/词替换也要整词块替换，别让共享字符碎裂
把"法**庭**"改"莲都法**院**"时，"法"字共享，字符级 diff 会渲染成"莲都法~~庭~~院"（接受后对，markup 脏）。
- **正解**：整词 `tracked_block_replace("本案法庭向申请人送达", "莲都法院向申请人送达")` → markup 是干净的[删旧短语][插新短语]。
- 同理坑：替换前先分类全文每处目标词——actor 指代（要改）vs 法条/术语原文（如"法庭审理""在法庭上出示"=不能动）。grep 出所有命中，逐个判，别一刀切 replace_all。

## 3. 整段删除（让自动编号重排）——库没有，需自加 `tracked_delete_paragraph`
合并两个自动编号请求项（删一项、后项自动续号）时，要的是**段落级修订删除**：段内每个 run 包进 `<w:del>`，**且段落标记也要标删**——在 `pPr/rPr` 里插一个 `<w:del>`。这样接受修订后整段连段落标记一起消失，自动编号从 一二三四 重排成 一二三。
```python
def tracked_delete_paragraph(self, search_text):
    p = self.find_para(search_text)
    for r in list(p.findall(qn('r'))):
        # 每个run的w:t搬进新建<w:del><w:r><w:delText>
        ...
    ppr = p.find(qn('pPr')) or 新建
    rpr = ppr.find(qn('rPr')) or 新建
    rpr.insert(0, <w:del author=... date=...>)   # 段落标记删除标记
```
缺了"段落标记删除"那一步，接受后会残留一个空的编号项。

## 4. 半角括号→全角：改 numbering.xml 的 lvlText，一次性根治
子标题 `(一)(二)…` 是自动编号时，半角括号来自 `word/numbering.xml` 里 `<w:lvlText w:val="(%1)">`。逐段改文档没用（那是渲染出来的）。
- **正解**：遍历 numbering.xml 所有 `<w:lvlText>`，`val` 里的 `(`→`（`、`)`→`）`，一次改全文所有同源编号。本例 37 处 lvlText 一次改完。
- 注意只动含 `()` 的 lvlText，`、`分隔的（如请求"一、二、三"用 `%1、`）不受影响。

## 5. 引号"统一为仿宋全角"——根因是引号 run 的字体不是中文字体
现象：正文中文是仿宋（继承样式），但弯引号 `“”`(U+201C/U+201D) 的 run `ascii=Times New Roman, eastAsia=None`。因为弯引号是**中西文模糊字符**，OnlyOffice 对没有 eastAsia 设定的字符按 ascii 字体渲染 → 引号显示成西文 Times 的粗重样式，和仿宋正文不协调。
- **正解（彻底版）**：对**纯引号/中文 run**，把 rFonts 的 `ascii/eastAsia/hAnsi/cs` 全设为「仿宋」+ `hint="eastAsia"`，消除歧义。对**引号+数字混排 run**（如 `“2026…`），按字符**拆 run**：引号段走仿宋、数字段保留 Times New Roman。
- 只设 eastAsia 不够稳——某些渲染下仍可能按 ascii 走 Times。纯引号 run 连 ascii 一起设仿宋最保险（数字 run 才需要保留 Times）。
- 核对：OnlyOffice x2t 渲染后裁剪含引号区域，确认引号纤细、与仿宋协调（不是又粗又重的衬线引号）。

## 6. 验证三件套（同合同终审，文书一样适用）
- `ed.validate()` 返回空。**注意**：诉讼文书的「请求项」原文常是加粗的（与合同正文不加粗体例不同），validate 的"不应加粗"规则会**误报**——先读原文该段普通 run 的 `<w:b>` 状态，若原文请求项本就加粗、INS 继承同样加粗=格式一致=误报，可放行。
- 逐个 `w:ins` 核 author 正确（诉讼文书署当前文书归属人，如本例 Doro 文书上署"小Maggie"修订；合同历史署"WB"——按文书归属定）、eastAsia 字体不缺。
- OnlyOffice x2t 渲两版：**修订态**（看 markup 干净）+ **接受态**（删 w:del、解包 w:ins、删段落标记被删的空段后重渲，看编号连续、全角括号生效、无乱码）。接受态自己生成：解包所有 ins、删所有 del、删 numPr 空段。

## 一句话
合同库的修订能力对所有 docx 文书通用；诉讼文书审改的差异点是：①大改写要整块 del+ins 保 markup 可读 ②引号/括号这类「字体/编号源」问题改 styles/numbering 层不改文档层 ③validate 加粗规则对加粗请求项会误报。

## 7. 接受所有修订 → 干净版 docx（反向操作，2026-06-24 徐函任务验证）
用户给一份**带修订痕迹+批注**的 docx，要「先接受现有修订、让我看干净版本」时——不是用 Word 手点"接受全部"，用 zipfile+lxml 一次处理：
```python
W='http://schemas.openxmlformats.org/wordprocessingml/2006/main'
def w(t): return f'{{{W}}}'+t
# 1) w:del → 整个元素删掉（连 delText 一起没）
for d in root.findall('.//'+w('del')): d.getparent().remove(d)
# 2) w:ins → 解包：用其子元素替换它本身（保留插入内容，去掉ins包裹）
for ins in root.findall('.//'+w('ins')):
    parent=ins.getparent(); idx=list(parent).index(ins)
    for child in reversed(list(ins)): parent.insert(idx, child)
    parent.remove(ins)
# 3) 属性变更追踪一并清：pPrChange/rPrChange/sectPrChange/tblPrChange/tcPrChange/trPrChange
for tag in ('pPrChange','rPrChange','sectPrChange','tblPrChange','tcPrChange','trPrChange'):
    for el in root.findall('.//'+w(tag)): el.getparent().remove(el)
# 4) 批注三处一起拆（用户要"干净版"= 连批注也清）：
#    document.xml: 删 commentRangeStart/End，删含 commentReference 的整个 run
#    settings.xml: 删 <w:trackRevisions/>（让文件退出跟踪模式）
#    打包时跳过 word/comments*.xml，并从 [Content_Types].xml 和 document.xml.rels 删 comments 的 Override/Relationship
```
要点：
- **w:del 删整块、w:ins 解包**——方向别搞反（del 是要丢弃的，ins 是要保留的）。
- **务必清 settings.xml 的 trackRevisions**，否则文件仍处于"跟踪修订"模式，用户继续编辑会又开始记修订。
- **批注要三处协同删**（comments.xml 本体 + document.xml 的 range/reference 锚点 + Content_Types/rels 注册），漏一处 OnlyOffice/Word 打开可能报损坏。
- 验证：解包后 `root.findall('.//w:ins')`/`w:del`/`w:commentReference` 全为 0；`'word/comments.xml' in zip.namelist()` 为 False；python-docx 能打开；OnlyOffice x2t 渲染核对无修订痕迹无批注无错位。
- vision 核干净版时顺带抓**残留内部标记**：黄色高亮（内部校对标记）、留白占位（编号"第　号"、日期"　日"）、标题英文双连字符`--`应为中文破折号`——`——这些不是修订痕迹但属"未清的内部审核稿"特征，正式交付前要清。
