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

如何用iTextSharp解析PDF指定页位置及书签间文本?

问题1:使用C# + iTextSharp从指定页面位置提取文档片段

要实现这个需求,核心是利用iTextSharp的文本提取策略,结合区域过滤来精准获取指定位置的文本。这里要特别注意:iTextSharp的坐标系统以页面左下角为原点,y轴向上,别和其他系统的坐标搞混了。

实现步骤与代码示例

  1. 初始化PdfReader加载目标PDF
  2. 定义起始/结束的页码和坐标位置
  3. 针对不同页面(起始页、中间页、结束页)设置对应的文本过滤区域
  4. 提取过滤后的文本并拼接结果
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using System.Text;

public string ExtractTextBetweenPositions(string pdfPath, int startPage, float startY, int endPage, float endY)
{
    var resultBuilder = new StringBuilder();
    
    using (var pdfReader = new PdfReader(pdfPath))
    {
        for (int currentPage = startPage; currentPage <= endPage; currentPage++)
        {
            var pageSize = pdfReader.GetPageSize(currentPage);
            float pageWidth = pageSize.Width;
            float pageHeight = pageSize.Height;

            RenderFilter textFilter = null;

            if (currentPage == startPage && currentPage == endPage)
            {
                // 同一页内提取:取两个y坐标之间的区域
                float minY = Math.Min(startY, endY);
                float maxY = Math.Max(startY, endY);
                textFilter = new RegionTextRenderFilter(new Rectangle(0, minY, pageWidth, maxY - minY));
            }
            else if (currentPage == startPage)
            {
                // 起始页:提取从startY到页面顶部的区域
                textFilter = new RegionTextRenderFilter(new Rectangle(0, startY, pageWidth, pageHeight - startY));
            }
            else if (currentPage == endPage)
            {
                // 结束页:提取从页面底部到endY的区域
                textFilter = new RegionTextRenderFilter(new Rectangle(0, 0, pageWidth, endY));
            }
            // 中间页直接提取整页文本,无需过滤

            ITextExtractionStrategy extractionStrategy;
            if (textFilter != null)
            {
                // 应用区域过滤的文本提取策略
                extractionStrategy = new FilteredTextRenderListener(
                    new LocationTextExtractionStrategy(), textFilter);
            }
            else
            {
                // 默认的位置文本提取策略(保持文本顺序)
                extractionStrategy = new LocationTextExtractionStrategy();
            }

            string pageText = PdfTextExtractor.GetTextFromPage(pdfReader, currentPage, extractionStrategy);
            resultBuilder.Append(pageText);
        }
    }

    return resultBuilder.ToString().Trim();
}

注意事项

  • 如果你的位置坐标来自其他系统(比如Word的左上角原点),需要转换为PDF坐标:pdfY = pageHeight - wordY
  • 若PDF是扫描件(纯图片格式),iTextSharp无法直接提取文本,需要先做OCR处理
问题2:提取PDF书签对应的文本内容

你已经能获取书签列表了,接下来就是解析书签的位置信息,然后提取两个书签之间的文本。书签的Page属性通常包含页码和位置参数(比如XYZ、FitH等),我们需要先解析这些参数。

实现步骤与代码示例

  1. 解析书签的Page属性,获取起始/结束的页码和坐标
  2. 递归查找目标书签(包括子书签)
  3. 调用问题1的方法提取书签间的文本

首先实现书签位置解析方法

