PSWritePDF.psm1

function Remove-EmptyValues { 
    [CmdletBinding()]
    param([System.Collections.IDictionary] $Hashtable,
        [switch] $Recursive,
        [int] $Rerun)
    foreach ($_ in [string[]] $Hashtable.Keys) { if ($Recursive) { if ($Hashtable[$_] -is [System.Collections.IDictionary]) { if ($Hashtable[$_].Count -eq 0) { $Hashtable.Remove($_) } else { Remove-EmptyValues -Hashtable $Hashtable[$_] -Recursive:$Recursive } } else { if ($null -eq $Hashtable[$_]) { $Hashtable.Remove($_) } elseif ($Hashtable[$_] -is [string] -and $Hashtable[$_] -eq '') { $Hashtable.Remove($_) } } } else { if ($null -eq $Hashtable[$_]) { $Hashtable.Remove($_) } elseif ($Hashtable[$_] -is [string] -and $Hashtable[$_] -eq '') { $Hashtable.Remove($_) } } }
    if ($Rerun) { for ($i = 0; $i -lt $Rerun; $i++) { Remove-EmptyValues -Hashtable $Hashtable -Recursive:$Recursive } }
}
function Add-PDFDocumentContent {
    [CmdletBinding()]
    param(
        [object] $Object
    )
    if ($Script:Document) {
        $null = $Script:Document.Add($Object)
    } else {
        $Splat = $Script:PDFStart.DocumentSettings
        $Settings = New-PDFPage @Splat #-MarginTop $MarginTop -MarginBottom $MarginBottom -MarginLeft $MarginLeft -MarginRight $MarginRight -PageSize $PageSize -Rotate:$Rotate.IsPresent
        New-InternalPDFPage -PageSize $Settings.Settings.PageSize -Rotate:$Settings.Settings.Rotate
        $null = $Script:Document.Add($Object)
    }
}
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 Initialize-PDF {
    [CmdletBinding()]
    param(
        [Array] $Elements
    )
    foreach ($Element in $Elements) {
        $Splat = $Element.Settings
        Remove-EmptyValues -Hashtable $Splat
        if ($Element.Type -eq 'Page') {
            if (-not $Script:PDFStart.FirstPageUsed) {
                #$Area = New-PDFArea -PageSize $Element.Settings.PageSize -Rotate:$Element.Settings.Rotate -AreaType ([iText.Layout.Properties.AreaBreakType]::LAST_PAGE)
                New-InternalPDFPage -PageSize $Element.Settings.PageSize -Rotate:$Element.Settings.Rotate

                #Add-PDFDocumentContent -Object $Area
                $Script:PDFStart.FirstPageUsed = $true
            } else {
                $Area = New-PDFArea -PageSize $Element.Settings.PageSize -Rotate:$Element.Settings.Rotate
                Add-PDFDocumentContent -Object $Area
            }
            # New-InternalPDFPage -PageSize $Element.Settings.PageSize -Rotate:$Element.Settings.Rotate
            # New-InternalPDFOptions -Settings $Element.Settings
            if ($Element.Settings.PageContent) {
                Initialize-PDF -Elements $Element.Settings.PageContent
            }
        } elseif ($Element.Type -eq 'Text') {
            $Paragraph = New-InternalPDFText @Splat
            foreach ($P in $Paragraph) {
                #$null = $Script:Document.Add($P)
                Add-PDFDocumentContent -Object $P
            }
        } elseif ($Element.Type -eq 'List') {
            New-InternalPDFList -Settings $Element.Settings
        } elseif ($Element.Type -eq 'Paragraph') {

        } elseif ($Element.Type -eq 'Options') {
            $SplatMargins = $Splat.Margins
            New-InternalPDFOptions @SplatMargins
        } elseif ($Element.Type -eq 'Table') {
            $Table = New-InteralPDFTable @Splat
            #$null = $Script:Document.Add($Table)
            Add-PDFDocumentContent -Object $Table
        }
    }
}
function New-InternalPDF {
    [CmdletBinding()]
    param(
        [string] $FilePath,
        [string] $Version #,
        # [ValidateScript( { & $Script:PDFPageSizeValidation } )][string] $PageSize,
        # [switch] $Rotate
    )

    if ($Version) {
        $PDFVersion = Get-PDFConstantVersion -Version $Version
        $WriterProperties = [iText.Kernel.Pdf.WriterProperties]::new()
        $null = $WriterProperties.SetPdfVersion($PDFVersion)
    }

    try {
        if ($Version) {
            $Script:Writer = [iText.Kernel.Pdf.PdfWriter]::new($FilePath, $WriterProperties)
        } else {
            $Script:Writer = [iText.Kernel.Pdf.PdfWriter]::new($FilePath)
        }
    } catch {
        if ($_.Exception.Message -like '*The process cannot access the file*because it is being used by another process.*') {
            Write-Warning "New-InternalPDF - File $FilePath is in use. Terminating."
            Exit
        } else {
            Write-Warning "New-InternalPDF - Terminating error: $($_.Exception.Message)"
            Exit
        }
    }

    if ($Script:Writer) {
        $PDF = [iText.Kernel.Pdf.PdfDocument]::new($Script:Writer)
    } else {
        Write-Warning "New-InternalPDF - Terminating as writer doesn't exists."
        Exit
    }
    return $PDF
}



