如何用C#和OpenXML复制Word书签内容并保留格式与图表
用C# + OpenXML复制Word书签内的富内容(含格式、表格、图表)
直接上可运行的解决方案,核心是正确复制书签范围内的所有节点,包括关联的文档部件(比如图表):
完整代码
先确保安装DocumentFormat.OpenXml NuGet包,然后用下面的方法:
using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using System.Collections.Generic; using System.Linq; public static void CopyBookmarkContent(string sourceDocPath, string sourceBookmarkName, string targetDocPath, string targetBookmarkName, string outputPath) { // 先复制目标模板到输出路径,避免修改原文件 System.IO.File.Copy(targetDocPath, outputPath, overwrite: true); // 打开源文档(只读)和输出文档(可编辑) using (var sourceDoc = WordprocessingDocument.Open(sourceDocPath, isEditable: false)) using (var targetDoc = WordprocessingDocument.Open(outputPath, isEditable: true)) { var sourceMainPart = sourceDoc.MainDocumentPart; var targetMainPart = targetDoc.MainDocumentPart; // 定位源书签的起始和结束节点 var sourceBookmarkStart = sourceMainPart.Document.Body.Descendants<BookmarkStart>() .FirstOrDefault(b => b.Name == sourceBookmarkName); var sourceBookmarkEnd = sourceMainPart.Document.Body.Descendants<BookmarkEnd>() .FirstOrDefault(b => b.Id == sourceBookmarkStart.Id); if (sourceBookmarkStart == null || sourceBookmarkEnd == null) throw new System.Exception("找不到源书签"); // 收集书签内的所有内容节点,深克隆保留格式 var contentNodes = new List<OpenXmlElement>(); var currentNode = sourceBookmarkStart.NextSibling(); while (currentNode != null && currentNode != sourceBookmarkEnd) { var clonedNode = currentNode.CloneNode(deep: true); contentNodes.Add(clonedNode); // 处理图表:复制关联的ChartPart到目标文档 if (clonedNode is Drawing drawing) { var chartRef = drawing.Descendants<DocumentFormat.OpenXml.Drawing.Charts.ChartReference>().FirstOrDefault(); if (chartRef != null) { var sourceChartPart = (ChartPart)sourceMainPart.GetPartById(chartRef.Id); var targetChartPart = targetMainPart.AddNewPart<ChartPart>(); // 复制图表数据 sourceChartPart.FeedData(targetChartPart.GetStream()); // 更新克隆节点的引用ID,指向目标文档的新ChartPart chartRef.Id = targetMainPart.GetIdOfPart(targetChartPart); } } currentNode = currentNode.NextSibling(); } // 定位目标书签的起始节点 var targetBookmarkStart = targetMainPart.Document.Body.Descendants<BookmarkStart>() .FirstOrDefault(b => b.Name == targetBookmarkName); if (targetBookmarkStart == null) throw new System.Exception("找不到目标书签"); // 在目标书签起始位置后插入所有复制的内容 foreach (var node in contentNodes) { targetBookmarkStart.Parent.InsertAfter(node, targetBookmarkStart); } // 保存修改 targetMainPart.Document.Save(); } }
调用示例
// 替换成你的实际路径和书签名称 CopyBookmarkContent( sourceDocPath: @"C:\Temp\Source.docx", sourceBookmarkName: "ToBeSaved2", targetDocPath: @"C:\Temp\Template.docx", targetBookmarkName: "ToBeSaved2", outputPath: @"C:\Temp\NewVersion.docx" );
关键注意事项
- 深克隆节点:用
CloneNode(deep: true)才能复制节点的所有子元素和格式属性,不然会丢失字体、段落样式等。 - 图表单独处理:Word的图表是作为独立的
ChartPart存储的,必须把这个部件复制到目标文档,同时更新正文里的引用ID,否则插入后图表无法显示。 - 保护原模板:先复制目标模板到输出路径再修改,避免破坏原始模板文件。
内容的提问来源于stack exchange,提问作者kristoufiol
相关产品推荐
相关产品推荐

