使用iTextSharp合并PDF时出现空白页插入及页面缺失问题排查
PDF合并工具空白页与页面缺失问题排查
问题描述
我是新手开发者Toru,日常使用C#开发。因业务需求,用PowerShell结合iTextSharp.dll开发了PDF合并工具。测试中发现合并后的PDF存在空白页插入问题:比如合并10页的AAA.pdf与10页的BBB.pdf后,AAA.pdf的第1页和第2页间插入了2-3页空白,还伴随页面缺失情况。尝试用相同PDF文件、增大合并后文件大小等方式复现问题,均未成功。
合并流程
- 获取Excel中填写的待合并文件路径数组
- 当Excel表格中
GrupeNo变量值变化时,进入合并分支 - 若输出PDF不存在则创建文件
- 检查数组非空则执行合并
- 引用相关DLL
- 若指定文件夹存在特定文件名,将其追加到输出PDF开头
- 获取输出PDF对象
- 获取待合并PDF对象并追加到输出PDF
- 释放待合并PDF对象
- 重复步骤8、9
PowerShell代码
#The 'if' statement for performing PDF merging if($GrupeNo -ne [int]$worksheet.Cells.Item($r, 5).Text) { #The variable [PdfName] holds the full path of the PDF to be created $PdfName = ($UserFullPath + "\"+ $StrFileName + [string]$GrupeNo + ".pdf") #If the output destination PDF does not exist, create a new output destination PDF file if((Test-Path $PdfName) -eq $false){ New-Item -path $UserFullPath -Name ($StrFileName + [string]$GrupeNo +".pdf") -ItemType "File" Start-Sleep -Milliseconds 3000 } #I am checking whether there is a path for the PDF files to be merged if ($ActivePdfFiles.Length -ne 0) { Set-ItemProperty -path $PdfName -name IsReadOnly -value $false $FSO = New-Object System.IO.FileStream($PdfName , [System.IO.FileMode]::OpenOrCreate) $BeforPassivePdfFile = New-Object iTextSharp.text.Document $PassivePdfFile = New-Object iTextSharp.text.pdf.PdfSmartCopy($BeforPassivePdfFile,$FSO) $BeforPassivePdfFile.Open() $refFirstPdfPath = Join-Path $Location "cover page" $FirstPdfPath = Join-Path $refFirstPdfPath ($StrFileName + [string]$GrupeNo +".pdf") #Processing of the cover page PDF file if(($PDFNum -eq 0) -and ((test-path $FirstPdfPath) -eq $true)) { $ActivePdfFile=New-Object iTextSharp.text.pdf.PdfReader($FirstPdfPath) $PassivePdfFile.AddDocument($ActivePdfFile); $ActivePdfFile.Close(); $PDFNum = 1 } #The iterative loop performing the PDF merging for ($i = $NumCheck ; $i -lt $ActivePdfFiles.Count; $i++) { if((test-path $ActivePdfFiles[$i]) -eq $false) { continue } $ActivePdfFile=New-Object iTextSharp.text.pdf.PdfReader($ActivePdfFiles[$i]) $PassivePdfFile.AddDocument($ActivePdfFile); $ActivePdfFile.Close(); } $NumCheck = $ActivePdfFiles.Length } #Releasing the objects if ($ActivePdfFile -ne $null) { $ActivePdfFile.dispose() } if ($PassivePdfFile -ne $null) { $PassivePdfFile.Dispose(); } if ($FSO -ne $null) { $fso.Dispose(); } if ($PhotoPdfFile -ne $null) { $PhotoPdfFile.Dispose(); } $GrupeNoCheck = $GrupeNo }
相关环境
- 使用的DLL:itextsharp.dll(5.5.13.3版本)、BouncyCastle.Crypto.dll
- 待合并PDF:1-20个,单文件400KB-15MB
- 输出PDF要求:大小必须小于10MB
排查方向与修复建议
1. 缺失Document对象关闭操作
iTextSharp要求必须调用Document.Close()才能确保所有页面内容写入输出文件,当前代码仅打开$BeforPassivePdfFile但未关闭,直接释放对象会导致文件内容不完整,引发空白页、页面缺失问题。
2. 空文件创建方式错误
用New-Item创建空文件后,直接用FileStream.OpenOrCreate结合PdfSmartCopy操作会有问题——空文件没有合法的PDF结构,PdfSmartCopy无法正确初始化。建议直接通过iTextSharp创建输出文档,替换原有的New-Item逻辑。
3. 对象释放顺序与资源泄漏
- 循环中仅调用
ActivePdfFile.Close()但未及时Dispose(),可能导致资源泄漏; - 对象释放顺序错误,应先关闭
Document,再依次释放PdfSmartCopy、FileStream,最后处理其他Reader对象。
4. 多余的等待逻辑
Start-Sleep -Milliseconds 3000完全没必要,New-Item是同步操作,执行完成后文件已创建完毕,多余等待无意义。
修改后的关键代码片段
if ($ActivePdfFiles.Length -ne 0) { Set-ItemProperty -path $PdfName -name IsReadOnly -value $false # 直接用iTextSharp创建输出文档,避免空文件问题 $FSO = New-Object System.IO.FileStream($PdfName , [System.IO.FileMode]::Create) $BeforPassivePdfFile = New-Object iTextSharp.text.Document $PassivePdfFile = New-Object iTextSharp.text.pdf.PdfSmartCopy($BeforPassivePdfFile,$FSO) $BeforPassivePdfFile.Open() $refFirstPdfPath = Join-Path $Location "cover page" $FirstPdfPath = Join-Path $refFirstPdfPath ($StrFileName + [string]$GrupeNo +".pdf") # 封面处理:及时释放Reader资源 if(($PDFNum -eq 0) -and (Test-Path $FirstPdfPath)) { $ActivePdfFile=New-Object iTextSharp.text.pdf.PdfReader($FirstPdfPath) $PassivePdfFile.AddDocument($ActivePdfFile); $ActivePdfFile.Close(); $ActivePdfFile.Dispose(); $PDFNum = 1 } # 合并循环:每次处理完就释放当前Reader for ($i = $NumCheck ; $i -lt $ActivePdfFiles.Count; $i++) { if(-not (Test-Path $ActivePdfFiles[$i])) { continue } $ActivePdfFile=New-Object iTextSharp.text.pdf.PdfReader($ActivePdfFiles[$i]) $PassivePdfFile.AddDocument($ActivePdfFile); $ActivePdfFile.Close(); $ActivePdfFile.Dispose(); } $NumCheck = $ActivePdfFiles.Length # 必须关闭Document,确保所有内容写入文件 $BeforPassivePdfFile.Close(); } # 调整对象释放顺序:先关闭Document,再释放相关对象 if ($BeforPassivePdfFile -ne $null) { if($BeforPassivePdfFile.IsOpen()){ $BeforPassivePdfFile.Close(); } $BeforPassivePdfFile.Dispose(); } if ($PassivePdfFile -ne $null) { $PassivePdfFile.Dispose(); } if ($FSO -ne $null) { $fso.Dispose(); } if ($PhotoPdfFile -ne $null) { $PhotoPdfFile.Dispose(); }
内容的提问来源于stack exchange,提问作者Toru
相关产品推荐
相关产品推荐