<#
function New-InternalPDF {
    param(
 
    )
    if ($Script:Writer) {
        if ($Script:PDF) {
            #$Script:PDF.Close()
        }
        $Script:PDF = [iText.Kernel.Pdf.PdfDocument]::new($Script:Writer)
    } else {
        Write-Warning "New-InternalPDF - Terminating as writer doesn't exists."
        Exit
    }
}
#>

Register-ArgumentCompleter -CommandName New-InternalPDF -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
Register-ArgumentCompleter -CommandName New-InternalPDF -ParameterName Version -ScriptBlock $Script:PDFVersion
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') {
            # Default
        } 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(
        [nullable[float]] $MarginLeft,
        [nullable[float]] $MarginRight,
        [nullable[float]] $MarginTop,
        [nullable[float]] $MarginBottom
    )

    if ($Script:Document) {
        if ($MarginLeft) {
            $Script:Document.SetLeftMargin($MarginLeft)
        }
        if ($MarginRight) {
            $Script:Document.SetRightMargin($MarginRight)
        }
        if ($MarginTop) {
            $Script:Document.SetTopMargin($MarginTop)
        }
        if ($MarginBottom) {
            $Script:Document.SetBottomMargin($MarginBottom)
        }
    }

    #if ($Settings.PageSize) {
    # $Script:Document.GetPdfDocument().SetDefaultPageSize([iText.Kernel.Geom.PageSize]::($Settings.PageSize))
    # }
}
function New-InternalPDFPage {
    [CmdletBinding()]
    param(
        [string] $PageSize,
        [switch] $Rotate
    )

    if ($PageSize -or $Rotate) {
        if ($PageSize) {
            $Page = [iText.Kernel.Geom.PageSize]::($PageSize)
        } else {
            $Page = [iText.Kernel.Geom.PageSize]::Default
        }
        if ($Rotate) {
            $Page = $Page.Rotate()
        }
    }
    if ($Page) {
        $null = $Script:PDF.AddNewPage($Page)
    } else {
        $null = $Script:PDF.AddNewPage()
    }
    $Script:Document = [iText.Layout.Document]::new($Script:PDF)
}

