#!/usr/bin/env python3 """Unify all tracked change authors in a docx to 'WB'. Usage: python unify-author-wb.py [output.docx] If output is omitted, overwrites input. Covers: w:ins, w:del, rPrChange, pPrChange, sectPrChange, tblPrChange, trPrChange, tcPrChange. Also fixes XML declaration (single→double quotes) for OnlyOffice compatibility. """ import sys, os, zipfile, re from lxml import etree WNS = '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}' CHANGE_TAGS = ('ins', 'del', 'rPrChange', 'pPrChange', 'sectPrChange', 'tblPrChange', 'trPrChange', 'tcPrChange') def unify_author(src_path, out_path=None): if out_path is None: out_path = src_path tmp_path = out_path + '.tmp' zin = zipfile.ZipFile(src_path, 'r') doc_xml = zin.read('word/document.xml') tree = etree.fromstring(doc_xml) body = tree.find(f'{WNS}body') changed = 0 for tag_suffix in CHANGE_TAGS: for elem in body.iter(f'{WNS}{tag_suffix}'): author = elem.get(f'{WNS}author') if author and author != 'WB': elem.set(f'{WNS}author', 'WB') changed += 1 # Serialize + fix XML declaration doc_bytes = etree.tostring(tree, xml_declaration=True, encoding='UTF-8', standalone=True) doc_str = doc_bytes.decode('utf-8') doc_str = doc_str.replace( "", '') with zipfile.ZipFile(tmp_path, 'w', zipfile.ZIP_DEFLATED) as zout: for item in zin.namelist(): if item == 'word/document.xml': zout.writestr(item, doc_str.encode('utf-8')) else: zout.writestr(item, zin.read(item)) zin.close() os.replace(tmp_path, out_path) # Verify z = zipfile.ZipFile(out_path) vdoc = z.read('word/document.xml') vtree = etree.fromstring(vdoc) vbody = vtree.find(f'{WNS}body') remaining = set() for tag_suffix in CHANGE_TAGS: for elem in vbody.iter(f'{WNS}{tag_suffix}'): a = elem.get(f'{WNS}author', '') if a != 'WB': remaining.add(a) z.close() print(f"✅ {changed} author attributes → WB") if remaining: print(f"⚠️ Remaining non-WB authors: {remaining}") else: print(f" All authors = WB") print(f" Output: {out_path} ({os.path.getsize(out_path):,} bytes)") if __name__ == '__main__': if len(sys.argv) < 2: print(__doc__) sys.exit(1) src = sys.argv[1] out = sys.argv[2] if len(sys.argv) > 2 else None unify_author(src, out)