如何用iTextSharp解析PDF指定页位置及书签间文本?
问题1:使用C# + iTextSharp从指定页面位置提取文档片段
要实现这个需求,核心是利用iTextSharp的文本提取策略,结合区域过滤来精准获取指定位置的文本。这里要特别注意:iTextSharp的坐标系统以页面左下角为原点,y轴向上,别和其他系统的坐标搞混了。
实现步骤与代码示例
- 初始化
PdfReader加载目标PDF - 定义起始/结束的页码和坐标位置
- 针对不同页面(起始页、中间页、结束页)设置对应的文本过滤区域
- 提取过滤后的文本并拼接结果
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等),我们需要先解析这些参数。
实现步骤与代码示例
- 解析书签的
Page属性,获取起始/结束的页码和坐标 - 递归查找目标书签(包括子书签)
- 调用问题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
相关产品推荐
相关产品推荐

