Round 3 Word bisection: co-factor probes around the PAGEREF fields

The mini opens with 3 byte-identical fields while the full shell fails even
with 3 (bisect_12) or 1 target (bisect_11), so the co-factor is outside the
TOC. Six new candidates keep fields and strip the remaining families or
probe the field mechanism:

- 13 drop scene-break paragraphs
- 14 drop all bookmarkStart/End (fields keep, targets vanish)
- 15 full shell with the mini's own 3-entry TOC spliced in verbatim
- 16 drop chapter title paragraphs (WAChapter)
- 17 rewrite PAGEREF instructions to PAGE (structure unchanged)
- 18 remove w:dirty (no auto field update on open)
This commit is contained in:
bm
2026-09-23 08:45:30 -07:00
parent 284ebad6af
commit 9f08de1e5d

View File

@@ -19,6 +19,7 @@ feature family:
Open each in Word; whichever show the error dialog pinpoint the feature
family at fault.
"""
from copy import deepcopy
import os
import sys
@@ -47,6 +48,12 @@ NAMES = [
'bisect_10_no_toc_section.docx',
'bisect_11_same_target_fields.docx',
'bisect_12_first3_toc.docx',
'bisect_13_no_scenes.docx',
'bisect_14_no_bookmarks.docx',
'bisect_15_mini_toc_in_full.docx',
'bisect_16_no_titles.docx',
'bisect_17_page_fields.docx',
'bisect_18_no_dirty.docx',
]
@@ -201,6 +208,83 @@ def make_first3_toc(doc):
p.remove(r)
def make_no_scenes(doc):
"""Drop the scene-break paragraphs (keep fields)."""
for p in list(doc.element.body.iter(qn('w:p'))):
txt = ''.join((t.text or '') for t in p.findall('.//' + qn('w:t')))
if '>>>' in txt:
p.getparent().remove(p)
def make_no_bookmarks(doc):
"""Remove every bookmarkStart/bookmarkEnd (fields stay, targets vanish)."""
for tag in ('w:bookmarkStart', 'w:bookmarkEnd'):
for b in list(doc.element.body.iter(qn(tag))):
b.getparent().remove(b)
def make_mini_toc_in_full(doc):
"""Full shell WITHOUT the TOC section, but with the mini book's own
3-entry TOC (heading + entries + fields) spliced in verbatim."""
make_no_toc_section(doc)
mini = Document(os.path.join(HERE, 'word_test_output.docx'))
paras = [c for c in mini.element.body if c.tag == qn('w:p')]
start = None
for i, p in enumerate(paras):
txt = ''.join((t.text or '') for t in p.findall('.//' + qn('w:t')))
if txt.strip() == 'Table of Contents':
start = i
break
end = len(paras)
for i in range(start + 1, len(paras)):
ppr = paras[i].find(qn('w:pPr'))
if ppr is not None and ppr.find(qn('w:sectPr')) is not None:
end = i
break
# anchor: first WAChapter paragraph (chapter 1 title) of the full shell
body = doc.element.body
anchor = None
for p in body.iter(qn('w:p')):
ppr = p.find(qn('w:pPr'))
if ppr is None:
continue
st = ppr.find(qn('w:pStyle'))
if st is not None and st.get(qn('w:val')) == 'WAChapter':
anchor = p
break
for pi in range(start, end):
body.insert(list(body).index(anchor), deepcopy(paras[pi]))
def make_no_titles(doc):
"""Drop the chapter title paragraphs (WAChapter) incl. their bookmarks."""
for p in list(doc.element.body.iter(qn('w:p'))):
ppr = p.find(qn('w:pPr'))
if ppr is None:
continue
st = ppr.find(qn('w:pStyle'))
if st is not None and st.get(qn('w:val')) == 'WAChapter':
p.getparent().remove(p)
def make_page_fields(doc):
"""Replace every PAGEREF instruction with PAGE (structure kept)."""
for it in doc.element.body.iter(qn('w:instrText')):
if (it.text or '').strip().startswith('PAGEREF'):
it.text = ' PAGE '
def make_no_dirty(doc):
"""Remove w:dirty from field runs (no auto field update on open)."""
for fc in doc.element.body.iter(qn('w:fldChar')):
r = fc.getparent()
rpr = r.find(qn('w:rPr'))
if rpr is not None:
d = rpr.find(qn('w:dirty'))
if d is not None:
rpr.remove(d)
MAKERS = [
make_no_running,
make_no_oddpage,
@@ -214,6 +298,12 @@ MAKERS = [
make_no_toc_section,
make_same_target_fields,
make_first3_toc,
make_no_scenes,
make_no_bookmarks,
make_mini_toc_in_full,
make_no_titles,
make_page_fields,
make_no_dirty,
]