使用Python复制Word文档样式并生成新文档
在日常办公自动化中,我们常常需要保留Word文档原有格式,仅替换部分内容生成新文档。本文将介绍如何使用 python-docx
和 lxml
操作 Word 文档的底层 XML 结构,实现样式与内容分离的文档生成方案。
🧰 核心功能概述
本方案包含以下关键技术点:
- 段落样式复制:精确还原字体、颜色、对齐方式等
- 表格样式复制:包括单元格边框、列宽等细节
- 分页符处理:保留原文档的页面布局
- 模板驱动开发:通过 JSON 数据驱动文档生成
📦 环境准备
pip install python-docx lxml
🧱 核心代码解析
1. 段落样式复制器
def copy_paragraph_style(run_from, run_to):"""复制 run 的样式"""run_to.bold = run_from.boldrun_to.italic = run_from.italicrun_to.underline = run_from.underlinerun_to.font.size = run_from.font.sizerun_to.font.color.rgb = run_from.font.color.rgbrun_to.font.name = run_from.font.namerun_to.font.all_caps = run_from.font.all_capsrun_to.font.strike = run_from.font.strikerun_to.font.shadow = run_from.font.shadow
✅ 支持复制 8 种常见文本属性,可扩展添加更多样式(如高亮色、下标等)
2. 分页符检测器
def is_page_break(element):"""判断元素是否为分页符"""if element.tag.endswith('p'):for child in element:if child.tag.endswith('br') and child.get(qn('type')) == 'page':return True# 表格后分页符处理...return False
📌 通过解析 XML 元素检测分页符,确保版式一致性
3. 段落克隆器
def clone_paragraph(para_style, old_para, new_doc):"""根据样式模板创建新段落"""new_para = new_doc.add_paragraph()new_para.style = list(para_style["style"].values())[0]new_run = new_para.add_run(old_para)copy_paragraph_style(para_style["run_style"][0], new_run)new_para.alignment = list(para_style["alignment"].values())[0]return new_para
🔄 实现样式继承机制:从模板获取样式 → 创建新段落 → 应用样式 → 插入内容
4. 表格边框复制器
def copy_cell_borders(old_cell, new_cell):"""深度复制单元格边框样式"""old_borders = old_cell._tc.xpath('.//w:tcBorders')if old_borders:new_border = OxmlElement('w:tcBorders')# 遍历复制六类边框(上下左右+内部)for border_type in ['top','left','bottom','right','insideH','insideV']:old_element = old_borders[0].find(f'.//w:{border_type}', namespaces=ns)if old_element is not None:new_element = OxmlElement(f'w:{border_type}')new_element.attrib.update(old_element.attrib)new_border.append(new_element)new_cell._tc.get_or_add_tcPr().append(new_border)
🔍 使用 XPath 定位 XML 节点,精确复制边框属性(类型/宽度/颜色)
📐 模板处理流程
def clone_document(old_s, old_p, new_doc_path):new_doc = Document()# 样式模板 + 内容数据 双驱动for para in old_p:for k, v in para.items():# 匹配样式模板style = [i for i in old_s if v in list(i["style"]