round-5 bisection: updateFields-path / mini-28-distinct / full-1field probes
This commit is contained in:
@@ -19,6 +19,9 @@ feature family:
|
||||
19 no_validate - +w:doNotValidateAgainstSchema in settings.xml
|
||||
20 shared_hf_parts - every section points at section 1's 6 header/footer parts
|
||||
21 mini_28fields - the MINI book with the TOC entry replicated to 28 fields
|
||||
22 updatefields_nodirty - w:dirty off + <w:updateFields w:val="true"/> in settings
|
||||
23 mini_28distinct - mini with 28 fields at 28 DISTINCT bookmarks
|
||||
24 full_1field - full book with exactly 1 dirty TOC field
|
||||
|
||||
Open each in Word; whichever show the error dialog pinpoint the feature
|
||||
family at fault.
|
||||
@@ -61,6 +64,9 @@ NAMES = [
|
||||
'bisect_19_no_validate.docx',
|
||||
'bisect_20_shared_hf_parts.docx',
|
||||
'bisect_21_mini_28fields.docx',
|
||||
'bisect_22_updatefields_nodirty.docx',
|
||||
'bisect_23_mini_28distinct.docx',
|
||||
'bisect_24_full_1field.docx',
|
||||
]
|
||||
|
||||
|
||||
@@ -358,6 +364,90 @@ def make_mini_28fields(doc):
|
||||
return mini
|
||||
|
||||
|
||||
def make_updatefields_nodirty(doc):
|
||||
"""Full book: remove w:dirty from every field run AND set
|
||||
<w:updateFields w:val="true"/> in settings (Word updates all fields on
|
||||
open through the settings path instead of per-field dirty flags)."""
|
||||
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)
|
||||
settings = doc.settings.element
|
||||
el = etree.Element(qn('w:updateFields'))
|
||||
el.set(qn('w:val'), 'true')
|
||||
# canonical CT_Settings order: ... alwaysMergeEmptyNamespace, updateFields,
|
||||
# hdrShapeDefaults, footnotePr, endnotePr, compat, rsids, mathPr
|
||||
anchor = None
|
||||
for tag in ('w:hdrShapeDefaults', 'w:footnotePr', 'w:endnotePr',
|
||||
'w:compat', 'w:rsids', 'w:mathPr'):
|
||||
anchor = settings.find(qn(tag))
|
||||
if anchor is not None:
|
||||
break
|
||||
if anchor is not None:
|
||||
anchor.addprevious(el)
|
||||
else:
|
||||
settings.append(el)
|
||||
|
||||
|
||||
def make_mini_28_distinct(doc):
|
||||
"""Return a copy of the MINI book with 28 dirty PAGEREF fields pointing at
|
||||
28 DISTINCT bookmarks: replicate the first TOC entry 25 more times with
|
||||
rewritten targets, plus 25 copies of the chapter-1 title paragraph each
|
||||
carrying a renumbered, renamed bookmark (_UF_ch_3..27). Un-confounds the
|
||||
same-target multiplicity in bisect_21."""
|
||||
mini = Document(os.path.join(HERE, 'word_test_output.docx'))
|
||||
body = mini.element.body
|
||||
paras = [c for c in body if c.tag == qn('w:p')]
|
||||
first_entry = None
|
||||
for p in paras:
|
||||
if p.find(".//" + qn('w:instrText')) is not None:
|
||||
first_entry = p
|
||||
break
|
||||
title_p = None
|
||||
for p in paras:
|
||||
ppr = p.find(qn('w:pPr'))
|
||||
st = ppr.find(qn('w:pStyle')) if ppr is not None else None
|
||||
if st is not None and st.get(qn('w:val')) == 'WAChapter':
|
||||
title_p = p
|
||||
break
|
||||
if first_entry is None or title_p is None:
|
||||
return mini
|
||||
used = {int(b.get(qn('w:id'))) for b in body.iter(qn('w:bookmarkStart'))}
|
||||
nid = (max(used) + 1) if used else 0
|
||||
for k in range(3, 28):
|
||||
ec = deepcopy(first_entry)
|
||||
ec.find(".//" + qn('w:instrText')).text = f" PAGEREF _UF_ch_{k} "
|
||||
body.insert(list(body).index(first_entry) + 1, ec)
|
||||
tc = deepcopy(title_p)
|
||||
for bs in tc.iter(qn('w:bookmarkStart')):
|
||||
bs.set(qn('w:name'), f"_UF_ch_{k}")
|
||||
bs.set(qn('w:id'), str(nid))
|
||||
nid += 1
|
||||
for be in tc.iter(qn('w:bookmarkEnd')):
|
||||
be.set(qn('w:id'), str(nid))
|
||||
nid += 1
|
||||
body.insert(list(body).index(title_p) + 1, tc)
|
||||
return mini
|
||||
|
||||
|
||||
def make_first1_toc(doc):
|
||||
"""Full book: keep only the FIRST TOC entry's field; plain-text the rest
|
||||
(count threshold probe: does full + exactly 1 dirty field open?)."""
|
||||
seen = 0
|
||||
for p in doc.element.body.iter(qn('w:p')):
|
||||
if p.find(".//" + qn('w:instrText')) is None:
|
||||
continue
|
||||
if seen < 1:
|
||||
seen += 1
|
||||
continue
|
||||
for r in list(p.findall(qn('w:r'))):
|
||||
if r.find(qn('w:fldChar')) is not None or r.find(qn('w:instrText')) is not None:
|
||||
p.remove(r)
|
||||
|
||||
|
||||
MAKERS = [
|
||||
make_no_running,
|
||||
make_no_oddpage,
|
||||
@@ -380,6 +470,9 @@ MAKERS = [
|
||||
make_no_validate,
|
||||
make_shared_hf_parts,
|
||||
make_mini_28fields,
|
||||
make_updatefields_nodirty,
|
||||
make_mini_28_distinct,
|
||||
make_first1_toc,
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user