如何用iTextSharp获取PDF指定页指定行中单词的X/Y坐标?
问题描述
现有一份包含数百个CNC参数说明的PDF文档,使用C#结合iTextSharp处理的流程如下:
- 定位到目标参数的定义词及对应页码;
- 调用方法传入参数名和页码,获取该参数在PDF中的X、Y坐标;
- 根据坐标添加命名目标。
当前流程整体正常,但步骤2存在缺陷:现有方法仅能通过页码和参数名查找坐标,当目标页中存在多行包含相同参数名时,代码总会定位到第一个匹配项。需要扩展方法,新增line_no参数,方法定义修改为:
public void get_position_of_string_in_PDF(string sys_var_definition, Int32 page_no , int line_no, out float X_Pos, out float Y_Pos )
现有代码如下:
public void get_position_of_string_in_PDF(string sys_var_definition ,Int32 page_no , out float X_Pos, out float Y_Pos ) { using (var reader = new PdfReader(@"C:\inetpub\wmi_asp\wwwroot\files\MTX\MacodaV14.pdf")) { var parser = new PdfReaderContentParser(reader); var strategy = parser.ProcessContent(page_no, new LocationTextExtractionStrategyWithPosition()); var res = strategy.GetLocations(); reader.Close(); var searchResult = res.Where(p => p.Text.Contains(sys_var_definition)).OrderBy(p => p.Y).Reverse().ToList(); X_Pos = searchResult[0].X; Y_Pos = searchResult[0].Y+50; Sys_VAR_dest_name_Pos_X_array.Add(X_Pos); Sys_VAR_dest_name_Pos_Y_array.Add(Y_Pos); } }
由于项目大量依赖iTextSharp,无法立即替换,需改进代码以支持指定行号获取参数位置。
解决方案
要实现指定行号的查找,核心是先将PDF页面的文本片段按行分组,再定位到目标行后查找参数。具体修改如下:
public void get_position_of_string_in_PDF(string sys_var_definition, Int32 page_no, int line_no, out float X_Pos, out float Y_Pos) { // 初始化输出参数 X_Pos = 0f; Y_Pos = 0f; using (var reader = new PdfReader(@"C:\inetpub\wmi_asp\wwwroot\files\MTX\MacodaV14.pdf")) { var parser = new PdfReaderContentParser(reader); var strategy = parser.ProcessContent(page_no, new LocationTextExtractionStrategyWithPosition()); var textLocations = strategy.GetLocations(); reader.Close(); // 1. 按行分组:PDF中同一行的文本片段Y坐标差异极小,取整后作为分组键 var lineGroups = textLocations .GroupBy(p => Math.Round(p.Y, 0)) .OrderByDescending(g => g.Key) // 按Y降序排序,对应页面从上到下的行顺序 .ToList(); // 2. 验证行号有效性(line_no从1开始计数) if (line_no < 1 || line_no > lineGroups.Count) { throw new ArgumentOutOfRangeException(nameof(line_no), "指定的行号超出页面范围"); } // 3. 获取目标行的所有文本片段 var targetLine = lineGroups[line_no - 1]; // 4. 在目标行中查找包含目标参数的文本片段 var match = targetLine.FirstOrDefault(p => p.Text.Contains(sys_var_definition)); if (match == null) { throw new InvalidOperationException($"目标行中未找到参数:{sys_var_definition}"); } // 5. 赋值坐标(保留原有+50的偏移逻辑) X_Pos = match.X; Y_Pos = match.Y + 50; // 添加到数组 Sys_VAR_dest_name_Pos_X_array.Add(X_Pos); Sys_VAR_dest_name_Pos_Y_array.Add(Y_Pos); } }
关键说明
- 行分组逻辑:利用PDF的Y坐标特性(原点在左下角,Y值越大越靠上),将Y坐标取整后作为分组键,确保同一行的文本片段被归为一组。如果PDF中文本行的Y坐标差异较大,可调整
Math.Round的精度(如保留1位小数),或改用基于区间的分组方式(如GroupBy(p => (int)(p.Y / 5) * 5))。 - 行号计数:代码假设传入的
line_no是从1开始的(对应页面第一行),如果你的行号从0开始,只需去掉line_no - 1中的减1操作。 - 异常处理:新增了行号越界和目标行无匹配的异常抛出,避免索引越界或空引用错误,可根据项目需求调整为返回默认值而非抛出异常。
内容的提问来源于stack exchange,提问作者Mdarende
相关产品推荐
相关产品推荐

