在Powershell中使用itextsharp从PDF提取页面

时间:2018-10-11 14:27:44

标签: powershell pdf itext

我已经对此进行了数周的研究,似乎在这个问题上没有太大的进展。我有一个很大的PDF(超过900页),这是邮件合并的结果。结果是同一文档的900多个副本(一页),唯一的不同是在底部有一个人的名字。我想做的是让Powershell脚本使用itextsharp读取文档,并将包含特定字符串(人名)的页面保存到各自的文件夹中。

这是我到目前为止所管理的。

Add-Type -Path C:\scripts\itextsharp.dll

$reader = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList 
"$pwd\downloads\TMs.pdf"
for($page = 1; $page -le $reader.NumberOfPages; $page++) {


    $pageText = [iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($reader,$page).Split([char]0x000A)

    if($PageText -match 'DAN KAGAN'){
    Write-Host "DAN FOUND"
    }
    }

如您所见,我现在仅使用一个名称进行测试。该脚本会正确找到该名称10次。我似乎找不到任何信息,是如何提取出现此字符串的页面。

我希望这很清楚。如果需要帮助,请告诉我。

谢谢!

1 个答案:

答案 0 :(得分:4)

我实际上刚写完一个非常相似的脚本。使用我的脚本,我需要扫描报告卡的PDF,找到学生的姓名和ID号,然后提取该页面并适当命名。但是,每个报告卡可以跨越多个页面。

您似乎正在使用iTextSharp 5,这很好,因为我也是如此。iTextSharp7的语法有很大不同,我还没有学过。

以下是进行页面提取的逻辑:

    $Document = [iTextSharp.text.Document]::new($PdfReader.GetPageSizeWithRotation($StartPage))
    $TargetMemoryStream = [System.IO.MemoryStream]::new()
    $PdfCopy = [iTextSharp.text.pdf.PdfSmartCopy]::new($Document, $TargetMemoryStream)

    $Document.Open()
    foreach ($Page in $StartPage..$EndPage) {
        $PdfCopy.AddPage($PdfCopy.GetImportedPage($PdfReader, $Page));
    }
    $Document.Close()

    $NewFileName = 'Elementary Student Record - {0}.pdf' -f $Current.Student_Id
    $NewFileFullName = [System.IO.Path]::Combine($OutputFolder, $NewFileName)
    [System.IO.File]::WriteAllBytes($NewFileFullName, $TargetMemoryStream.ToArray())

这是完整的工作脚本。我删除了尽可能少的内容,为您提供了一个可行的示例:

Import-Module -Name SqlServer -Cmdlet Invoke-Sqlcmd
Add-Type -Path 'C:\...\itextsharp.dll'

# Get table of valid student IDs
$ServerInstance = '...'
$Database = '...'
$Query = @'
select student_id, student_name from student
'@
$ValidStudents = @{}
Invoke-Sqlcmd -Query $Query -ServerInstance $ServerInstance -Database $Database -OutputAs DataRows | ForEach-Object {
    [void]$ValidStudents.Add($_.student_id.trim(), $_.student_name)
}

$PdfFiles = Get-ChildItem "G:\....\*.pdf" -File |
    Select-Object -ExpandProperty FullName
$OutputFolder = 'G:\...'

$StudentIDSearchPattern = '(?mn)^(?<Student_Id>\d{6,7}) - (?<Student_Name>.*)$'
foreach ($PdfFile in $PdfFiles) {
    $PdfReader = [iTextSharp.text.pdf.PdfReader]::new($PdfFile)

    $StudentStack = [System.Collections.Stack]::new()

    # Map out the PDF file.
    foreach ($Page in 1..($PdfReader.NumberOfPages)) {
        [iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($PdfReader, $Page) |
            Where-Object { $_ -match $StudentIDSearchPattern } |
            ForEach-Object {
            $StudentStack.Push([PSCustomObject]@{
                    Student_Id   = $Matches['Student_Id']
                    Student_Name = $Matches['Student_Name']
                    StartPage    = $Page
                    IsValid      = $ValidStudents.ContainsKey($Matches['Student_Id'])
                })
        }
    }

    # Extract the pages and save the files
    $LastPage = $PdfReader.NumberOfPages
    while ($StudentStack.Count -gt 0) {
        $Current = $StudentStack.Pop()

        $StartPage = $Current.StartPage
        $EndPage = $LastPage

        $Document = [iTextSharp.text.Document]::new($PdfReader.GetPageSizeWithRotation($StartPage))
        $TargetMemoryStream = [System.IO.MemoryStream]::new()
        $PdfCopy = [iTextSharp.text.pdf.PdfSmartCopy]::new($Document, $TargetMemoryStream)

        $Document.Open()
        foreach ($Page in $StartPage..$EndPage) {
            $PdfCopy.AddPage($PdfCopy.GetImportedPage($PdfReader, $Page));
        }
        $Document.Close()

        $NewFileName = 'Elementary Student Record - {0}.pdf' -f $Current.Student_Id
        $NewFileFullName = [System.IO.Path]::Combine($OutputFolder, $NewFileName)
        [System.IO.File]::WriteAllBytes($NewFileFullName, $TargetMemoryStream.ToArray())

        $LastPage = $Current.StartPage - 1
    }
}

在我的测试环境中,这将在15秒钟内处理5个源PDF中的大约500名学生。

我倾向于使用构造函数而不是New-Object,但是它们之间并没有真正的区别。我只是觉得它们更容易阅读。