You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 15:35:27