100 lines
3.8 KiB
Python
100 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Post-save sweep: strip explicit attributes from WB INS runs when
|
|
the same-paragraph original runs rely on inheritance (ea=None, hint=None, sz=None).
|
|
|
|
Usage: python3 strip-inherited-ins-attrs.py <docx_path>
|
|
|
|
Modifies the file in place. Run AFTER ContractEditor.save() and BEFORE
|
|
wb-ins-font-verify.py to fix the known "ContractEditor默认sz=21与docDefaults继承冲突".
|
|
|
|
The pattern: for each paragraph containing WB INS, find the first plain w:r
|
|
(non-INS, non-DEL) as reference. If that reference run has no explicit
|
|
eastAsia/hint/sz, strip those from all WB INS runs in the same paragraph.
|
|
"""
|
|
import sys
|
|
import zipfile
|
|
import tempfile
|
|
import shutil
|
|
from lxml import etree
|
|
|
|
WNS = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}'
|
|
|
|
|
|
def strip_inherited_attrs(filepath):
|
|
with zipfile.ZipFile(filepath, 'r') as z:
|
|
doc_xml = z.read('word/document.xml')
|
|
all_files = {n: z.read(n) for n in z.namelist()}
|
|
|
|
tree = etree.fromstring(doc_xml)
|
|
body = tree.find(f'{WNS}body')
|
|
paras = body.findall(f'{WNS}p')
|
|
|
|
fixed = 0
|
|
for p in paras:
|
|
# Find first plain run as reference
|
|
orig_run = None
|
|
for child in p:
|
|
if child.tag == f'{WNS}r':
|
|
orig_run = child
|
|
break
|
|
if orig_run is None:
|
|
continue
|
|
|
|
orig_rpr = orig_run.find(f'{WNS}rPr')
|
|
orig_rf = orig_rpr.find(f'{WNS}rFonts') if orig_rpr is not None else None
|
|
orig_sz = orig_rpr.find(f'{WNS}sz') if orig_rpr is not None else None
|
|
orig_ea = orig_rf.get(f'{WNS}eastAsia') if orig_rf is not None else None
|
|
orig_hint = orig_rf.get(f'{WNS}hint') if orig_rf is not None else None
|
|
orig_sz_val = orig_sz.get(f'{WNS}val') if orig_sz is not None else None
|
|
|
|
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')
|
|
sz = rpr.find(f'{WNS}sz')
|
|
|
|
if orig_ea is None and rf is not None:
|
|
for attr in ['eastAsia', 'ascii', 'hAnsi']:
|
|
key = f'{WNS}{attr}'
|
|
if key in rf.attrib:
|
|
if orig_rf is None or orig_rf.get(key) is None:
|
|
del rf.attrib[key]
|
|
fixed += 1
|
|
|
|
if orig_hint is None and rf is not None and f'{WNS}hint' in rf.attrib:
|
|
del rf.attrib[f'{WNS}hint']
|
|
fixed += 1
|
|
|
|
if orig_sz_val is None and sz is not None:
|
|
rpr.remove(sz)
|
|
fixed += 1
|
|
|
|
# Save
|
|
tmp = tempfile.mktemp(suffix='.docx')
|
|
with zipfile.ZipFile(tmp, 'w', zipfile.ZIP_DEFLATED) as zout:
|
|
for name in all_files:
|
|
if name == 'word/document.xml':
|
|
new_xml = etree.tostring(tree, xml_declaration=True, encoding='UTF-8', standalone=True)
|
|
new_str = new_xml.decode('utf-8')
|
|
new_str = new_str.replace(
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>",
|
|
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
|
|
new_str = new_str.replace('\n', '\r\n')
|
|
zout.writestr(name, new_str.encode('utf-8'))
|
|
else:
|
|
zout.writestr(name, all_files[name])
|
|
shutil.move(tmp, filepath)
|
|
return fixed
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if len(sys.argv) < 2:
|
|
print(f"Usage: {sys.argv[0]} <docx_path>")
|
|
sys.exit(1)
|
|
n = strip_inherited_attrs(sys.argv[1])
|
|
print(f"Fixed {n} inherited attribute issues in {sys.argv[1]}")
|