Powershell: utilizzare iText Core per la gestione di file PDF tramite script
Sviluppare uno script PowerShell per generare o manipolare file PDF può rivelarsi estremamente utile in vari contesti aziendali come ad esempio:
- l’automazione di processi ripetitivi di gestione dei file PDF (creazione, modifica, unione, divisione) al fine di ridurre il tempo necessario per eseguire tali attività e aumentare la precisione;
- la generazione di report;
- la personalizzazione di file PDF come l’aggiunta di watermark o l’estrazione di testo o immagini.
Per generare o manipolare manipolare file PDF è possibile utilizzare ad esempio iText Core, una libreria per la creazione, manipolazione e gestione dei PDF in .NET. iText è stato sviluppato nel 2000 da Bruno Lowagie come libreria Java per automatizzare la creazione di PDF. In seguito è cresciuto notevolmente, espandendosi al mondo .NET attraverso iTextSharp e successivamente unificando le piattaforme con iText Core. Attualmente iText ha raggiunto la versione e la 9 e consente:
- la creazione e generazione di PDF;
- la manipolazione di PDF esistenti;
- la creazione e modifica di moduli AcroForms e XFA e l’automazione della compilazione e gestione dei dati utente;
- la crittografia e decrittografia dei PDF, la protezione tramite password e il supporto per firme digitali e timestamp;
- la generazione di codici a barre 1D e 2D, inclusi QR Code e Data Matrix;
- il supporto per PDF/A (archiviazione a lungo termine), PDF/UA (accessibilità) e PDF/X (stampa professionale).
iText Core è distribuito con un modello a doppia licenza:
- AGPL: Per progetti open source;
- Commerciale: per applicazioni chiuse o commerciali, con supporto e funzionalità avanzate.
Per poter utilizzare iText in uno script PowerShell è necessaria scaricare la libreria itext ad esempio da NuGet al seguente link NuGet Gallery | itext 9.0.0 e quindi le varie dipendenze ovvero:
- NuGet Gallery | itext.commons 9.0.0
- NuGet Gallery | BouncyCastle.Cryptography 2.0.0
- NuGet Gallery | Microsoft.Extensions.Logging 5.0.0
- NuGet Gallery | Microsoft.Extensions.Options 5.0.0
- NuGet Gallery | Microsoft.Extensions.Logging.Abstractions 5.0.0
- NuGet Gallery | System.Memory 4.5.5
Per capire quali versioni delle librerie occorrono si può utilizzare il tool dotPeek per analizzare le references delle dll che sarà necessario usare per implementare le funzionalità di gestione di file PDF all’interno dello script PowerShell.
In alternativa è possibile gestire le eccezioni nello script, best practice comunque consigliabile, per ricostruire la catena delle dipendenze.
Di seguito il frammento di codice da inserire in testa allo script per importare le dll necessarie per la creazione di un semplice PDF:
Try{
Add-Type -Path $PSScriptRoot\Lib\4.5.5\net461\System.Memory.dll
Add-Type -Path $PSScriptRoot\Lib\5.0.0\net461\Microsoft.Extensions.Logging.Abstractions.dll
Add-Type -Path $PSScriptRoot\Lib\5.0.0\net461\Microsoft.Extensions.Logging.dll
Add-Type -Path $PSScriptRoot\Lib\5.0.0\net461\Microsoft.Extensions.Options.dll
Add-Type -Path $PSScriptRoot\Lib\9.0.0\net461\itext.commons.dll
Add-Type -Path $PSScriptRoot\Lib\9.0.0\net461\itext.kernel.dll
Add-Type -Path $PSScriptRoot\Lib\9.0.0\net461\itext.layout.dll
Add-Type -Path $PSScriptRoot\Lib\2.0.0\net461\BouncyCastle.Cryptography.dll
Write-Host “Dll caricate correttamente!”
}
Catch{
Write-Host $_.Exception.MessageIf( $_.Exception.LoaderExceptions) { Write-Host $_.Exception.LoaderExceptions.Message }
Exit
}
Nel frammento di codice si è ipotizzato di copiare le dll necessarie in una sotto cartella Lib della cartella in cui è contenuto lo script, inoltre nella cartella Lib le dll sono state copiate mantenendo le sottocartelle che fanno riferimento alla loro versione ed edizione per maggior comprensione.
Di seguito invece un frammento di codice per generare un semplice PDF con un titolo e un paragrafo.
Try{
# Crea un documento PDF
$pdfWriter = New-Object iText.Kernel.Pdf.PdfWriter(“$PSScriptRoot\output.pdf”)
$pdfDocument = New-Object iText.Kernel.Pdf.PdfDocument($pdfWriter)
$document = New-Object iText.Layout.Document($pdfDocument)# Carica il font per il grassetto
$PdfFontFactory = [iText.Kernel.Font.PdfFontFactory] $boldFont = $PdfFontFactory::CreateFont(“Helvetica-Bold”) # Usa un font di sistema compatibile# Crea un oggetto Text con il font bold e dimensione 18
$titleText = New-Object iText.Layout.Element.Text “Titolo del Documento”
$titleText.SetFont($boldFont)
$titleText.SetFontSize(18)# Aggiungi il testo al paragrafo e poi al documento
$titleParagraph = New-Object iText.Layout.Element.Paragraph
$titleParagraph.Add($titleText)
$Document.Add($titleParagraph)# Aggiungi un paragrafo al documento
$paragraph = New-Object iText.Layout.Element.Paragraph(“Paragrafo del documento.”)
$document.Add($paragraph)# Chiude il documento
$document.Close()Write-Output “PDF creato con successo!”
}Catch{
$line = $_.InvocationInfo.ScriptLineNumber
Write-Host “Errore nella riga $line.”Write-Host $_.Exception.Message
$e = $_.Exception
While ($e.InnerException) {
$e = $e.InnerException
Write-Host $e.Message
}
}
Anche in questo caso particolare cura è stata posta nella gestione delle eccezioni per comprendere in modo semplice e preciso l’origine di eventuali errori.
Ipotizzando che lo script venga memorizzato nel file Create-TestPDF.ps1 sarà possibile eseguire lo script creato nella cartella dello script un file batch denominato Create-TestPDF.cmd con il seguente comando:
powershell -ExecutionPolicy RemoteSigned -Command %~dp0%~n0.ps1
One or more dependency seems to be missing:
$pdf = [iText.Kernel.Pdf.PdfDocument]::new($reader)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception calling “.ctor” with “1” argument(s): “The type initializer for ‘iText.Commons.Actions.EventManager’ threw
| an exception.”