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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 14:56:01