63 lines
1.9 KiB
Python
63 lines
1.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
import argparse
|
||
|
|
import json
|
||
|
|
import re
|
||
|
|
from pathlib import Path
|
||
|
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||
|
|
|
||
|
|
|
||
|
|
def fill_docx_template(template_path: str, output_path: str, data: dict) -> int:
|
||
|
|
with ZipFile(template_path, 'r') as zin:
|
||
|
|
xml = zin.read('word/document.xml').decode('utf-8', 'ignore')
|
||
|
|
|
||
|
|
replaced = 0
|
||
|
|
for key, value in data.items():
|
||
|
|
ph = '{{' + str(key) + '}}'
|
||
|
|
val = str(value)
|
||
|
|
count = xml.count(ph)
|
||
|
|
if count:
|
||
|
|
replaced += count
|
||
|
|
xml = xml.replace(ph, val)
|
||
|
|
|
||
|
|
# warn on unresolved placeholders
|
||
|
|
unresolved = sorted(set(re.findall(r'\{\{[^{}]+\}\}', xml)))
|
||
|
|
|
||
|
|
with ZipFile(template_path, 'r') as zin, ZipFile(output_path, 'w', ZIP_DEFLATED) as zout:
|
||
|
|
for item in zin.infolist():
|
||
|
|
content = zin.read(item.filename)
|
||
|
|
if item.filename == 'word/document.xml':
|
||
|
|
content = xml.encode('utf-8')
|
||
|
|
zout.writestr(item, content)
|
||
|
|
|
||
|
|
print(f'written: {output_path}')
|
||
|
|
print(f'replaced: {replaced}')
|
||
|
|
if unresolved:
|
||
|
|
print('unresolved_placeholders:')
|
||
|
|
for u in unresolved:
|
||
|
|
print(f' - {u}')
|
||
|
|
else:
|
||
|
|
print('unresolved_placeholders: none')
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
ap = argparse.ArgumentParser()
|
||
|
|
ap.add_argument('--template', required=True, help='Path to .docx template with {{PLACEHOLDER}}')
|
||
|
|
ap.add_argument('--data', required=True, help='Path to JSON map: {"PLACEHOLDER":"value"}')
|
||
|
|
ap.add_argument('--out', required=True, help='Output .docx path')
|
||
|
|
args = ap.parse_args()
|
||
|
|
|
||
|
|
template = Path(args.template)
|
||
|
|
data_path = Path(args.data)
|
||
|
|
out = Path(args.out)
|
||
|
|
|
||
|
|
with open(data_path, 'r', encoding='utf-8') as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
||
|
|
raise SystemExit(fill_docx_template(str(template), str(out), data))
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|