private void ParseBookmarkPagePosition(string pageAttribute, out int pageNumber, out float yCoordinate)
{
    pageNumber = 1;
    yCoordinate = 0;

    string[] parts = pageAttribute.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
    if (parts.Length == 0) return;

    // 解析页码
    int.TryParse(parts[0], out pageNumber);

    // 解析位置参数(处理XYZ、FitH两种常见格式)
    for (int i = 1; i < parts.Length; i++)
    {
        if (parts[i].Equals("XYZ", StringComparison.OrdinalIgnoreCase) && i + 2 < parts.Length)
        {
            float.TryParse(parts[i + 2], out yCoordinate);
            break;
        }
        else if (parts[i].Equals("FitH", StringComparison.OrdinalIgnoreCase) && i + 1 < parts.Length)
        {
            float.TryParse(parts[i + 1], out yCoordinate);
            break;
        }
        // 若需要支持其他格式(如FitV、FitR),可在此扩展
    }
}

然后实现书签文本提取的主方法

public string ExtractTextByBookmark(string pdfPath, string targetBookmarkTitle)
{
    using (var pdfReader = new PdfReader(pdfPath))
    {
        var bookmarks = SimpleBookmark.GetBookmark(pdfReader);
        if (bookmarks == null) return "未找到任何书签";

        // 查找目标书签和对应的下一个书签(作为结束位置)
        Dictionary<string, object> targetBookmark = null;
        Dictionary<string, object> nextBookmark = null;

        // 先遍历顶级书签
        for (int i = 0; i < bookmarks.Count; i++)
        {
            var bookmark = bookmarks[i];
            if (bookmark["Title"].ToString().Equals(targetBookmarkTitle, StringComparison.OrdinalIgnoreCase))
            {
                targetBookmark = bookmark;
                nextBookmark = i + 1 < bookmarks.Count ? bookmarks[i + 1] : null;
                break;
            }

            // 递归查找子书签
            if (bookmark.ContainsKey("Kids"))
            {
                var childBookmarks = (List<Dictionary<string, object>>)bookmark["Kids"];
                targetBookmark = FindBookmarkByTitle(childBookmarks, targetBookmarkTitle);
                if (targetBookmark != null)
                {
                    // 找子书签的下一个兄弟
                    int childIndex = childBookmarks.IndexOf(targetBookmark);
                    nextBookmark = childIndex + 1 < childBookmarks.Count 
                        ? childBookmarks[childIndex + 1] 
                        : (i + 1 < bookmarks.Count ? bookmarks[i + 1] : null);
                    break;
                }
            }
        }

        if (targetBookmark == null) return $"未找到名为「{targetBookmarkTitle}」的书签";

        // 解析目标书签的起始位置
        ParseBookmarkPagePosition(targetBookmark["Page"].ToString(), out int startPage, out float startY);
        var startPageSize = pdfReader.GetPageSize(startPage);

        // 解析结束位置(默认到最后一页底部)
        int endPage = pdfReader.NumberOfPages;
        float endY = 0;
        if (nextBookmark != null)
        {
            ParseBookmarkPagePosition(nextBookmark["Page"].ToString(), out endPage, out endY);
        }

        // 调用问题1的方法提取文本
        return ExtractTextBetweenPositions(pdfPath, startPage, startY, endPage, endY);
    }
}

// 递归查找子书签的辅助方法
private Dictionary<string, object> FindBookmarkByTitle(List<Dictionary<string, object>> bookmarks, string title)
{
    foreach (var bookmark in bookmarks)
    {
        if (bookmark["Title"].ToString().Equals(title, StringComparison.OrdinalIgnoreCase))
        {
            return bookmark;
        }
        if (bookmark.ContainsKey("Kids"))
        {
            var result = FindBookmarkByTitle((List<Dictionary<string, object>>)bookmark["Kids"], title);
            if (result != null) return result;
        }
    }
    return null;
}

注意事项

  • 书签的Page属性格式可能有多种,若你的PDF使用了其他定位格式(如FitB、FitR),需要扩展ParseBookmarkPagePosition方法的解析逻辑
  • 文本提取的准确性依赖于PDF的内部结构,Word转的PDF通常结构良好,提取效果稳定;但如果PDF是经过多次编辑或压缩的,可能会有文本顺序错乱的情况

内容的提问来源于stack exchange,提问作者amval17

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:39:02