如何用NPOI在C# .NET 4.7中遍历docx所有书签?
在C# .NET 4.7中使用NPOI遍历Docx所有书签(含表格内)并插入图片
默认仅遍历文档直接段落的方式会遗漏表格单元格内的书签,解决核心是递归遍历所有块级元素,包括文档主体段落、表格行、单元格内的段落,再在书签位置插入图片。以下是完整实现:
前置准备
通过NuGet安装NPOI包(选择支持.NET 4.7的版本,如2.5.6及以上),无需安装Office。
核心代码实现
1. 递归收集所有书签
遍历文档的所有段落(含表格单元格内的段落),提取所有BookmarkStart对象:
using NPOI.XWPF.UserModel; using NPOI.OpenXmlFormats.Wordprocessing; using System.IO; using System.Collections.Generic; private static List<BookmarkStart> GetAllBookmarks(XWPFDocument doc) { var bookmarks = new List<BookmarkStart>(); // 遍历文档直接包含的段落 foreach (var para in doc.Paragraphs) { foreach (var run in para.Runs) { foreach (var part in run.GetCTR().GetItems()) { if (part is CT_BookmarkStart bookmarkStart) { bookmarks.Add(new BookmarkStart(bookmarkStart, run)); } } } } // 遍历表格中所有单元格的段落 foreach (var table in doc.Tables) { foreach (var row in table.Rows) { foreach (var cell in row.GetTableCells()) { foreach (var para in cell.Paragraphs) { foreach (var run in para.Runs) { foreach (var part in run.GetCTR().GetItems()) { if (part is CT_BookmarkStart bookmarkStart) { bookmarks.Add(new BookmarkStart(bookmarkStart, run)); } } } } } } } return bookmarks; }
2. 在书签位置插入图片
将图片嵌入文档媒体库,然后在书签对应的Run中插入图片元素:
private static void InsertImageAtBookmark(XWPFDocument doc, BookmarkStart bookmark, string imagePath, int width, int height) { using (var fs = new FileStream(imagePath, FileMode.Open, FileAccess.Read)) { // 根据图片类型调整PictureType,如JPEG、PNG等 string imgId = doc.AddPictureData(fs, PictureType.PNG); var imgRun = bookmark.Run.GetCTR(); var drawing = imgRun.AddNewDrawing(); var inline = drawing.AddNewInline(); // 转换尺寸为EMU(1像素≈9525 EMU,1厘米=360000 EMU) inline.Extent.Cx = width * 9525; inline.Extent.Cy = height * 9525; var docPr = inline.AddNewDocPr(); docPr.Id = (uint)doc.NextPicNameNumber; docPr.Name = $"Picture {docPr.Id}"; var graphic = inline.AddNewGraphic(); var graphicData = graphic.AddNewGraphicData(); graphicData.Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture"; var pic = graphicData.AddNewPic(); var nvPicPr = pic.AddNewNvPicPr(); nvPicPr.AddNewCNvPr().Id = (uint)doc.NextPicNameNumber; nvPicPr.AddNewCNvPicPr(); var blipFill = pic.AddNewBlipFill(); blipFill.AddNewBlip().Embed = imgId; blipFill.AddNewStretch().AddNewFillRect(); var spPr = pic.AddNewSpPr(); var xfrm = spPr.AddNewXfrm(); xfrm.AddNewExt().Cx = inline.Extent.Cx; xfrm.AddNewExt().Cy = inline.Extent.Cy; spPr.AddNewPrstGeom().Prst = ST_ShapeType.rect; } }
3. 主处理逻辑
整合上述方法,批量处理书签与图片的映射:
public static void ProcessDocxBookmarks(string inputDocxPath, string outputDocxPath, Dictionary<string, string> bookmarkToImageMap) { using (var inputStream = new FileStream(inputDocxPath, FileMode.Open, FileAccess.Read)) { var doc = new XWPFDocument(inputStream); var allBookmarks = GetAllBookmarks(doc); foreach (var bookmark in allBookmarks) { if (bookmarkToImageMap.TryGetValue(bookmark.Name, out string imagePath)) { // 插入300x200像素的图片,可根据需求调整尺寸 InsertImageAtBookmark(doc, bookmark, imagePath, 300, 200); } } using (var outputStream = new FileStream(outputDocxPath, FileMode.Create, FileAccess.Write)) { doc.Write(outputStream); } } }
使用示例
// 定义书签名称到图片路径的映射 var bookmarkImageMap = new Dictionary<string, string> { {"HeaderBookmark", @"D:\Images\header.png"}, {"TableCellBookmark", @"D:\Images\content.jpg"} }; // 处理文档 ProcessDocxBookmarks(@"D:\Input.docx", @"D:\Output.docx", bookmarkImageMap);
注意事项
- 图片类型需与
PictureType匹配,避免插入失败 - 尺寸转换可根据需求调整,若需按厘米计算,直接替换为
3 * 360000(3厘米)这类值 - 确保文档中的书签为有效状态(存在
BookmarkStart标记)
内容的提问来源于stack exchange,提问作者Julien Brdy
相关产品推荐
相关产品推荐

