使用python-docx拆分Word文档时图片无法正常显示的问题
问题原因与解决方案
问题根源
你直接将原文档的XML元素(element)追加到新文档的body中,仅复制了图片的占位符结构,未同步复制图片的实际二进制数据。Word文档的图片存储在独立的媒体部件(Parts)中,XML元素通过rId引用这些部件;新文档没有对应的媒体部件,因此无法加载图片,显示“Can't display drawing”。
修复方案
不能直接操作底层XML元素进行迁移,必须同步复制图片的媒体部件,并更新引用ID(rId)。以下是修改后的核心逻辑:
替换原元素追加逻辑
删除原代码中elements_to_save.append(element)的逻辑,改用高层API+部件复制的方式迁移内容,确保图片资源被正确拷贝:
# 初始化新文档时保留这行 doc2 = docx.Document() # 遍历原文档内容块时,替换原有的元素处理逻辑 for block in doc.element.body: if isinstance(block, CT_Tbl): table = Table(block, doc) # 创建新表格并复制结构 new_table = doc2.add_table(rows=len(table.rows), cols=len(table.columns)) # 复制单元格内容(含图片) for i, row in enumerate(table.rows): for j, cell in enumerate(row.cells): new_cell = new_table.cell(i, j) # 复制单元格内的所有段落 for p in cell.paragraphs: new_p = new_cell.add_paragraph() for run in p.runs: new_run = new_p.add_run(run.text) # 复制文字格式 new_run.bold = run.bold new_run.italic = run.italic new_run.font.size = run.font.size # 处理run内的嵌入式图片 for inline_shape in run._element.xpath('.//pic:pic'): # 获取原文档中图片的引用ID rId = inline_shape.xpath('.//a:blip/@r:embed')[0] # 从原文档获取图片部件 image_part = doc.part.related_parts[rId] # 在新文档中添加图片部件,生成新的引用ID new_rId = doc2.part.relate_to( image_part.target_ref, image_part.reltype, is_external=False ) # 更新图片元素的引用ID为新文档的ID inline_shape.xpath('.//a:blip')[0].set('r:embed', new_rId) # 将处理后的图片元素添加到新run中 new_run._element.append(inline_shape) # --- 保留你原有的表格拆分、目录创建等业务逻辑 --- # (即原代码中if isinstance(element, CT_Tbl):下的业务代码,除了elements_to_save相关部分) elif isinstance(block, CT_P): p = Paragraph(block, doc) new_p = doc2.add_paragraph() for run in p.runs: new_run = new_p.add_run(run.text) # 复制文字格式 new_run.bold = run.bold new_run.italic = run.italic new_run.font.size = run.font.size # 处理run内的嵌入式图片 for inline_shape in run._element.xpath('.//pic:pic'): rId = inline_shape.xpath('.//a:blip/@r:embed')[0] image_part = doc.part.related_parts[rId] new_rId = doc2.part.relate_to( image_part.target_ref, image_part.reltype, is_external=False ) inline_shape.xpath('.//a:blip')[0].set('r:embed', new_rId) new_run._element.append(inline_shape) # --- 保留你原有的段落提取编号等业务逻辑 --- # (即原代码中if isinstance(element, CT_P):下的业务代码) # 当需要保存文档时,直接保存doc2即可(无需elements_to_save的循环) doc2.save(f'{pv_dir_path}/{doc_name_unique}.docx')
关键说明
- 媒体部件复制:通过
doc.part.related_parts[rId]获取原图片的二进制部件,再用doc2.part.relate_to()将其添加到新文档,生成新的rId。 - 引用更新:将图片XML元素中的
r:embed属性替换为新文档的rId,确保新文档能找到对应的图片资源。 - 格式同步:复制段落、run的格式(加粗、斜体等),保证拆分后的文档与原格式一致。
浮动图片处理(可选)
如果原文档包含浮动图片(非嵌入式),需额外处理wp:anchor元素,逻辑与嵌入式图片一致:
# 在处理run的元素时,添加浮动图片的处理 for anchor in run._element.xpath('.//wp:anchor'): rId = anchor.xpath('.//a:blip/@r:embed')[0] image_part = doc.part.related_parts[rId] new_rId = doc2.part.relate_to(image_part.target_ref, image_part.reltype, is_external=False) anchor.xpath('.//a:blip')[0].set('r:embed', new_rId) new_run._element.append(anchor)
内容的提问来源于stack exchange,提问作者Georgii
相关产品推荐
相关产品推荐

