feat: export core Hermes skills
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# ContractEditor 原文Run属性污染诊断与修复
|
||||
|
||||
## 2026-07-13 洋励合同实证
|
||||
|
||||
### 问题描述
|
||||
|
||||
ContractEditor(contract_docx_lib.py)在处理文档时,不仅给WB INS runs添加多余属性,还会**修改原文runs**的rPr——给本来靠docDefaults/style继承的orig runs添加显式eastAsia/cs/sz。
|
||||
|
||||
### 典型污染模式
|
||||
|
||||
| 属性 | 原文(待审查) | 被污染后(交付物中的orig run) | WB INS run |
|
||||
|------|--------------|-------------------------------|-----------|
|
||||
| eastAsia | None (继承minorEastAsia) | **宋体** (被加) | None |
|
||||
| cs | None | **宋体** (被加) | None |
|
||||
| sz | None (继承docDefaults=22) | **21** (被加且值错) | None |
|
||||
| ascii | 宋体 | 宋体 | None |
|
||||
| hint | eastAsia (部分有) | eastAsia | None |
|
||||
|
||||
### 后果
|
||||
|
||||
1. 原文所有文字从11pt(docDefaults sz=22)变成10.5pt(显式sz=21) — 整体缩小0.5pt
|
||||
2. INS文字没有任何属性 → 走docDefaults 11pt → 与被改小的原文不一致
|
||||
3. 字体验证脚本(wb-ins-font-verify.py)报INS缺属性,但实际问题是orig被污染
|
||||
|
||||
### 诊断步骤
|
||||
|
||||
```bash
|
||||
# 1. 读原文代表性段落run rPr
|
||||
python3 -c "
|
||||
import zipfile
|
||||
from lxml import etree
|
||||
WNS = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}'
|
||||
with zipfile.ZipFile('原文.docx', 'r') as z:
|
||||
...
|
||||
# 检查: eastAsia=None? sz=None?
|
||||
# 如果是 → 原文靠继承
|
||||
|
||||
# 2. 读交付物同段落orig run rPr
|
||||
# 检查: 是否多了eastAsia/cs/sz?
|
||||
# 如果是 → 被污染
|
||||
```
|
||||
|
||||
### 修复代码模板
|
||||
|
||||
```python
|
||||
import zipfile, tempfile, shutil
|
||||
from lxml import etree
|
||||
|
||||
WNS = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}'
|
||||
|
||||
with zipfile.ZipFile(filepath, 'r') as z:
|
||||
all_files = {n: z.read(n) for n in z.namelist()}
|
||||
|
||||
tree = etree.fromstring(all_files['word/document.xml'])
|
||||
body = tree.find(f'{WNS}body')
|
||||
|
||||
# Step 1: Strip contaminated attributes from ALL orig runs
|
||||
for p in body.findall(f'{WNS}p'):
|
||||
for r in p.findall(f'{WNS}r'): # Only direct child runs (not inside ins/del)
|
||||
rpr = r.find(f'{WNS}rPr')
|
||||
if rpr is None:
|
||||
continue
|
||||
rf = rpr.find(f'{WNS}rFonts')
|
||||
if rf is not None:
|
||||
# Strip eastAsia (original didn't have it)
|
||||
if f'{WNS}eastAsia' in rf.attrib:
|
||||
del rf.attrib[f'{WNS}eastAsia']
|
||||
# Strip cs (original didn't have it)
|
||||
if f'{WNS}cs' in rf.attrib:
|
||||
del rf.attrib[f'{WNS}cs']
|
||||
# Strip sz (original relies on docDefaults)
|
||||
sz = rpr.find(f'{WNS}sz')
|
||||
if sz is not None:
|
||||
rpr.remove(sz)
|
||||
|
||||
# Step 2: Fix INS runs to match REAL original format
|
||||
for ins in body.findall(f'.//{WNS}ins'):
|
||||
if ins.get(f'{WNS}author') != 'WB':
|
||||
continue
|
||||
for r in ins.findall(f'{WNS}r'):
|
||||
rpr = r.find(f'{WNS}rPr')
|
||||
if rpr is None:
|
||||
continue
|
||||
rf = rpr.find(f'{WNS}rFonts')
|
||||
if rf is None:
|
||||
rf = etree.SubElement(rpr, f'{WNS}rFonts')
|
||||
# Match real original: ascii=宋体, hAnsi=宋体, NO eastAsia
|
||||
rf.set(f'{WNS}ascii', '宋体')
|
||||
rf.set(f'{WNS}hAnsi', '宋体')
|
||||
if f'{WNS}eastAsia' in rf.attrib:
|
||||
del rf.attrib[f'{WNS}eastAsia']
|
||||
# Remove sz (let it inherit)
|
||||
sz = rpr.find(f'{WNS}sz')
|
||||
if sz is not None:
|
||||
rpr.remove(sz)
|
||||
|
||||
# Step 3: Per-paragraph hint matching
|
||||
for p in body.findall(f'{WNS}p'):
|
||||
# Get orig run's hint
|
||||
orig_hint = None
|
||||
for r in p.findall(f'{WNS}r'):
|
||||
rpr = r.find(f'{WNS}rPr')
|
||||
if rpr is not None:
|
||||
rf = rpr.find(f'{WNS}rFonts')
|
||||
orig_hint = rf.get(f'{WNS}hint') if rf is not None else None
|
||||
break
|
||||
# Apply to INS runs in same paragraph
|
||||
for ins in p.findall(f'.//{WNS}ins'):
|
||||
if ins.get(f'{WNS}author') != 'WB':
|
||||
continue
|
||||
for r in ins.findall(f'{WNS}r'):
|
||||
rpr = r.find(f'{WNS}rPr')
|
||||
if rpr is None: continue
|
||||
rf = rpr.find(f'{WNS}rFonts')
|
||||
if rf is None: continue
|
||||
if orig_hint:
|
||||
rf.set(f'{WNS}hint', orig_hint)
|
||||
elif f'{WNS}hint' in rf.attrib:
|
||||
del rf.attrib[f'{WNS}hint']
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **必须对比原文确定被污染了哪些属性** — 不同合同模板的原文属性不同
|
||||
2. **不是所有合同都有此问题** — 取决于原文是否靠继承(有显式属性的不会被"污染",因为值相同)
|
||||
3. **Step 1必须在Step 2之前** — 否则wb-ins-font-verify仍会报INS与(被污染的)orig不一致
|
||||
4. **hint要逐段处理** — 同一文档不同段落的orig runs可能有的有hint有的没有
|
||||
Reference in New Issue
Block a user