Register-ArgumentCompleter -CommandName New-InternalPDFPage -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
function New-InteralPDFTable {
    [CmdletBinding()]
    param(
        [Array] $DataTable
    )

    if ($DataTable[0] -is [System.Collections.IDictionary]) {
        [Array] $ColumnNames = 'Name', 'Value' # $DataTable[0].Keys
        [Array] $TemporaryTable = foreach ($_ in $DataTable2) {
            $_.GetEnumerator() | Select-Object Name, Value
        }
    } else {
        [Array] $ColumnNames = $DataTable[0].PSObject.Properties.Name
        [Array] $TemporaryTable = $DataTable
    }

    [iText.layout.element.Table] $Table = [iText.Layout.Element.Table]::new($ColumnNames.Count)
    $Table = $Table.UseAllAvailableWidth()

    foreach ($Column in $ColumnNames) {
        $Splat = @{
            Text = $Column
            #Font = $Font
            #FontFamily = $FontFamily
            #FontColor = $FontColor
            #FontBold = $FontBold
        }
        $Paragraph = New-InternalPDFText @Splat
        #$Paragraph = New-PDFText -Text $Column
        [iText.Layout.Element.Cell] $Cell = [iText.Layout.Element.Cell]::new().Add($Paragraph)
        $null = $Table.AddCell($Cell)
    }
    foreach ($_ in $TemporaryTable) {
        foreach ($Column in $ColumnNames) {
            $Splat = @{
                Text = $_.$Column
                #Font = $Font
                #FontFamily = $FontFamily
                #FontColor = $FontColor
                #FontBold = $FontBold
            }
            $Paragraph = New-InternalPDFText @Splat
            #$Paragraph = New-PDFText -Text $_.$Column
            [iText.Layout.Element.Cell] $Cell = [iText.Layout.Element.Cell]::new().Add($Paragraph)
            $null = $Table.AddCell($Cell)
        }
    }
    $Table
}
function New-InternalPDFText {
    [CmdletBinding()]
    param(
        [string[]] $Text,
        [ValidateScript( { & $Script:PDFFontValidation } )][string[]] $Font,
        #[string[]] $FontFamily,
        [ValidateScript( { & $Script:PDFColorValidation } )][string[]] $FontColor,
        [bool[]] $FontBold
    )

    $Paragraph = [iText.Layout.Element.Paragraph]::new()

    if ($FontBold) {
        [Array] $FontBold = $FontBold
        $DefaultBold = $FontBold[0]
    }
    if ($FontColor) {
        [Array] $FontColor = $FontColor
        $DefaultColor = $FontColor[0]
    }
    if ($Font) {
        [Array] $Font = $Font
        $DefaultFont = $Font[0]
    }

    for ($i = 0; $i -lt $Text.Count; $i++) {
        [iText.Layout.Element.Text] $PDFText = $Text[$i]

        if ($FontBold) {
            if ($null -ne $FontBold[$i]) {
                if ($FontBold[$i]) {
                    $PDFText = $PDFText.SetBold()
                }
            } else {
                if ($DefaultBold) {
                    $PDFText = $PDFText.SetBold()
                }
            }
        }
        if ($FontColor) {
            if ($null -ne $FontColor[$i]) {
                if ($FontColor[$i]) {
                    $ConvertedColor = Get-PDFConstantColor -Color $FontColor[$i]
                    $PDFText = $PDFText.SetFontColor($ConvertedColor)
                }
            } else {
                if ($DefaultColor) {
                    $ConvertedColor = Get-PDFConstantColor -Color $DefaultColor
                    $PDFText = $PDFText.SetFontColor($ConvertedColor)
                }
            }
        }
        if ($Font) {
            if ($null -ne $Font[$i]) {
                if ($Font[$i]) {
                    $ConvertedFont = Get-PDFConstantFont -Font $Font[$i]
                    $ApplyFont = [iText.Kernel.Font.PdfFontFactory]::CreateFont($ConvertedFont)
                    $PDFText = $PDFText.SetFont($ApplyFont)
                }
            } else {
                if ($DefaultColor) {
                    $ConvertedFont = Get-PDFConstantFont -Font $DefaultFont
                    $ApplyFont = [iText.Kernel.Font.PdfFontFactory]::CreateFont($ConvertedFont)
                    $PDFText = $PDFText.SetFont($ApplyFont)
                }
            }
        }
        $null = $Paragraph.Add($PDFText)
    }
    $Paragraph
}
# Validation for Actions
$Script:PDFAction = {
    ([iText.Kernel.Pdf.Action.PdfAction] | Get-Member -static -MemberType Property).Name
}
$Script:PDFActionValidation = {
    $Array = @(
        (& $Script:PDFAction)
        ''
    )
    $_ -in $Array
}

function Get-PDFConstantAction {
    [CmdletBinding()]
    param(
        [ValidateScript( { & $Script:PDFActionValidation } )][string] $Action
    )
    return [iText.Kernel.Pdf.Action.PdfAction]::$Action
}

Register-ArgumentCompleter -CommandName Get-PDFConstantAction -ParameterName Action -ScriptBlock $Script:PDFAction
# Validation for Colors
$Script:PDFColor = {
    ([iText.Kernel.Colors.ColorConstants] | Get-Member -static -MemberType Property).Name
}
$Script:PDFColorValidation = {
    $Array = @(
        (& $Script:PDFColor)
        ''
    )
    $_ -in $Array
}

function Get-PDFConstantColor {
    [CmdletBinding()]
    param(
        [ValidateScript( { & $Script:PDFColorValidation } )][string] $Color
    )
    return [iText.Kernel.Colors.ColorConstants]::$Color
}

Register-ArgumentCompleter -CommandName Get-PDFConstantColor -ParameterName Color -ScriptBlock $Script:PDFColor
# Validation for Fonts
$Script:PDFFont = {
    ([iText.IO.Font.Constants.StandardFonts] | Get-Member -static -MemberType Property).Name
}
$Script:PDFFontValidation = {
    $Array = @(
        (& $Script:PDFFont)
        ''
    )
    $_ -in $Array
}

