import json import sys from docx import Document from docx.shared import Inches def get_command_argv_by_sys(): # 默认值-用于测试 template_path = "template.docx" filename_path = "demo.docx" datafile_path = "data.json" # 读取参数 number = len(sys.argv) if 2 == number: template_path = sys.argv[1] if 3 == number: template_path = sys.argv[1] filename_path = sys.argv[2] if 4 == number: template_path = sys.argv[1] filename_path = sys.argv[2] datafile_path = sys.argv[3] return template_path, filename_path, datafile_path def read_data(filepath): content = [] if filepath != "": try: with open(filepath, "r", encoding="utf-8") as file: content = json.load(file) finally: return content def replace(template_path, filename_path, data_json): try: document_file = Document(template_path) except Exception: return 2 # 段落替换 for paragraph in document_file.paragraphs: paragraph.text = paragraph.text.replace(" ", "") find = False for datum in data_json: key = datum["key"] if key not in paragraph.text: continue find = True if datum["type"] == "text": runs = paragraph.runs for i in range(len(runs)): if key in runs[i].text: runs[i].text = runs[i].text.replace(key, str(datum["value"])) elif datum["type"] == "number": paragraph.text = paragraph.text.replace(key, str(datum["value"])) elif datum["type"] == "picture": # 清空整个段落并插入图片(假设占位符独占段落) paragraph.clear() width = float(datum.get("width", 2.0)) paragraph.add_run().add_picture(datum["value"], width=Inches(width)) elif datum["type"] == "list": p_elem = paragraph._p parent = p_elem.getparent() index = parent.index(p_elem) values = datum["value"] # 删除原段落 parent.remove(p_elem) # 为每个值插入新段落 for i, val in enumerate(values): new_para = document_file.add_paragraph() new_para.text = str(val) parent.insert(index + i, new_para._p) elif datum["type"] == "object_list": p_elem = paragraph._p parent = p_elem.getparent() index = parent.index(p_elem) values = datum["value"] # 删除原段落(占位符) parent.remove(p_elem) # 为每个对象插入新段落 for i, obj_item in enumerate(values): new_para = document_file.add_paragraph() item_type = obj_item.get("type") if item_type == "text": new_para.add_run(str(obj_item["value"])) elif item_type == "number": new_para.add_run(str(obj_item["value"])) elif item_type == "picture": width = float(obj_item.get("width", 2.0)) new_para.add_run().add_picture(obj_item["value"], width=Inches(width)) else: new_para.add_run(str(obj_item.get("value", ""))) # 插入到原位置 parent.insert(index + i, new_para._p) if not find: paragraph.text = "" # 表格替换 for table in document_file.tables: # 字段 group = None fields = [] repeat = False for row in table.rows: if repeat: break for cell in row.cells: if "." in cell.text: split = cell.text.replace("}","").split(".") group = split[0] + "}" field = split[1] fields.append(field.strip()) repeat = True else: for paragraph in cell.paragraphs: for run in paragraph.runs: find = False for datum in data_json: if datum["key"] in cell.text: find = True if "text" == datum["type"]: run.text = run.text.replace(datum["key"], datum["value"]) elif "picture" == datum["type"]: paragraph = cell.paragraphs[0] paragraph.clear() paragraph.add_run().add_picture(datum["value"], width=Inches(datum["width"])) elif "number" == datum["type"]: run.text = run.text.replace(datum["key"], str(datum["value"])) if not find and run.text.startswith("{") and run.text.endswith("}"): run.text = "" # 替换 if repeat: for datum in data_json: rows = len(table.rows) if datum["key"] in group: values = datum["value"] # 读取表格格式模板(保存每列的格式信息) column_formats = [] if rows > 0: template_row = table.rows[0] for i, cell in enumerate(template_row.cells): if i < len(fields): # 只需要处理与数据字段对应的列 if cell.paragraphs: para = cell.paragraphs[0] # 保存列格式信息 column_format = { 'font_name': '', 'font_size': None, 'alignment': para.alignment, 'bold': False, 'italic': False, 'underline': False, } # 优先使用run的格式(如果有) if para.runs: run = para.runs[0] if run.font.name: column_format['font_name'] = run.font.name if run.font.size: column_format['font_size'] = run.font.size if run.font.bold is not None: column_format['bold'] = run.font.bold if run.font.italic is not None: column_format['italic'] = run.font.italic if run.font.underline is not None: column_format['underline'] = run.font.underline # 保存字体颜色 if run.font.color.rgb: column_format['font_color'] = run.font.color.rgb # 如果没有run,使用样式的格式 else: if para.style.font.name: column_format['font_name'] = para.style.font.name if para.style.font.size: column_format['font_size'] = para.style.font.size if para.style.font.bold is not None: column_format['bold'] = para.style.font.bold if para.style.font.italic is not None: column_format['italic'] = para.style.font.italic if para.style.font.underline is not None: column_format['underline'] = para.style.font.underline column_formats.append(column_format) # 处理每一行数据 for (i, item) in enumerate(values): if i < rows: row = table.rows[i] else: row = table.add_row() # 复制行高 if i == 0 and rows > 0: row.height = template_row.height row.height_rule = template_row.height_rule # 处理每一列数据和格式 for z in range(0, len(fields)): field = fields[z] cell = row.cells[z] # 设置单元格内容 if item.get(field) is not None: cell.text = str(item.get(field)) else: cell.text = "" # 应用表格格式 if z < len(column_formats) and cell.paragraphs: format_info = column_formats[z] para = cell.paragraphs[0] # 设置段落对齐方式 para.alignment = format_info['alignment'] # 设置字体格式 if para.runs: run = para.runs[0] if format_info['font_name']: run.font.name = format_info['font_name'] if format_info['font_size']: run.font.size = format_info['font_size'] run.font.bold = format_info['bold'] run.font.italic = format_info['italic'] run.font.underline = format_info['underline'] # 设置字体颜色 if 'font_color' in format_info: run.font.color.rgb = format_info['font_color'] document_file.save(filename_path) return 0 def main(): params = get_command_argv_by_sys() [template, filename, datafile] = params data = read_data(datafile) if len(data) == 0: return 1 else: return replace(template, filename, data) if __name__ == '__main__': print(main())