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

如何使用iText7提取PDF文本时避免文本拆分?

问题描述

使用NuGet安装iText7 API编写VB.Net程序提取PDF文本及位置信息时,大量字符串被拆分为多个片段,例如原本的TRANSPORT被拆分为:

Left: 128,2880 /Right: 147,7406 /Top: 693,7059 /Bottom: 688,3118 /Text: [TRANSPO]
Left: 147,7384 /Right: 150,5556 /Top: 693,7059 /Bottom: 688,3118 /Text: [R]
Left: 150,4318 /Right: 153,0811 /Top: 693,7059 /Bottom: 688,3118 /Text: [T]

希望通过iText7原生功能解决该问题,无需自行编写文本拼接逻辑,附带完整VB.Net代码如下:

Imports System.IO
Imports System.Net
Imports System.Runtime.InteropServices
Imports iText.Kernel.Pdf
Imports iText.Kernel.Pdf.Canvas.Parser
Imports iText.Kernel.Pdf.Canvas.Parser.Listener
Imports System.Text
Imports System.Text.RegularExpressions
Imports iText.StyledXmlParser.Jsoup.Nodes
Imports iText.Kernel.Utils
Imports iText.Kernel.Geom
Imports iText.Kernel.Pdf.Canvas.Parser.Data
Imports iText.Commons.Utils
Imports iText.IO.Util
Imports Org.BouncyCastle.Math.EC
Imports System.ComponentModel
Imports iText.StyledXmlParser.Jsoup

Module MainModule

    Public Class XEventListener
        Implements IEventListener

        Private oInfoList As New List(Of TextRenderInfo)

        Public Function getResultantTextWithPosition() As List(Of TextRenderInfo)
            Return oInfoList
        End Function

        Private Sub RenderText(t As TextRenderInfo)
            If t.GetText().Trim().Length() = 0 Then Return

            oInfoList.Add(t)
        End Sub

        Public Sub EventOccurred(data As IEventData, type As EventType) Implements IEventListener.EventOccurred
            Dim t As TextRenderInfo = data
            t.PreserveGraphicsState()
            Call RenderText(t)
        End Sub

        Public Function GetSupportedEvents() As ICollection(Of EventType) Implements IEventListener.GetSupportedEvents
            Dim collection As New List(Of EventType)
            collection.Add(EventType.RENDER_TEXT)
            Return collection
        End Function
    End Class

    Public Sub Main()
        Dim sPdfFile as String = "CinéTéléRevue.2023-01-19.pdf"

        Using oReader As PdfReader = New PdfReader(sPdfFile)
            Using pdfDocument As PdfDocument = New PdfDocument(oReader)
                For pageNum As Integer = 1 To pdfDocument.GetNumberOfPages
                    Dim page As PdfPage = pdfDocument.GetPage(pageNum)
                    Call ExtractPage(page)
                Next pageNum
            End Using
        End Using
    End Sub

    Private Sub ExtractPage(page As PdfPage, sTitle As String)
        Dim sNewPage As String = sPdfFile.Replace(".pdf", "") & "." & sTitle & ".pdf"

        Using oWriter As New PdfWriter(sNewPage)
            Using pdfDocument As New PdfDocument(oWriter)
                pdfDocument.AddPage(page.CopyTo(pdfDocument))
                pdfDocument.Close()
            End Using
        End Using

        Dim listener As New XEventListener()
        Dim processor As New PdfCanvasProcessor(listener)
        processor.ProcessPageContent(page)
        Dim t1 As TextRenderInfo

        sNewPage = sPdfFile.Replace(".pdf", "") & "." & sTitle & ".info"
        Using sw As New StreamWriter(sNewPage)
            Dim result = listener.getResultantTextWithPosition()
            For Each t As TextRenderInfo In result
                Dim ascent As LineSegment = t.GetAscentLine()
                Dim descent As LineSegment = t.GetDescentLine()

                Dim initX As Single = descent.GetStartPoint().Get(0)
                Dim initY As Single = descent.GetStartPoint().Get(1)
                Dim endX As Single = ascent.GetEndPoint().Get(0)
                Dim endY As Single = ascent.GetEndPoint().Get(1)

                Dim rect As Rectangle = New Rectangle(initX, initY, endX - initX, endY - initY)

                Dim iLeft As Single = rect.GetLeft()
                Dim iRight As Single = rect.GetRight()
                Dim iTop As Single = rect.GetTop()
                Dim iBottom As Single = rect.GetBottom()

                Dim sLine As String = ""
                sLine &= "Left: " & iLeft.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Right: " & iRight.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Top: " & iTop.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Bottom: " & iBottom.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Text: [" & t.GetText() & "]"

                sw.WriteLine(sLine)
            Next
            sw.Close()
        End Using
    End Sub