function Get-PDFConstantFont {
    [CmdletBinding()]
    param(
        [ValidateScript( { & $Script:PDFFontValidation } )][string] $Font
    )
    return [iText.IO.Font.Constants.StandardFonts]::$Font
}

Register-ArgumentCompleter -CommandName Get-PDFConstantFont -ParameterName Font -ScriptBlock $Script:PDFFont
# Validation for PageSize
$Script:PDFPageSize = {
    ([iText.Kernel.Geom.PageSize] | Get-Member -static -MemberType Property).Name
}
$Script:PDFPageSizeValidation = {
    # Empty element is added to allow no parameter value
    $Array = @(
        (& $Script:PDFPageSize)
        ''
    )
    $_ -in $Array
}

function Get-PDFConstantPageSize {
    [CmdletBinding()]
    param(
        [ValidateScript( { & $Script:PDFPageSizeValidation } )][string] $PageSize
    )
    return [iText.Kernel.Geom.PageSize]::$PageSize
}

Register-ArgumentCompleter -CommandName Get-PDFConstantPageSize  -ParameterName Version -ScriptBlock $Script:PDFPageSize
# Validation for Fonts
$Script:PDFVersion = {
    ([iText.Kernel.Pdf.PdfVersion] | Get-Member -static -MemberType Property).Name
}
$Script:PDFVersionValidation = {
    $Array = @(
        (& $Script:PDFVersion)
        ''
    )
    $_ -in $Array
}

function Get-PDFConstantVersion {
    [CmdletBinding()]
    param(
        [ValidateScript( { & $Script:PDFVersionValidation } )][string] $Version
    )
    return [iText.Kernel.Pdf.PdfVersion]::$Version
}

