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

C#/Blazor项目中PPT转PDF文本提取至Elasticsearch的布局保留方案咨询

C#/Blazor项目中PPT转PDF文本提取至Elasticsearch的布局保留方案咨询

我太懂这种糟心的情况了——PPT转的PDF因为排版逻辑和原生PDF不一样,用普通的文本提取工具确实会把原本清晰的标题、项目符号全拆成零散的行,存到Elasticsearch里搜起来、读起来都费劲。结合你现在用的iText和Blazor场景,给你几个可行的方向:

1. 改用iText的结构化文本提取策略

你现在用的PdfTextExtractor.GetTextFromPage()默认是用SimpleTextExtractionStrategy,它只会按文本的读取顺序拼接,完全忽略布局信息。其实iText提供了LocationTextExtractionStrategy,可以获取到每个文本块的位置、字体大小、缩进等元数据,你可以基于这些信息来重建布局:

  • 字体大小明显更大的文本块,大概率是PPT里的标题,你可以给它加上加粗标记;
  • 有明显左侧缩进的文本块,就是PPT里的项目符号列表,你可以给它加上- 前缀;

给你调整后的代码示例,核心是用带位置信息的提取策略:

using var reader = new PdfReader(file.Path);
using var pdf = new PdfDocument(reader);
var strategy = new LocationTextExtractionStrategy();

for (var i = 1; i <= pdf.GetNumberOfPages(); i++)
{
    var page = pdf.GetPage(i);
    PdfTextExtractor.GetTextFromPage(page, strategy);
    var textChunks = strategy.GetResultantTextChunks();
    
    var processedText = new StringBuilder();
    float? lastFontSize = null;
    foreach (var chunk in textChunks)
    {
        // 这里可以根据字体大小判断标题(比如大于14号的是标题)
        if (chunk.FontSize > 14 && !string.IsNullOrWhiteSpace(chunk.Text.Trim()))
        {
            processedText.AppendLine($"**{chunk.Text.Trim()}**");
            lastFontSize = chunk.FontSize;
        }
        // 根据缩进判断列表项(比如左侧偏移大于50的是列表)
        else if (chunk.GetStartLocation().Get(Vector.I1) > 50)
        {
            processedText.AppendLine($"- {chunk.Text.Trim()}");
        }
        else
        {
            // 普通段落,和上一段同字体就拼接,不同就换行
            if (lastFontSize.HasValue && lastFontSize != chunk.FontSize)
            {
                processedText.AppendLine();
            }
            processedText.Append(chunk.Text);
            lastFontSize = chunk.FontSize;
        }
    }
    
    await elasticService.WriteAsync(new ElasticWriteModel
    {
        DocId = document.Id,
        Page = i,
        Text = processedText.ToString(),
        AccessLevel = document.AccessLevel == AccessLevel.Private ? 0 : 1,
        Timestamp = DateTime.UtcNow
    }, cancellationToken);
    
    // 重置策略,处理下一页
    strategy.Reset();
}

2. 利用PDF的结构化标签(如果存在)

有些PPT转PDF时(比如用Microsoft Office导出),会保留PDF的结构化标签,你可以直接遍历PDF的结构树来获取层级化的内容,而不是纯文本:

using var reader = new PdfReader(file.Path);
using var pdf = new PdfDocument(reader);
var structTree = new PdfStructTreeController(pdf);

for (var i = 1; i <= pdf.GetNumberOfPages(); i++)
{
    var page = pdf.GetPage(i);
    var structRoot = structTree.GetStructRoot();
    var processedText = new StringBuilder();
    
    // 递归遍历结构树节点
    void TraverseStructNode(PdfStructElem node)
    {
        if (node.GetRole() == PdfName.H1)
        {
            processedText.AppendLine($"# {node.GetKidsContent()}");
        }
        else if (node.GetRole() == PdfName.L)
        {
            // 处理列表节点
            foreach (var kid in node.GetKids())
            {
                if (kid is PdfStructElem listItem)
                {
                    processedText.AppendLine($"- {listItem.GetKidsContent()}");
                }
            }
        }
        else if (node.GetRole() == PdfName.P)
        {
            processedText.AppendLine(node.GetKidsContent());
        }
        // 递归处理子节点
        foreach (var kid in node.GetKids())
        {
            if (kid is PdfStructElem childNode)
            {
                TraverseStructNode(childNode);
            }
        }
    }
    
    if (structRoot != null)
    {
        TraverseStructNode(structRoot);
    }
    
    // 写入Elasticsearch的逻辑和你原来的一致
}

这个方法的前提是你的PDF保留了结构化标签,如果是用轻量工具转的PDF可能没有,这时候就需要用第一种方法。

3. 对提取后的纯文本做后处理

如果上面两种方法对你来说有点复杂,也可以对已提取的纯文本做简单的后处理,比如:

  • 识别开头有多个空格的行,当成列表项,替换成- 前缀;
  • 识别短且全大写的行,当成标题,加上加粗标记;

示例代码片段:

var pageText = PdfTextExtractor.GetTextFromPage(pdf.GetPage(i));
var lines = pageText.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var processedLines = new List<string>();

foreach (var line in lines)
{
    var trimmedLine = line.TrimStart();
    var indentLength = line.Length - trimmedLine.Length;
    
    // 缩进超过4个空格,判定为列表项
    if (indentLength > 4)
    {
        processedLines.Add($"- {trimmedLine}");
    }
    // 短文本且全大写,判定为标题
    else if (trimmedLine.Length < 30 && trimmedLine.All(c => char.IsUpper(c) || char.IsWhiteSpace(c)))
    {
        processedLines.Add($"**{trimmedLine}**");
    }
    else
    {
        processedLines.Add(trimmedLine);
    }
}

var structuredText = string.Join(Environment.NewLine, processedLines);
// 然后用structuredText写入Elasticsearch

额外提醒

PPT转PDF的工具会直接影响提取效果,尽量用Microsoft Office原生导出PDF,而不是第三方轻量工具——原生导出的PDF会保留更多排版元数据,不管用哪种提取方法效果都更好。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 10:33:06