End Module
解决方案

iText7内置的LocationTextExtractionStrategy可以原生处理文本拆分问题,它会根据文本的位置(同一行、相邻字符间距)自动合并片段。如果需要保留位置信息,可以继承该策略类来收集合并后的文本块及其位置数据,修改后的代码如下:

Imports System.IO
Imports iText.Kernel.Pdf
Imports iText.Kernel.Pdf.Canvas.Parser
Imports iText.Kernel.Pdf.Canvas.Parser.Listener
Imports iText.Kernel.Geom

Module MainModule

    ' 继承内置策略,同时收集文本块位置信息
    Public Class PositionAwareTextExtractionStrategy
        Inherits LocationTextExtractionStrategy

        Private ReadOnly _textBlocks As New List(Of TextBlock)

        Public ReadOnly Property TextBlocks As List(Of TextBlock)
            Get
                Return _textBlocks
            End Get
        End Property

        Protected Overrides Sub EventOccurred(data As IEventData, type As EventType)
            MyBase.EventOccurred(data, type)
            If type = EventType.RENDER_TEXT Then
                Dim renderInfo = CType(data, TextRenderInfo)
                Dim rect = renderInfo.GetDescentLine().GetBoundingRectangle()
                _textBlocks.Add(New TextBlock With {
                    .Text = renderInfo.GetText(),
                    .Left = rect.GetLeft(),
                    .Right = rect.GetRight(),
                    .Top = rect.GetTop(),
                    .Bottom = rect.GetBottom()
                })
            End If
        End Sub

        ' 自定义类存储文本块的文本与位置信息
        Public Class TextBlock
            Public Property Text As String
            Public Property Left As Single
            Public Property Right As Single
            Public Property Top As Single
            Public Property Bottom As Single
        End Class
    End Class

    Public Sub Main()
        Dim sPdfFile As String = "CinéTéléRevue.2023-01-19.pdf"

        Using oReader As PdfReader = New PdfReader(sPdfFile)
            Using pdfDocument As PdfDocument = New PdfDocument(oReader)
                For pageNum As Integer = 1 To pdfDocument.GetNumberOfPages
                    Dim page As PdfPage = pdfDocument.GetPage(pageNum)
                    ExtractPage(page, $"Page{pageNum}")
                Next pageNum
            End Using
        End Using
    End Sub

    Private Sub ExtractPage(page As PdfPage, sTitle As String)
        Dim sPdfFile As String = "CinéTéléRevue.2023-01-19.pdf"
        Dim sNewPage As String = sPdfFile.Replace(".pdf", "") & "." & sTitle & ".pdf"

        Using oWriter As New PdfWriter(sNewPage)
            Using pdfDocument As New PdfDocument(oWriter)
                pdfDocument.AddPage(page.CopyTo(pdfDocument))
            End Using
        End Using

        ' 使用自定义策略提取合并后的文本及位置
        Dim strategy As New PositionAwareTextExtractionStrategy()
        PdfTextExtractor.GetTextFromPage(page, strategy)

        sNewPage = sPdfFile.Replace(".pdf", "") & "." & sTitle & ".info"
        Using sw As New StreamWriter(sNewPage)
            For Each block In strategy.TextBlocks
                Dim sLine As String = ""
                sLine &= "Left: " & block.Left.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Right: " & block.Right.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Top: " & block.Top.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Bottom: " & block.Bottom.ToString("0.0000").PadLeft(10) & " /"
                sLine &= "Text: [" & block.Text & "]"

                sw.WriteLine(sLine)
            Next
        End Using
    End Sub

End Module

关键说明

  • LocationTextExtractionStrategy是iText7官方提供的文本提取策略,内置了文本合并逻辑,会自动识别并合并同一行内相邻的拆分文本片段。
  • 自定义的PositionAwareTextExtractionStrategy继承自该策略,既保留了原生的合并能力,又能收集每个文本块的位置信息,满足你提取文本及位置的需求。
  • 替换原有的PdfCanvasProcessor+自定义IEventListener的实现方式,改用PdfTextExtractor.GetTextFromPage结合自定义策略,简化代码的同时解决文本拆分问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:05:39