Register-ArgumentCompleter -CommandName Get-PDFConstantVersion -ParameterName Version -ScriptBlock $Script:PDFVersion
function Convert-PDFToText {
    [CmdletBinding()]
    param(
        [string] $FilePath,
        [int[]] $Page
    )
    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        $ResolvedPath = Resolve-Path -LiteralPath $FilePath
        $Source = [iText.Kernel.Pdf.PdfReader]::new($ResolvedPath)
        try {
            [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()
        } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Warning "Convert-PDFToText - Processing document $ResolvedPath failed with error: $ErrorMessage"
        }

        $PagesCount = $SourcePDF.GetNumberOfPages()
        if ($Page.Count -eq 0) {
            for ($Count = 1; $Count -le $PagesCount; $Count++) {
                try {
                    $ExtractedPage = $SourcePDF.GetPage($Count)
                    [iText.Kernel.Pdf.Canvas.Parser.PdfTextExtractor]::GetTextFromPage($ExtractedPage, $ExtractionStrategy)
                } catch {
                    $ErrorMessage = $_.Exception.Message
                    Write-Warning "Convert-PDFToText - Processing document $ResolvedPath failed with error: $ErrorMessage"
                }
            }
        } else {
            foreach ($Count in $Page) {
                if ($Count -le $PagesCount -and $Count -gt 0) {
                    try {
                        $ExtractedPage = $SourcePDF.GetPage($Count)
                        [iText.Kernel.Pdf.Canvas.Parser.PdfTextExtractor]::GetTextFromPage($ExtractedPage, $ExtractionStrategy)
                    } catch {
                        $ErrorMessage = $_.Exception.Message
                        Write-Warning "Convert-PDFToText - Processing document $ResolvedPath failed with error: $ErrorMessage"
                    }
                } else {
                    Write-Warning "Convert-PDFToText - File $ResolvedPath 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) {
        try {
            [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)
        } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Warning "Merge-PDF - Processing document $OutputFile failed with error: $ErrorMessage"
        }
        foreach ($File in $InputFile) {
            if ($File -and (Test-Path -LiteralPath $File)) {
                $ResolvedFile = Resolve-Path -LiteralPath $File
                try {
                    $Source = [iText.Kernel.Pdf.PdfReader]::new($ResolvedFile)
                    [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 $ResolvedFile 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 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)) {
        $ResolvedPath = Resolve-Path -LiteralPath $FilePath
        if ($OutputFolder -and (Test-Path -LiteralPath $OutputFolder)) {
            try {
                $PDFFile = [iText.Kernel.Pdf.PdfReader]::new($ResolvedPath)
                $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."
    }
}
function New-PDF {
    [CmdletBinding()]
    param(
        [scriptblock] $PDFContent,
        [string] $FilePath,
        [string] $Version,

        [nullable[float]] $MarginLeft,
        [nullable[float]] $MarginRight,
        [nullable[float]] $MarginTop,
        [nullable[float]] $MarginBottom,
        [ValidateScript( { & $Script:PDFPageSizeValidation } )][string] $PageSize,
        [switch] $Rotate,

        [alias('Open')][switch] $Show
    )
    # The types needs filling in for the workaround below. DONT FORGET!
    $Script:Types = 'Text', 'List', 'Paragraph', 'Table', 'Image'
    $Script:PDFStart = @{
        Start            = $true
        FilePath         = $FilePath
        DocumentSettings = @{
            MarginTop    = $MarginTop
            MarginBottom = $MarginBottom
            MarginLeft   = $MarginLeft
            MarginRight  = $MarginRight
            PageSize     = $PageSize
            Rotate       = $Rotate.IsPresent
        }
        FirstPageUsed    = $false
        UsedTypes        = [System.Collections.Generic.List[string]]::new()
    }
    if ($PDFContent) {
        [Array] $Elements = @(
            [Array] $Content = & $PDFContent
            # This is workaround to support multiple scenarios:
            # New-PDF { New-PDFPage, New-PDFPage }
            # New-PDF { }
            # New-PDF { New-PDFText, New-PDFPage }
            # This is there to make sure we allow for New-PDF to allow control for -Rotate/PageSize to either whole document or to text/tables
            # and other types until New-PDFPage is given
            # It's ugly but seems to wrok
            foreach ($_ in $Content) {
                if ($_.Type -in $Script:Types) {
                    $Script:PDFStart.UsedTypes.Add($_.Type)
                } elseif ($_.Type -eq 'Page') {
                    if ($Script:PDFStart.UsedTypes.Count -ne 0) {
                        New-PDFPage -PageSize $PageSize -Rotate:$Rotate
                    }
                    break
                }
            }
            $Content
        )
        $Script:PDF = New-InternalPDF -FilePath $FilePath -Version $Version #-PageSize $PageSize -Rotate:$Rotate
    } else {
        $Script:PDFStart['Start'] = $false
        # if there's no scriptblock that means we're using standard way of working with PDF
        $Script:PDF = New-InternalPDF -FilePath $FilePath -Version $Version #-PageSize $PageSize -Rotate:$Rotate
        return $Script:PDF
    }

    $Script:PDFStart['Start'] = $true
    $Script:Document = New-PDFDocument -PDF $Script:PDF
    New-InternalPDFOptions -MarginLeft $MarginLeft -MarginRight $MarginRight -MarginTop $MarginTop -MarginBottom $MarginBottom

    Initialize-PDF -Elements $Elements
    if ($Script:PDF) {
        $Script:PDF.Close();
    }
    if ($Show) {
        if (Test-Path -LiteralPath $FilePath) {
            Invoke-Item -LiteralPath $FilePath
        }
    }
    $Script:PDFStart = $null
    #$Script:PDFStart['Start'] = $false
}

Register-ArgumentCompleter -CommandName New-PDF -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
Register-ArgumentCompleter -CommandName New-PDF -ParameterName Version -ScriptBlock $Script:PDFVersion
function New-PDFArea {
    param(
        [iText.Layout.Properties.AreaBreakType] $AreaType = [iText.Layout.Properties.AreaBreakType]::NEXT_AREA,
        [string] $PageSize,
        [switch] $Rotate
    )
    # https://api.itextpdf.com/iText7/dotnet/7.1.8/classi_text_1_1_kernel_1_1_geom_1_1_page_size.html
    $AreaBreak = [iText.Layout.Element.AreaBreak]::new($AreaType)
    if ($PageSize) {
        $Page = [iText.Kernel.Geom.PageSize]::($PageSize)
    } else {
        $Page = [iText.Kernel.Geom.PageSize]::Default
    }
    if ($Rotate) {
        $Page = $Page.Rotate()
    }
    $AreaBreak.SetPageSize($Page)
    return $AreaBreak
}

Register-ArgumentCompleter -CommandName New-PDFArea  -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
function New-PDFDocument {
    [CmdletBinding()]
    param(
        [iText.Kernel.Pdf.PdfDocument] $PDF
    )
    [iText.Layout.Document] $Document = [iText.Layout.Document]::new($PDF)
    return $Document
}
function New-PDFInfo {
    [CmdletBinding()]
    param(
        [iText.Kernel.Pdf.PdfDocument] $PDF,
        [string] $Title,
        [string] $Author,
        [string] $Creator,
        [string] $Subject,
        [string[]] $Keywords,
        [switch] $AddCreationDate,
        [switch] $AddModificationDate
    )

    try {
        [iText.Kernel.Pdf.PdfDocumentInfo] $info = $pdf.GetDocumentInfo()
    } catch {
        Write-Warning "New-PDFInfo - Error: $($_.Exception.Message)"
        return
    }

    if ($Title) {
        $null = $info.SetTitle($Title)
    }
    if ($AddCreationDate) {
        $null = $info.AddCreationDate()
    }
    if ($AddModificationDate) {
        $null = $info.AddModDate()
    }
    if ($Author) {
        $null = $info.SetAuthor($Author)
    }
    if ($Creator) {
        $null = $info.SetCreator($Creator)
    }
    if ($Subject) {
        $null = $info.SetSubject($Subject)
    }
    if ($Keywords) {
        $KeywordsString = $Keywords -join ','
        $null = $info.SetKeywords($KeywordsString)
    }
}
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 = @{
                MarginLeft   = $MarginLeft
                MarginRight  = $MarginRight
                MarginTop    = $MarginTop
                MarginBottom = $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
    )
    if ($null -ne $Script:PDFStart -and $Script:PDFStart['Start']) {
        $Page = [PSCustomObject] @{
            Type     = 'Page'
            Settings = @{
                Margins     = @{
                    Left   = $MarginLeft
                    Right  = $MarginRight
                    Top    = $MarginTop
                    Bottom = $MarginBottom
                }
                PageSize    = $PageSize
                Rotate      = $Rotate.IsPresent
                PageContent = if ($PageContent) { & $PageContent } else { $null }
            }
        }
        $Page
    } else {
        # New-InternalPDFPage -PageSize $PageSize -Rotate:$Rotate.IsPresent
    }
}

Register-ArgumentCompleter -CommandName New-PDFPage -ParameterName PageSize -ScriptBlock $Script:PDFPageSize
function New-PDFTable {
    [CmdletBinding()]
    param(
        [Array] $DataTable
    )
    if ($null -ne $Script:PDFStart -and $Script:PDFStart['Start']) {
        $Settings = [PSCustomObject] @{
            Type     = 'Table'
            Settings = @{
                DataTable = $DataTable
            }
        }
        $Settings
    } else {
        New-InteralPDFTable -DataTable $DataTable
    }
}
function New-PDFText {
    [CmdletBinding()]
    param(
        [string[]] $Text,
        [ValidateScript( { & $Script:PDFFontValidation } )][string[]] $Font,
        #[string[]] $FontFamily,
        [ValidateScript( { & $Script:PDFColorValidation } )][string[]] $FontColor,
        [bool[]] $FontBold
    )
    $Splat = @{ }
    if ($Text) {
        $Splat['Text'] = $Text
    }
    if ($Font) {
        $Splat['Font'] = $Font
    }
    #FontFamily = $FontFamily
    if ($FontColor) {
        $Splat['FontColor'] = $FontColor
    }
    if ($FontBold) {
        $Splat['FontBold'] = $FontBold
    }

    if ($null -ne $Script:PDFStart -and $Script:PDFStart['Start']) {
        $Settings = [PSCustomObject] @{
            Type     = 'Text'
            Settings = $Splat
        }
        $Settings
    } else {
        New-InternalPDFText @Splat
    }
}

Register-ArgumentCompleter -CommandName New-PDFText -ParameterName Font -ScriptBlock $Script:PDFFont
Register-ArgumentCompleter -CommandName New-PDFText -ParameterName FontColor -ScriptBlock $Script:PDFColor



Export-ModuleMember -Function @('Convert-PDFToText', 'Get-PDFConstantAction', 'Get-PDFConstantColor', 'Get-PDFConstantFont', 'Get-PDFConstantPageSize', 'Get-PDFConstantVersion', 'Merge-PDF', 'New-PDF', 'New-PDFArea', 'New-PDFDocument', 'New-PDFInfo', 'New-PDFList', 'New-PDFListItem', 'New-PDFOptions', 'New-PDFPage', 'New-PDFTable', 'New-PDFText', 'Split-PDF') -Alias @()