Files
hermes-skills/skills/legal/contract-editor/references/numpr-strip-when-adding-manual-numbering.md
T

6.4 KiB

Strip numPr When Adding Manual Numbering to Auto-Numbered Paragraphs

Problem (2026-07-01 反委托代发工资协议)

Original contract paragraphs have <w:numPr> with actual auto-numbering (e.g., numId=3 → abstractNum decimal "%1." start=1). When you insert manual "第X条" numbering as w:ins at paragraph start, OnlyOffice renders BOTH:

1. 第一条  乙方应严格按照...   ← "1." is auto-numbering, "第一条" is your INS

This looks broken — two different numbering systems stacked.

Root Cause

The paragraph's pPr/numPr tells the rendering engine to prepend an automatic decimal number. Your INS adds a second, manual number. They coexist independently.

Additionally, if the paragraph has pPr/pPrChange (tracking the old paragraph formatting), the old numPr inside pPrChange can ALSO render in markup view.

Fix (two-step)

After inserting manual numbering INS elements, strip auto-numbering from ALL affected paragraphs:

for idx in target_paragraph_indices:
    p = paras[idx]
    ppr = p.find(f'{WNS}pPr')
    if ppr is not None:
        # Step 1: Remove direct numPr
        num_pr = ppr.find(f'{WNS}numPr')
        if num_pr is not None:
            ppr.remove(num_pr)
        
        # Step 2: Remove numPr inside pPrChange (old formatting record)
        ppr_change = ppr.find(f'{WNS}pPrChange')
        if ppr_change is not None:
            inner_ppr = ppr_change.find(f'{WNS}pPr')
            if inner_ppr is not None:
                inner_num = inner_ppr.find(f'{WNS}numPr')
                if inner_num is not None:
                    inner_ppr.remove(inner_num)

When This Applies

  • You're converting a contract from auto-numbered clauses to manual "第X条" heading-style numbering
  • The original .doc/.docx used Word's list numbering for clause structure
  • You're adding "第一条 " etc. as INS at paragraph start

Verification

After fix:

  1. pdftotext -layout of OnlyOffice render should show NO stray "1." / "2." / "3." before your "第X条"
  2. Accept-revisions preview should also be clean (no residual auto-numbers)

Scenario B: Auto-Numbering Resets Across Tracked-Deleted Paragraphs (2026-07-01 反委托代发工资协议)

Problem

When paragraphs with numPr auto-numbering are interspersed with entirely deleted paragraphs (all content in w:del), OnlyOffice's auto-number counter resets to 1 after the deleted block. This makes continuous numbering impossible with numPr alone.

Example structure:

P6:  numPr=1  INS content (clause 1) → renders "1."
P7:  numPr=1  continuation            → renders "2." (wrong if P7 shouldn't be numbered)
P8:  numPr=1  ALL w:del               → renders "3." with strikethrough
P9:  numPr=1  ALL w:del               → renders "4." with strikethrough
P10: numPr=1  INS content (clause 2)  → renders "1." ← RESETS! Should be "2."

The auto-numbering engine counts visible (non-deleted) items in the numId sequence, but deleted paragraphs break the continuity in OnlyOffice's rendering.

Solution: Convert to Manual Text Numbering

Strip numPr from ALL paragraphs and insert "N. " as w:ins text at paragraph start. This gives identical visual output ("1. 2. 3. ...") without depending on the broken auto-number counter.

# Step 1: Strip ALL numPr (including inside pPrChange)
for i, p in enumerate(paragraphs):
    pPr = p.find(f'{{{W}}}pPr')
    if pPr is not None:
        numPr = pPr.find(f'{{{W}}}numPr')
        if numPr is not None:
            pPr.remove(numPr)
        for pPrChange in pPr.findall(f'{{{W}}}pPrChange'):
            old_pPr = pPrChange.find(f'{{{W}}}pPr')
            if old_pPr is not None:
                old_numPr = old_pPr.find(f'{{{W}}}numPr')
                if old_numPr is not None:
                    old_pPr.remove(old_numPr)

# Step 2: Insert "N. " as w:ins text for each clause paragraph
# Only number paragraphs that have VISIBLE content (not entirely w:del)
clause_map = {6: 1, 10: 2, 11: 3, ...}  # para_index: clause_number

for para_idx, clause_num in clause_map.items():
    p = paragraphs[para_idx]
    # Build INS element with "N. " text
    ins_elem = ET.Element(f'{{{W}}}ins')
    ins_elem.set(f'{{{W}}}id', str(next_rev_id()))
    ins_elem.set(f'{{{W}}}author', rev_author)  # from existing INS in doc
    ins_elem.set(f'{{{W}}}date', rev_date)
    
    r_elem = ET.SubElement(ins_elem, f'{{{W}}}r')
    # Clone rPr from existing runs for font consistency
    rPr = get_run_rPr_from_paragraph(p)
    if rPr is not None:
        r_elem.append(copy.deepcopy(rPr))
    
    t_elem = ET.SubElement(r_elem, f'{{{W}}}t')
    t_elem.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
    t_elem.text = f"{clause_num}. "
    
    # Insert after pPr
    pPr = p.find(f'{{{W}}}pPr')
    if pPr is not None:
        p.insert(list(p).index(pPr) + 1, ins_elem)
    else:
        p.insert(0, ins_elem)

When to Use This (vs Scenario A)

  • Scenario A (above): You're CHANGING the numbering scheme (auto "1." → manual "第一条")
  • Scenario B (this): You're KEEPING the same format ("1. 2. 3.") but converting from auto to manual because auto-numbering resets across w:del paragraphs
  • Trigger: Original uses numPr auto-numbering + your edits create entirely-deleted paragraphs between numbered items → auto counter resets → switch to manual text

Key Decision: Which Paragraphs to Number

Only number paragraphs that will be visible after accepting revisions:

  • Paragraphs with ONLY w:del content → skip (they're deleted)
  • Paragraphs that are continuations of the previous clause (no independent number) → skip
  • New INS-only paragraphs (new clauses) → number them
  • Rewritten paragraphs (mixed INS+DEL, first clause in sequence) → number them

Verification

After conversion:

  1. OnlyOffice render (x2t → PDF) should show continuous "1. 2. 3. ... 11." without resets
  2. No stray auto-numbers from numPr remnants
  3. Deleted paragraphs (entirely w:del) should NOT show any number

Distinction from Existing Rules

  • Rule 5 (A類 vs B類) talks about NEW clauses inheriting/stripping numPr
  • Scenario A is EXISTING paragraphs where you're REPLACING their numbering scheme with a different format via INS
  • Scenario B is EXISTING paragraphs where auto-numbering BREAKS due to tracked-deleted paragraphs, requiring conversion to same-format manual text
  • numId=0 trap (Rule 5 sub-note) is about fake auto-numbering; BOTH scenarios here are about REAL auto-numbering that renders visible numbers