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

如何使用ASP.NET Core与PdfPig去除PDF发票重复页脚

解决PdfPig提取发票PDF时重复页脚的问题

问题1:页脚仅保留一次或单独提取的方案

PdfPig本身没有直接识别、过滤页脚的内置功能,需要通过提取时的位置过滤或提取后的文本后处理来实现:

  • 提取时过滤:识别每页底部的页脚区域,只保留最后一页的页脚,其余页面的页脚直接跳过;
  • 提取后处理:先提取所有页面的完整文本,再通过内容匹配找出重复的页脚内容,仅保留最后一次出现的版本,或者单独提取所有页脚内容合并去重。

问题2:判断WordElement是否为页脚的方法

可以通过位置判断和内容匹配两种方式实现,结合C#文本处理或正则即可完成过滤:

方法1:基于页面位置判断(最可靠)

页脚通常固定在页面底部的特定区域,可以通过Word的BoundingBox计算相对页面的位置:

  1. 获取页面的总高度:page.Height;
  2. 定义页脚区域的阈值,比如页面底部10%的区域(可根据实际发票调整);
  3. 判断Word的BoundingBox.Bottom是否小于等于页面高度的10%(PDF坐标原点在左下角,Bottom值越小越靠近底部)。

方法2:基于内容正则匹配

如果页脚有固定格式(比如包含发票号、公司名称、固定标语等),可以编写对应正则表达式匹配内容,标记并过滤这类重复内容。


修改后的完整代码示例

以下代码结合位置判断实现提取时过滤中间页的页脚,仅保留最后一页的页脚:

async Task<string> ExtractTextPdfPigAsync(MemoryStream pdfStream)
{
    pdfStream.Position = 0;
    StringBuilder tulem = new();
    using UglyToad.PdfPig.PdfDocument document = UglyToad.PdfPig.PdfDocument.Open(pdfStream, new UglyToad.PdfPig.ParsingOptions() { SkipMissingFonts = true });
    var pages = document.GetPages().ToList();
    // 遍历所有页面,最后一页单独处理保留页脚
    for (int i = 0; i < pages.Count; i++)
    {
        var page = pages[i];
        // 仅最后一页保留页脚,其余页面提取时过滤页脚
        GetWordsInReadingOrder(page, tulem, isLastPage: i == pages.Count - 1);
        // 页面间添加换行分隔
        if (i != pages.Count - 1)
        {
            tulem.AppendLine();
            tulem.AppendLine("---");
        }
    }
    return tulem.ToString();
}

/// <summary>
/// 按阅读顺序提取页面文本,可选择是否保留页脚
/// </summary>
/// <param name="page">目标页面</param>
/// <param name="builder">文本构建器</param>
/// <param name="isLastPage">是否为最后一页(保留页脚)</param>
public static void GetWordsInReadingOrder(Page page, StringBuilder builder, bool isLastPage = false)
{
    List<Word> words = page.GetWords().ToList();
    if (words.Count == 0)
        return;

    // 过滤页脚:如果不是最后一页,移除页面底部10%区域的文字
    if (!isLastPage)
    {
        double footerThreshold = page.Height * 0.1; // 底部10%区域定义为页脚
        words = words.Where(w => w.BoundingBox.Bottom > footerThreshold).ToList();
        if (words.Count == 0)
            return;
    }

    List<Word> sorted = words.OrderBy(r => r.BoundingBox.Bottom).ThenBy(r => r.BoundingBox.Left).ToList();

    List<WordsWithLines> lines = [];
    const double rowHeight = 10;
    double lnLastTop = 0;
    int lnLastRow = 1;
    double csvReaVpos = sorted.First().BoundingBox.Bottom;

    foreach (Word r in sorted)
    {
        if (r.BoundingBox.Bottom >= lnLastTop + (0.5 * rowHeight) || r.BoundingBox.Bottom - csvReaVpos > rowHeight)
        {
            ++lnLastRow;
            csvReaVpos = r.BoundingBox.Bottom;
        }
        lnLastTop = r.BoundingBox.Bottom;
        lines.Add(new WordsWithLines()
        {
            RowNo = lnLastRow,
            WordElement = r
        });
    }

    IOrderedEnumerable<WordsWithLines> wordsList = lines.OrderByDescending(r => r.RowNo).ThenBy(r => r.WordElement.BoundingBox.Left);

    int rowNo = wordsList.First().RowNo;
    bool first = true;
    foreach (WordsWithLines word in wordsList)
    {
        if (rowNo != word.RowNo)
        {
            builder.AppendLine();
            first = true;
            rowNo = word.RowNo;
        }
        if (!first)
            builder.Append(' ');
        builder.Append(word.WordElement.Text);
        first = false;
    }
}

sealed class WordsWithLines
{
    public int RowNo;
    public Word WordElement;
}

正则去重的补充方案

如果需要提取后通过内容去重,可以在提取完成后添加以下逻辑:

// 假设页脚正则为匹配固定格式的内容,比如包含"公司名称"和"发票号"的行
string footerPattern = @"^.*公司名称.*发票号:\d+.*$";
Regex footerRegex = new Regex(footerPattern, RegexOptions.Multiline | RegexOptions.IgnoreCase);

string fullText = tulem.ToString();
// 找出所有页脚匹配项
var matches = footerRegex.Matches(fullText).Cast<Match>().Select(m => m.Value).Distinct().ToList();
// 移除所有页脚,再添加一次唯一的页脚
string cleanedText = footerRegex.Replace(fullText, "");
if (matches.Any())
{
    cleanedText += Environment.NewLine + Environment.NewLine + matches.First();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:00:16