# 审查意见文档字体强制设置

## 背景（2026-07-02 肃言+恭兴合同返工）

review-rules.md 规定审查意见文档：中文统一仿宋体，英文Times New Roman。

**问题**：模板文件（`朱家角 审查意见【模板】.docx`）的表头行有显式eastAsia=仿宋，但新建的数据行字体设置不一致：
- 恭兴审查意见：editor给数据行设了 ascii=仿宋 hAnsi=仿宋（错：英文也变仿宋了）
- 肃言审查意见：editor压根没给数据行设ascii/hAnsi（只有eastAsia=仿宋）

**根因**：LLM每次独立session生成代码，字体设置逻辑不稳定。

## 强制修复代码（生成审查意见后必跑）

```python
from docx import Document
from docx.oxml.ns import qn
from docx.oxml import OxmlElement

def enforce_review_opinion_fonts(doc_path, save=True):
    """审查意见文档生成后强制设置所有run的字体。
    中文=仿宋, 英文=Times New Roman
    """
    doc = Document(doc_path)
    fixed = 0
    
    # Fix all table cells
    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for p in cell.paragraphs:
                    for run in p.runs:
                        fixed += _fix_run_font(run._element)
    
    # Fix all paragraphs outside tables
    for p in doc.paragraphs:
        for run in p.runs:
            fixed += _fix_run_font(run._element)
    
    if save:
        doc.save(doc_path)
    return fixed

def _fix_run_font(run_element):
    """Ensure run has eastAsia=仿宋, ascii/hAnsi=Times New Roman"""
    rPr = run_element.find(qn('w:rPr'))
    if rPr is None:
        rPr = OxmlElement('w:rPr')
        run_element.insert(0, rPr)
    
    rFonts = rPr.find(qn('w:rFonts'))
    if rFonts is None:
        rFonts = OxmlElement('w:rFonts')
        rPr.insert(0, rFonts)
    
    changed = False
    
    # eastAsia must be 仿宋
    if rFonts.get(qn('w:eastAsia')) != '仿宋':
        rFonts.set(qn('w:eastAsia'), '仿宋')
        changed = True
    
    # ascii must be Times New Roman (NOT 仿宋)
    if rFonts.get(qn('w:ascii')) != 'Times New Roman':
        rFonts.set(qn('w:ascii'), 'Times New Roman')
        changed = True
    
    # hAnsi must be Times New Roman (NOT 仿宋)
    if rFonts.get(qn('w:hAnsi')) != 'Times New Roman':
        rFonts.set(qn('w:hAnsi'), 'Times New Roman')
        changed = True
    
    return 1 if changed else 0
```

## 验证方法

```python
def verify_review_opinion_fonts(doc_path):
    """验证审查意见文档字体全部正确"""
    from docx import Document
    from docx.oxml.ns import qn
    doc = Document(doc_path)
    errors = []
    for table in doc.tables:
        for i, row in enumerate(table.rows):
            for j, cell in enumerate(row.cells):
                for p in cell.paragraphs:
                    for run in p.runs:
                        rpr = run._element.find(qn('w:rPr'))
                        if rpr is None:
                            errors.append(f'Row{i}Col{j}: no rPr')
                            continue
                        rf = rpr.find(qn('w:rFonts'))
                        if rf is None:
                            errors.append(f'Row{i}Col{j}: no rFonts')
                            continue
                        ea = rf.get(qn('w:eastAsia'))
                        ascii_f = rf.get(qn('w:ascii'))
                        hAnsi = rf.get(qn('w:hAnsi'))
                        if ea != '仿宋':
                            errors.append(f'Row{i}Col{j}: eastAsia={ea} (should be 仿宋)')
                        if ascii_f != 'Times New Roman':
                            errors.append(f'Row{i}Col{j}: ascii={ascii_f} (should be TNR)')
                        if hAnsi != 'Times New Roman':
                            errors.append(f'Row{i}Col{j}: hAnsi={hAnsi} (should be TNR)')
    return errors
```

## 常见错误

| 错误 | 后果 | 根因 |
|------|------|------|
| ascii/hAnsi=仿宋 | 英文/数字渲染用仿宋（无西文字形，显示异常） | LLM把"统一仿宋"理解为所有属性都设仿宋 |
| 数据行无ascii/hAnsi | 英文回退系统默认字体（可能是宋体/黑体） | 只设了eastAsia，忘设西文字体 |
| 只有表头有字体 | 数据行全部回退默认 | 模板限制：只有表头行有显式字体 |

## 预防方案

最佳方案：修改模板文件的**Normal样式或Table Grid样式**定义，预设完整字体。但模板可能被多场景共用，最稳妥还是生成后强制设置。
