如何使用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
相关产品推荐
相关产品推荐

