PSWritePDF.psm1

class CustomSplitter : iText.Kernel.Utils.PdfSplitter {
    [int] $_order
    [string] $_destinationFolder
    [string] $_outputName
    CustomSplitter([iText.Kernel.Pdf.PdfDocument] $pdfDocument, [string] $destinationFolder, [string] $OutputName) : base($pdfDocument) {
        $this._destinationFolder = $destinationFolder
        $this._order = 0
        $this._outputName = $OutputName
    }
    [iText.Kernel.Pdf.PdfWriter] GetNextPdfWriter([iText.Kernel.Utils.PageRange] $documentPageRange) {
        $Name = -join ($this._outputName, $this._order++, ".pdf")
        $Path = [IO.Path]::Combine($this._destinationFolder, $Name)
        return [iText.Kernel.Pdf.PdfWriter]::new($Path)
    }
}
function Convert-PDFToText {
    [CmdletBinding()]
    param([string] $FilePath,
        [int[]] $Page)
    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        $Source = [iText.Kernel.Pdf.PdfReader]::new($FilePath)
        [iText.Kernel.Pdf.PdfDocument] $SourcePDF = [iText.Kernel.Pdf.PdfDocument]::new($Source)
        [iText.Kernel.Pdf.Canvas.Parser.Listener.LocationTextExtractionStrategy] $ExtractionStrategy = [iText.Kernel.Pdf.Canvas.Parser.Listener.LocationTextExtractionStrategy]::new()
        $PagesCount = $SourcePDF.GetNumberOfPages()
        if ($Page.Count -eq 0) {
            for ($Count = 1; $Count -le $PagesCount; $Count++) {
                $ExtractedPage = $SourcePDF.GetPage($Count)
                [iText.Kernel.Pdf.Canvas.Parser.PdfTextExtractor]::GetTextFromPage($ExtractedPage, $ExtractionStrategy)
            }
        } else {
            foreach ($Count in $Page) {
                if ($Count -le $PagesCount -and $Count -gt 0) {
                    $ExtractedPage = $SourcePDF.GetPage($Count)
                    [iText.Kernel.Pdf.Canvas.Parser.PdfTextExtractor]::GetTextFromPage($ExtractedPage, $ExtractionStrategy)
                } else { Write-Warning "Convert-PDFToText - File $FilePath doesn't contain page number $Count. Skipping." }
            }
        }
    } else { Write-Warning "Convert-PDFToText - Path $FilePath doesn't exists. Terminating." }
}
function Merge-PDF {
    [CmdletBinding()]
    param([string[]] $InputFile,
        [string] $OutputFile)
    if ($OutputFile) {
        [iText.Kernel.Pdf.PdfWriter] $Writer = [iText.Kernel.Pdf.PdfWriter]::new($OutputFile)
        [iText.Kernel.Pdf.PdfDocument] $PDF = [iText.Kernel.Pdf.PdfDocument]::new($Writer)
        [iText.Kernel.Utils.PdfMerger] $Merger = [iText.Kernel.Utils.PdfMerger]::new($PDF)
        foreach ($File in $InputFile) {
            if ($File -and (Test-Path -LiteralPath $File)) {
                try {
                    $Source = [iText.Kernel.Pdf.PdfReader]::new($File)
                    [iText.Kernel.Pdf.PdfDocument] $SourcePDF = [iText.Kernel.Pdf.PdfDocument]::new($Source)
                    $null = $Merger.merge($SourcePDF, 1, $SourcePDF.getNumberOfPages())
                    $SourcePDF.close()
                } catch {
                    $ErrorMessage = $_.Exception.Message
                    Write-Warning "Merge-PDF - Processing document $File failed with error: $ErrorMessage"
                }
            }
        }
        try { $PDF.Close() } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Warning "Merge-PDF - Saving document $OutputFile failed with error: $ErrorMessage"
        }
    } else { Write-Warning "Merge-PDF - Output file was empty. Please give a name to file. Terminating." }
}
function New-InternalPDF {
    [CmdletBinding()]
    param([string] $FilePath)
    $Script:Writer = [iText.Kernel.Pdf.PdfWriter]::new($FilePath)
    $Script:PDF = [iText.Kernel.Pdf.PdfDocument]::new($writer)
}
function New-InternalPDFList {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Settings)
    $List = [iText.Layout.Element.List]::new()
    if ($null -ne $Settings.Indent) { $null = $List.SetSymbolIndent($Settings.Indent) }
    if ($Settings.Symbol) {
        if ($Settings.Symbol -eq 'hyphen') { } elseif ($Settings.Symbol -eq 'bullet') {
            $Symbol = [regex]::Unescape("\u2022")
            $null = $List.SetListSymbol($Symbol)
        }
    }
    foreach ($_ in $Settings.Items) { $null = $List.Add([iText.Layout.Element.ListItem]::new($_.Text)) }
    $null = $Script:Document.Add($List)
}
function New-InternalPDFOptions {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Settings)
    if ($Settings.Margins) {
        if ($Settings.Margins.Left) { $Script:Document.SetLeftMargin($Settings.Margins.Left) }
        if ($Settings.Margins.Right) { $Script:Document.SetRightMargin($Settings.Margins.Right) }
        if ($Settings.Margins.Top) { $Script:Document.SetTopMargin($Settings.Margins.Top) }
        if ($Settings.Margins.Bottom) { $Script:Document.SetBottomMargin($Settings.Margins.Bottom) }
    }
}
function New-InternalPDFPage {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Settings)
    if ($Settings.PageSize -or $Settings.Rotate) {
        if ($Settings.PageSize) { $PageSize = [iText.Kernel.Geom.PageSize]::($Settings.PageSize) }
        if ($Settings.Rotate) {
            if ($PageSize) { $PageSize = $PageSize.Rotate() } else {
                $PageSize = [iText.Kernel.Geom.PageSize]::A4
                $PageSize = $PageSize.Rotate()
            }
        }
    }
    if ($PageSize) { $null = $Script:PDF.AddNewPage($PageSize) } else { $null = $Script:PDF.AddNewPage() }
    $Script:Document = [iText.Layout.Document]::new($Script:PDF)
}
function New-InternalPDFText {
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Settings)
    $Paragraph = [iText.Layout.Element.Paragraph]::new()
    foreach ($_ in $Settings) {
        if ($_.Font) {
            $Font = [iText.IO.Font.Constants.StandardFonts]::($_.Font)
            $ApplyFont = [iText.Kernel.Font.PdfFontFactory]::CreateFont($Font)
            $null = $Paragraph.Add($_.Text).SetFont($ApplyFont)
        } else { $null = $Paragraph.Add($_.Text) }
    }
    $null = $Script:Document.add($Paragraph)
}
function New-PDF {
    [CmdletBinding()]
    param([scriptblock] $PDFContent,
        [string] $FilePath,
        [nullable[float]] $MarginLeft,
        [nullable[float]] $MarginRight,
        [nullable[float]] $MarginTop,
        [nullable[float]] $MarginBottom,
        [ValidateScript( { & $Script:PDFPageSizeValidation })][string] $PageSize,
        [switch] $Rotate,
        [alias('Open')][switch] $Show)
    New-InternalPDF -FilePath $FilePath
    $FirstPage = New-PDFPage -MarginTop $MarginTop -MarginBottom $MarginBottom -MarginLeft $MarginLeft -MarginRight $MarginRight -PageSize $PageSize -Rotate:$Rotate.IsPresent
    [Array] $Output = @($FirstPage
        & $PDFContent)
    foreach ($Element in $Output) {
        if ($Element.Type -eq 'Page') { New-InternalPDFPage -Settings $Element.Settings }
        if ($Element.Type -eq 'Text') { New-InternalPDFText -Settings $Element.Settings }
        if ($Element.Type -eq 'List') { New-InternalPDFList -Settings $Element.Settings }
        if ($Element.Type -eq 'Paragraph') { }
        if ($Element.Type -eq 'Options') { New-InternalPDFOptions -Settings $Element.Settings }
    }
    $Script:PDF.Close()
    if ($Show) { if (Test-Path -LiteralPath $FilePath) { Invoke-Item -LiteralPath $FilePath } }
}
Register-ArgumentCompleter -CommandName New-PDF -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
function New-PDFList {
    [CmdletBinding()]
    param([ScriptBlock] $ListItems,
        [nullable[float]] $Indent,
        [ValidateSet('bullet', 'hyphen')][string] $Symbol = 'hyphen')
    $Output = & $ListItems
    $Items = foreach ($_ in $Output) { if ($_.Type -eq 'ListItem') { $_.Settings } }
    [PSCustomObject] @{Type = 'List'
        Settings = @{Items = $Items
            Indent = $Indent
            Symbol = $Symbol
        }
    }
}
function New-PDFListItem {
    [CmdletBinding()]
    param([string] $Text)
    [PSCustomObject] @{Type = 'ListItem'
        Settings = @{Text = $Text }
    }
}
function New-PDFOptions {
    [CmdletBinding()]
    param([nullable[float]] $MarginLeft,
        [nullable[float]] $MarginRight,
        [nullable[float]] $MarginTop,
        [nullable[float]] $MarginBottom,
        [ValidateScript( { & $Script:PDFPageSizeValidation })][string] $PageSize)
    [PSCustomObject] @{Type = 'Options'
        Settings = @{Margins = @{Left = $MarginLeft
                Right = $MarginRight
                Top = $MarginTop
                Bottom = $MarginBottom
            }
            PageSize = $PageSize
        }
    }
}
Register-ArgumentCompleter -CommandName New-PDFOptions -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
function New-PDFPage {
    [CmdletBinding()]
    param([ScriptBlock] $PageContent,
        [nullable[float]] $MarginLeft,
        [nullable[float]] $MarginRight,
        [nullable[float]] $MarginTop,
        [nullable[float]] $MarginBottom,
        [ValidateScript( { & $Script:PDFPageSizeValidation })][string] $PageSize,
        [switch] $Rotate)
    $Page = [PSCustomObject] @{Type = 'Page'
        Settings = @{Margins = @{Left = $MarginLeft
                Right = $MarginRight
                Top = $MarginTop
                Bottom = $MarginBottom
            }
            PageSize = $PageSize
            Rotate = $Rotate.IsPresent
        }
    }
    $Page
}
Register-ArgumentCompleter -CommandName New-PDFPage -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
function New-PDFText {
    [CmdletBinding()]
    param([string[]] $Text,
        [ValidateScript( { & $Script:PDFFontValidation })][string] $Font)
    [PSCustomObject] @{Type = 'Text'
        Settings = @{Text = $Text
            Font = $Font
        }
    }
}
Register-ArgumentCompleter -CommandName New-PDFText -ParameterName Font -ScriptBlock $Script:PDFFont
function Split-PDF {
    [CmdletBinding()]
    param([Parameter(Mandatory)][string] $FilePath,
        [Parameter(Mandatory)][string] $OutputFolder,
        [string] $OutputName = 'OutputDocument',
        [int] $SplitCount = 1)
    if ($SplitCount -eq 0) {
        Write-Warning "Split-PDF - SplitCount is 0. Terminating."
        return
    }
    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        if ($OutputFolder -and (Test-Path -LiteralPath $OutputFolder)) {
            try {
                $PDFFile = [iText.Kernel.Pdf.PdfReader]::new($FilePath)
                $Document = [iText.Kernel.Pdf.PdfDocument]::new($PDFFile)
                $Splitter = [CustomSplitter]::new($Document, $OutputFolder, $OutputName)
                $List = $Splitter.SplitByPageCount($SplitCount)
                foreach ($_ in $List) { $_.Close() }
            } catch {
                $ErrorMessage = $_.Exception.Message
                Write-Warning "Split-PDF - Error has occured: $ErrorMessage"
            }
        } else { Write-Warning "Split-PDF - Destination folder $OutputFolder doesn't exists. Terminating." }
    } else { Write-Warning "Split-PDF - Path $FilePath doesn't exists. Terminating." }
}
$Script:PDFPageSize = { ([iText.Kernel.Geom.PageSize] | Get-Member -static -MemberType Property).Name }
$Script:PDFPageSizeValidation = { $Array = @((& $Script:PDFPageSize)
        '')
    $_ -in $Array }
