48 lines
2.3 KiB
Markdown
48 lines
2.3 KiB
Markdown
# WPS Format Handling & Renumber Order Pitfall
|
|
|
|
## WPS File Conversion
|
|
|
|
邱律师有时发送 `.wps` 格式文件(WPS Office 原生格式)。python-docx 无法直接读取。
|
|
|
|
**转换命令:**
|
|
```bash
|
|
libreoffice --headless --convert-to docx "/path/to/file.wps" --outdir /tmp/contract-review/
|
|
```
|
|
|
|
**注意事项:**
|
|
- 转换后文件名保留原名但扩展名变为 .docx
|
|
- 转换后仍需复制为简短ASCII文件名避免路径问题
|
|
- 交付时文件名前加【修】,扩展名用 .docx(不恢复为 .wps)
|
|
- 设备清单可能是图片而非文字表格(WPS特有),需用vision检查 word/media/ 中的图片
|
|
|
|
## tracked_replace + add_clause 编号顺延的执行顺序
|
|
|
|
**铁律:先做所有 add_clause/add_clause_before 插入,最后统一 tracked_replace 重编号。**
|
|
|
|
**错误顺序(会报 lxml ValueError):**
|
|
```python
|
|
editor.add_clause_before('第七条 转包', before_search='第七条不可抗力')
|
|
editor.tracked_replace('第七条不可抗力', '第八条不可抗力') # ✅ OK
|
|
editor.add_clause_before('第八条 侵权', before_search='第八条不可抗力')
|
|
editor.tracked_replace('第八条不可抗力', '第九条不可抗力') # ❌ FAIL - paragraph already modified
|
|
```
|
|
|
|
**正确顺序:**
|
|
```python
|
|
# 1. 先做所有插入(用原文文本定位)
|
|
editor.add_clause_before('第七条 转包\n...', before_search='第七条不可抗力')
|
|
editor.add_clause_before('第八条 侵权\n...', before_search='第七条不可抗力')
|
|
# 注意:两个都用原文"第七条不可抗力"定位,因为原文段落文本未变
|
|
|
|
# 2. 最后统一重编号(此时原文段落只被rename一次)
|
|
editor.tracked_replace('第七条不可抗力', '第九条不可抗力')
|
|
editor.tracked_replace('第八条争议解决', '第十条争议解决')
|
|
# ...
|
|
```
|
|
|
|
**根因:** `tracked_replace` 会把原文 run 删除并插入 `w:del` + `w:ins`,修改后的段落内部结构已变。再次对同一段落调用 `tracked_replace` 时,lxml 尝试 remove 已不在原位的 element,抛出 `ValueError: Element is not a child of this node`。
|
|
|
|
## 2026-07-13 实证
|
|
|
|
练塘硬件购销合同(璞石医疗):新增第七条转包+第八条第三方侵权,原第七至十条顺延为第九至十二条。第一次尝试"插一个改一个"失败,改为"先全部插入再统一改编号"成功。
|