$Script:PDFFont = { ([iText.IO.Font.Constants.StandardFonts] | Get-Member -static -MemberType Property).Name }
$Script:PDFFontValidation = { $Array = @((& $Script:PDFFont)
        '')
    $_ -in $Array }
if ($PSEdition -eq 'Core') {
    Add-Type -Path $PSScriptRoot\Lib\Core\BouncyCastle.Crypto.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\Common.Logging.Core.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\Common.Logging.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.barcodes.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.forms.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.hyph.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.io.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.kernel.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.layout.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.pdfa.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.sign.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.styledxmlparser.dll
    Add-Type -Path $PSScriptRoot\Lib\Core\itext.svg.dll
} else {
    Add-Type -Path $PSScriptRoot\Lib\Default\BouncyCastle.Crypto.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\Common.Logging.Core.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\Common.Logging.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.barcodes.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.forms.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.hyph.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.io.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.kernel.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.layout.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.pdfa.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.sign.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.styledxmlparser.dll
    Add-Type -Path $PSScriptRoot\Lib\Default\itext.svg.dll
}
Export-ModuleMember -Function @('Convert-PDFToText', 'Merge-PDF', 'New-PDF', 'New-PDFList', 'New-PDFListItem', 'New-PDFOptions', 'New-PDFPage', 'New-PDFText', 'Split-PDF') -Alias @()