PSWritePDF.psm1

function Remove-EmptyValue { 
    [alias('Remove-EmptyValues')]
    [CmdletBinding()]
    param([alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable,
        [string[]] $ExcludeParameter,
        [switch] $Recursive,
        [int] $Rerun,
        [switch] $DoNotRemoveNull,
        [switch] $DoNotRemoveEmpty,
        [switch] $DoNotRemoveEmptyArray,
        [switch] $DoNotRemoveEmptyDictionary)
    foreach ($Key in [string[]] $Hashtable.Keys) { if ($Key -notin $ExcludeParameter) { if ($Recursive) { if ($Hashtable[$Key] -is [System.Collections.IDictionary]) { if ($Hashtable[$Key].Count -eq 0) { if (-not $DoNotRemoveEmptyDictionary) { $Hashtable.Remove($Key) } } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } }
    if ($Rerun) { for ($i = 0; $i -lt $Rerun; $i++) { Remove-EmptyValue -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 -MarginTop $MarginTop -MarginBottom $MarginBottom -MarginLeft $MarginLeft -MarginRight $MarginRight
        $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)
    }
}

# [iText.Kernel.Geom.Vector]::I1
<#
    RenderText([iText.Kernel.Pdf.Canvas.Parser.Data.TextRenderInfo] $renderInfo) {
        [string] $curFont = $renderInfo.GetFont().PostscriptFontName
        #//Check if faux bold is used
        if ((renderInfo.GetTextRenderMode() == (int)TextRenderMode.FillThenStrokeText)) {
            $curFont += "-Bold";
        }
 
        #//This code assumes that if the baseline changes then we're on a newline
        [iText.Kernel.Geom.Vector] $curBaseline = $renderInfo.GetBaseline().GetStartPoint();
        [iText.Kernel.Geom.Vector] $topRight = $renderInfo.GetAscentLine().GetEndPoint();
        [iText.Kernel.Geom.Rectangle] $rect = [iText.Kernel.Geom.Rectangle]::new($curBaseline[Vector.I1], $curBaseline[Vector.I2], $topRight[Vector.I1], $topRight[Vector.I2]);
        [Single] $curFontSize = $rect.Height;
 
        #//See if something has changed, either the baseline, the font or the font size
        if (($this.lastBaseLine -eq $null) -or ($curBaseline[Vector.I2] -ne $lastBaseLine[Vector.I2]) -or ($curFontSize -ne $lastFontSize) -or ($curFont -ne $lastFont)) {
            #//if we've put down at least one span tag close it
            if (($this.lastBaseLine -ne $null)) {
                $this.result.AppendLine("</span>");
            }
            #//If the baseline has changed then insert a line break
            if (($this.lastBaseLine -ne $null) -and ($curBaseline[Vector.I2] -ne $lastBaseLine[Vector.I2]) {
                $this.result.AppendLine("<br />");
            }
            #//Create an HTML tag with appropriate styles
            $this.result.AppendFormat("<span style=`"font-family: { 0 }; font-size: { 1 }`">", $curFont, $curFontSize);
        }
        #//Append the current text
        $this.result.Append($renderInfo.GetText());
 
        #//Set currently used properties
        $this.lastBaseLine = curBaseline;
        $this.lastFontSize = curFontSize;
        $this.lastFont = curFont;
    }
#>

Enum TextRenderMode {
    FillText = 0
    StrokeText = 1
    FillThenStrokeText = 2
    Invisible = 3
    FillTextAndAddToPathForClipping = 4
    StrokeTextAndAddToPathForClipping = 5
    FillThenStrokeTextAndAddToPathForClipping = 6
    AddTextToPaddForClipping = 7
}
class TextWithFontExtractionStategy : iText.Kernel.Pdf.Canvas.Parser.Listener.ITextExtractionStrategy {
    #HTML buffer
    [System.Text.StringBuilder] $result = [System.Text.StringBuilder]::new()

    #Store last used properties
    [iText.Kernel.Geom.Vector] $lastBaseLine
    [string] $lastFont
    [single] $lastFontSize

    RenderText([iText.Kernel.Pdf.Canvas.Parser.Data.TextRenderInfo] $renderInfo) {
        [string] $curFont = $renderInfo.GetFont().PostscriptFontName;
        #//Check if faux bold is used
        if ($renderInfo.GetTextRenderMode() -eq [int][TextRenderMode]::FillThenStrokeText) {
            $curFont += "-Bold";
        }

        #//This code assumes that if the baseline changes then we're on a newline
        [iText.Kernel.Geom.Vector] $curBaseline = $renderInfo.GetBaseline().GetStartPoint();
        [iText.Kernel.Geom.Vector] $topRight = $renderInfo.GetAscentLine().GetEndPoint();
        [iText.Kernel.Geom.Rectangle] $rect = [iText.Kernel.Geom.Rectangle]::new($curBaseline, $curBaseline, $topRight, $topRight);
        [Single] $curFontSize = $rect.Height;

        #//See if something has changed, either the baseline, the font or the font size
        if (($null -eq $this.lastBaseLine) -or ($curBaseline -ne $this.lastBaseLine) -or ($curFontSize -ne $this.lastFontSize) -or ($curFont -ne $this.lastFont)) {
            #//if we've put down at least one span tag close it
            if ($null -ne $this.lastBaseLine) {
                $this.result.AppendLine("</span>");
            }
            #//If the baseline has changed then insert a line break
            if (($null -ne $this.lastBaseLine) -and ($curBaseline -ne $this.lastBaseLine)) {
                $this.result.AppendLine("<br />");
            }
            #//Create an HTML tag with appropriate styles
            $this.result.AppendFormat("<span style=`"font-family: { 0 }; font-size: { 1 }`">", $curFont, $curFontSize);
        }
        #//Append the current text
        $this.result.Append($renderInfo.GetText());

        #//Set currently used properties
        $this.lastBaseLine = $curBaseline;
        $this.lastFontSize = $curFontSize;
        $this.lastFont = $curFont;
    }

    [string] GetResultantText() {
        #//If we wrote anything then we'll always have a missing closing tag so close it here
        if ($this.result.Length -gt 0) {
            $this.result.Append("</span>");
        }
        return $this.result.ToString();
    }
    [System.Collections.Generic.ICollection[iText.Kernel.Pdf.Canvas.Parser.EventType]] GetSupportedEvents() {
        return $null
    }
    [void] EventOccurred([iText.Kernel.Pdf.Canvas.Parser.Data.IEventData] $data, [iText.Kernel.Pdf.Canvas.Parser.EventType] $type) {
        return
    }
}
# Validation for Fonts
$Script:PDFFont = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    ([iText.IO.Font.Constants.StandardFonts] | Get-Member -Static -MemberType Property).Name | Where-Object { $_ -like "*$wordToComplete*" }
}
$Script:PDFFontValidation = {
    $Array = @(
        (& $Script:PDFFont)
        ''
    )
    $_ -in $Array
}
$Script:PDFFontList = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    @(
        ([iText.IO.Font.Constants.StandardFonts] | Get-Member -Static -MemberType Property).Name
        if ($Script:Fonts.Keys) { $Script:Fonts.Keys }
    ) | Where-Object { $_ -like "*$wordToComplete*" }
}
$Script:PDFFontValidationList = {
    $Array = @(
        (& $Script:PDFFont)
        $Script:Fonts.Keys
        ''
    )
    $_ -in $Array
}
function Get-InternalPDFFont {
    [cmdletBinding()]
    param(
        [ValidateScript( { & $Script:PDFFontValidationList } )][string] $Font
    )
    if (-not $Script:Fonts[$Font]) {
        # If not defined font, we use contstant fonts
        $ConvertedFont = Get-PDFConstantFont -Font $Font
        $ApplyFont = [iText.Kernel.Font.PdfFontFactory]::CreateFont($ConvertedFont)
    } else {
        # if defined we use whatever we defined
        $ApplyFont = $Script:Fonts[$Font]
    }
    $ApplyFont
}

Register-ArgumentCompleter -CommandName Get-InternalPDFFont -ParameterName Font -ScriptBlock $Script:PDFFontList
function Get-PDFNamedPageSize {
    [CmdletBinding()]
    param(
        [int] $Height,
        [int] $Width
    )

    $PDFSizes = @{
        '33702384' = [PSCustomObject] @{ PageSize = 'A0'; Rotated = $false }
        '23843370' = [PSCustomObject] @{ PageSize = 'A0'; Rotated = $true }
        '23841684' = [PSCustomObject] @{ PageSize = 'A1'; Rotated = $false }
        '16842384' = [PSCustomObject] @{ PageSize = 'A1'; Rotated = $true }
        '10574'    = [PSCustomObject] @{ PageSize = 'A10'; Rotated = $false }
        '74105'    = [PSCustomObject] @{ PageSize = 'A10'; Rotated = $true }
        '16841190' = [PSCustomObject] @{ PageSize = 'A2'; Rotated = $false }
        '11901684' = [PSCustomObject] @{ PageSize = 'A2'; Rotated = $true }
        '1190842'  = [PSCustomObject] @{ PageSize = 'A3'; Rotated = $false }
        '8421190'  = [PSCustomObject] @{ PageSize = 'A3'; Rotated = $true }
        '842595'   = [PSCustomObject] @{ PageSize = 'A4'; Rotated = $false }
        '595842'   = [PSCustomObject] @{ PageSize = 'A4'; Rotated = $true }
        '595420'   = [PSCustomObject] @{ PageSize = 'A5'; Rotated = $false }
        '420595'   = [PSCustomObject] @{ PageSize = 'A5'; Rotated = $true }
        '420298'   = [PSCustomObject] @{ PageSize = 'A6'; Rotated = $false }
        '298420'   = [PSCustomObject] @{ PageSize = 'A6'; Rotated = $true }
        '298210'   = [PSCustomObject] @{ PageSize = 'A7'; Rotated = $false }
        '210298'   = [PSCustomObject] @{ PageSize = 'A7'; Rotated = $true }
        '210148'   = [PSCustomObject] @{ PageSize = 'A8'; Rotated = $false }
        '148210'   = [PSCustomObject] @{ PageSize = 'A8'; Rotated = $true }
        '547105'   = [PSCustomObject] @{ PageSize = 'A9'; Rotated = $false }
        '105547'   = [PSCustomObject] @{ PageSize = 'A9'; Rotated = $true }
        '40082834' = [PSCustomObject] @{ PageSize = 'B0'; Rotated = $false }
        '28344008' = [PSCustomObject] @{ PageSize = 'B0'; Rotated = $true }
        '28342004' = [PSCustomObject] @{ PageSize = 'B1'; Rotated = $false }
        '20042834' = [PSCustomObject] @{ PageSize = 'B1'; Rotated = $true }
        '12488'    = [PSCustomObject] @{ PageSize = 'B10'; Rotated = $false }
        '88124'    = [PSCustomObject] @{ PageSize = 'B10'; Rotated = $true }
        '20041417' = [PSCustomObject] @{ PageSize = 'B2'; Rotated = $false }
        '14172004' = [PSCustomObject] @{ PageSize = 'B2'; Rotated = $true }
        '14171000' = [PSCustomObject] @{ PageSize = 'B3'; Rotated = $false }
        '10001417' = [PSCustomObject] @{ PageSize = 'B3'; Rotated = $true }
        '1000708'  = [PSCustomObject] @{ PageSize = 'B4'; Rotated = $false }
        '7081000'  = [PSCustomObject] @{ PageSize = 'B4'; Rotated = $true }
        '708498'   = [PSCustomObject] @{ PageSize = 'B5'; Rotated = $false }
        '498708'   = [PSCustomObject] @{ PageSize = 'B5'; Rotated = $true }
        '498354'   = [PSCustomObject] @{ PageSize = 'B6'; Rotated = $false }
        '354498'   = [PSCustomObject] @{ PageSize = 'B6'; Rotated = $true }
        '354249'   = [PSCustomObject] @{ PageSize = 'B7'; Rotated = $false }
        '249354'   = [PSCustomObject] @{ PageSize = 'B7'; Rotated = $true }
        '249175'   = [PSCustomObject] @{ PageSize = 'B8'; Rotated = $false }
        '175249'   = [PSCustomObject] @{ PageSize = 'B8'; Rotated = $true }
        '175124'   = [PSCustomObject] @{ PageSize = 'B9'; Rotated = $false }
        '124175'   = [PSCustomObject] @{ PageSize = 'B9'; Rotated = $true }
        '756522'   = [PSCustomObject] @{ PageSize = 'EXECUTIVE'; Rotated = $false }
        '522756'   = [PSCustomObject] @{ PageSize = 'EXECUTIVE'; Rotated = $true }
        '7921224'  = [PSCustomObject] @{ PageSize = 'LEDGER or TABLOID'; Rotated = $false }
        '1224792'  = [PSCustomObject] @{ PageSize = 'LEDGER or TABLOID'; Rotated = $true }
        '1008612'  = [PSCustomObject] @{ PageSize = 'LEGAL'; Rotated = $false }
        '6121008'  = [PSCustomObject] @{ PageSize = 'LEGAL'; Rotated = $true }
        '792612'   = [PSCustomObject] @{ PageSize = 'LETTER'; Rotated = $false }
        '612792'   = [PSCustomObject] @{ PageSize = 'LETTER'; Rotated = $true }
    }
    $Size = $PDFSizes["$Height$Width"]
    if ($Size) {
        return $Size
    } else {
        return [PSCustomObject] @{ PageSize = 'Unknown'; $Rotated = $null }
    }
}
function Initialize-PDF {
    [CmdletBinding()]
    param(
        [Array] $Elements
    )
    foreach ($Element in $Elements) {
        $Splat = $Element.Settings
        if ($Splat) {
            Remove-EmptyValue -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

                # This adds margings to first page if New-PDFOptions is used but no margins were defined on New-PDFPage
                if (-not $Splat.Margins.MarginBottom) {
                    $Splat.Margins.MarginBottom = $Script:PDFStart.DocumentSettings.MarginBottom
                }
                if (-not $Splat.Margins.MarginTop) {
                    $Splat.Margins.MarginTop = $Script:PDFStart.DocumentSettings.MarginTop
                }
                if (-not $Splat.Margins.MarginLeft) {
                    $Splat.Margins.MarginLeft = $Script:PDFStart.DocumentSettings.MarginLeft
                }
                if (-not $Splat.Margins.MarginRight) {
                    $Splat.Margins.MarginRight = $Script:PDFStart.DocumentSettings.MarginRight
                }
                New-InternalPDFPage -PageSize $Splat.PageSize -Rotate:$Splat.Rotate -MarginLeft $Splat.Margins.MarginLeft -MarginTop $Splat.Margins.MarginTop -MarginBottom $Splat.Margins.MarginBottom -MarginRight $Splat.Margins.MarginRight
                #Add-PDFDocumentContent -Object $Area
                $Script:PDFStart.FirstPageUsed = $true
            } else {
                # This fixes margins. Margins have to be added before new PageArea
                $SplatMargins = $Splat.Margins
                New-InternalPDFOptions @SplatMargins

                $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) {
                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."
            return
        } else {
            Write-Warning "New-InternalPDF - Terminating error: $($_.Exception.Message)"
            return
        }
    }

    if ($Script:Writer) {
        $PDF = [iText.Kernel.Pdf.PdfDocument]::new($Script:Writer)
    } else {
        Write-Warning "New-InternalPDF - Terminating as writer doesn't exists."
        return
    }
    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 ($ItemSettings in $Settings.Items) {
        $Paragraph = New-InternalPDFText @ItemSettings
        $ListItem = [iText.Layout.Element.ListItem]::new()
        $null = $ListItem.Add($Paragraph)
        $null = $List.Add($ListItem)
    }
    $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)
        }
    } else {
        # If Document doesn't exists it means we're using New-PDFOptions before New-PDFPage
        # We update DocumentSettings and use it just in case New-PDFPage doesn't have those values used.
        # Workaround, ugly but don't see a better way
        $Script:PDFStart['DocumentSettings']['MarginTop'] = $MarginTop
        $Script:PDFStart['DocumentSettings']['MarginBottom'] = $MarginBottom
        $Script:PDFStart['DocumentSettings']['MarginLeft'] = $MarginLeft
        $Script:PDFStart['DocumentSettings']['MarginRight'] = $MarginRight
    }

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

    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)

    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)
        }
    }
}

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 $DataTable) {
            $_.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:PDFFontValidationList } )][string[]] $Font,
        #[string[]] $FontFamily,
        [ValidateScript( { & $Script:PDFColorValidation } )][string[]] $FontColor,
        [nullable[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, [iText.IO.Font.PdfEncodings]::IDENTITY_H, $false)
                    #$ApplyFont = [iText.Kernel.Font.PdfFontFactory]::CreateFont('TIMES_ROMAN', [iText.IO.Font.PdfEncodings]::IDENTITY_H, $false)
                    #$PDFText = $PDFText.SetFont($ApplyFont)
                    $ApplyFont = Get-InternalPDFFont -Font $Font[$i]
                    $PDFText = $PDFText.SetFont($ApplyFont)
                }
            } else {
                if ($DefaultColor) {
                    # $ConvertedFont = Get-PDFConstantFont -Font $DefaultFont
                    # $ApplyFont = [iText.Kernel.Font.PdfFontFactory]::CreateFont($ConvertedFont, [iText.IO.Font.PdfEncodings]::IDENTITY_H, $false)
                    # $PDFText = $PDFText.SetFont($ApplyFont)
                    $ApplyFont = Get-InternalPDFFont -Font $DefaultFont
                    $PDFText = $PDFText.SetFont($ApplyFont)
                }
            }
        } else {
            if ($Script:DefaultFont) {
                $PDFText = $PDFText.SetFont($Script:DefaultFont)
            }
        }
        $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
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,
        [switch] $All
    )
    if (-not $All) {
        return [iText.Kernel.Geom.PageSize]::$PageSize
    } else {
        return & $Script:PDFPageSize
    }
}

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 = Convert-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."
                }
            }
        }
        try {
            $SourcePDF.Close()
        } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Warning "Convert-PDFToText - Closing document $FilePath failed with error: $ErrorMessage"
        }
    } 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 = Convert-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 Set-PDFForm {
    <#
    .SYNOPSIS
    Will try to fill form fields in source PDF with values from hash table.
    Can also flatten the form to prevent changes with -flatten.
 
    .DESCRIPTION
    Adds a file name extension to a supplied name.
    Takes any strings for the file name or extension.
 
    .PARAMETER SourceFilePath
    Specifies the to be filled in PDF Form.
 
    .PARAMETER DestinationFilePath
    Specifies the output filepath for the completed form.
 
    .PARAMETER FieldNameAndValueHashTable
    Specifies the hashtable for the fields data. Key in the hashtable needs to match the feild name in the PDF.
 
    .PARAMETER Flatten
    Will flatten the output PDF so form fields will no longer be able to be changed.
 
    .EXAMPLE
    $FilePath = [IO.Path]::Combine("$PSScriptRoot", "Output", "SampleAcroFormOutput.pdf")
    $FilePathSource = [IO.Path]::Combine("$PSScriptRoot", "Input", "SampleAcroForm.pdf")
 
    $FieldNameAndValueHashTable = [ordered] @{
        "Text 1" = "Text 1 input"
        "Text 2" = "Text 2 input"
        "Text 3" = "Text 3 input"
        "Check Box 1 True" = $true
        "Check Box 2 False" = $false
        "Check Box 3 False" = $false
        "Check Box 4 True" = $true
        "Doesn't Exist" = "will not be used"
    }
    Set-PDFForm -SourceFilePath $FilePathSource -DestinationFilePath $FilePath -FieldNameAndValueHashTable $FieldNameAndValueHashTable -Flatten
 
    .EXAMPLE
    $FilePath = [IO.Path]::Combine("$PSScriptRoot", "Output", "SampleAcroFormOutput.pdf")
    $FilePathSource = [IO.Path]::Combine("$PSScriptRoot", "Input", "SampleAcroForm.pdf")
    Set-PDFForm -SourceFilePath $FilePathSource -DestinationFilePath $FilePath -Flatten
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][ValidateNotNullOrEmpty()] $SourceFilePath,
        [Parameter(Mandatory)][ValidateNotNullOrEmpty()] $DestinationFilePath,
        [System.Collections.IDictionary] $FieldNameAndValueHashTable,
        [alias('FlattenFields')][Switch] $Flatten
    )
    $SourceFilePath = Convert-Path -LiteralPath $SourceFilePath
    $DestinationFolder = Split-Path -LiteralPath $DestinationFilePath
    $DestinationFolderPath = Convert-Path -LiteralPath $DestinationFolder -ErrorAction SilentlyContinue

    if ($DestinationFolderPath -and (Test-Path -LiteralPath $SourceFilePath) -and (Test-Path -LiteralPath $DestinationFolderPath)) {
        $File = Split-Path -Path $DestinationFilePath -Leaf
        $DestinationFilePath = Join-Path -Path $DestinationFolderPath -ChildPath $File

        try {
            $Script:Reader = [iText.Kernel.Pdf.PdfReader]::new($SourceFilePath)
            $Script:Writer = [iText.Kernel.Pdf.PdfWriter]::new($DestinationFilePath)
            $PDF = [iText.Kernel.Pdf.PdfDocument]::new($Script:Reader, $Script:Writer)
            $PDFAcroForm = [iText.Forms.PdfAcroForm]::getAcroForm($PDF, $true)
        } catch {
            Write-Warning "Set-PDFForm - Error has occured: $($_.Exception.Message)"
        }
        foreach ($Key in $FieldNameAndValueHashTable.Keys) {
            $FormField = $PDFAcroForm.getField($Key)
            if ( -not $FormField) {
                Write-Warning "Set-PDFForm - No form field with name '$Key' found"
                continue
            }

            if ($FormField.GetType().Name -match "Button") {
                $null = $FormField.setValue(
                    $FormField.GetAppearanceStates()[[Int]$FieldNameAndValueHashTable[$Key]]
                )
            } else {
                $null = $FormField.setValue($FieldNameAndValueHashTable[$Key])
            }
        }

        if ($Flatten) {
            $PDFAcroForm.flattenFields()
        }

        try {
            $PDF.Close()
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                Write-Error $_
                return
            } else {
                $ErrorMessage = $_.Exception.Message
                Write-Warning "Set-PDFForm - Error has occured: $ErrorMessage"
            }
        }
    } else {
        if (-not (Test-Path -LiteralPath $SourceFilePath)) {
            Write-Warning "Set-PDFForm - Path $SourceFilePath doesn't exists. Terminating."
        }

        if (-not (Test-Path -LiteralPath $DestinationFolder)) {
            Write-Warning "Set-PDFForm - Folder $DestinationFolder doesn't exists. 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 = Convert-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"
            }
            try {
                $PDFFile.Close()
            } catch {
                $ErrorMessage = $_.Exception.Message
                Write-Warning "Split-PDF - Closing document $FilePath failed with error: $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 Close-PDF {
    [CmdletBinding()]
    param(
        [iText.Kernel.Pdf.PdfDocument] $Document
    )
    if ($Document) {
        $Document.Close()
    }
}
function Get-PDF {
    [CmdletBinding()]
    param(
        [string] $FilePath
    )

    if ($FilePath -and (Test-Path -LiteralPath $FilePath)) {
        $ResolvedPath = Convert-Path -LiteralPath $FilePath

        try {
            $PDFFile = [iText.Kernel.Pdf.PdfReader]::new($ResolvedPath)
            $Document = [iText.Kernel.Pdf.PdfDocument]::new($PDFFile)
        } catch {
            $ErrorMessage = $_.Exception.Message
            Write-Warning "Get-PDF - Processing document $FilePath failed with error: $ErrorMessage"
        }
        $Document

    }
}
function Get-PDFDetails {
    [CmdletBinding()]
    param(
        [iText.Kernel.Pdf.PdfDocument] $Document
    )
    if ($Document) {
        [iText.Layout.Document] $LayoutDocument = [iText.Layout.Document]::new($Document)

        $Output = [ordered] @{
            Author       = $Document.GetDocumentInfo().GetAuthor()
            Creator      = $Document.GetDocumentInfo().GetCreator()
            HashCode     = $Document.GetDocumentInfo().GetHashCode()
            Keywords     = $Document.GetDocumentInfo().GetKeywords()
            Producer     = $Document.GetDocumentInfo().GetProducer()
            Subject      = $Document.GetDocumentInfo().GetSubject()
            Title        = $Document.GetDocumentInfo().GetTitle()
            Trapped      = $Document.GetDocumentInfo().GetTrapped()
            Version      = $Document.GetPdfVersion()
            PagesNumber  = $Document.GetNumberOfPages()
            MarginLeft   = $LayoutDocument.GetLeftMargin()
            MarginRight  = $LayoutDocument.GetRightMargin()
            MarginBottom = $LayoutDocument.GetBottomMargin()
            MarginTop    = $LayoutDocument.GetTopMargin()
            Pages        = [ordered] @{ }
        }
        for ($a = 1; $a -le $Output.PagesNumber; $a++) {
            $Height = $Document.GetPage($a).GetPageSizeWithRotation().GetHeight()
            $Width = $Document.GetPage($a).GetPageSizeWithRotation().GetWidth()
            $NamedSize = Get-PDFNamedPageSize -Height $Height -Width $Width
            $Output['Pages']["$a"] = [PSCustomObject] @{
                Height   = $Height
                Width    = $Width
                Rotation = $Document.GetPage($a).GetRotation()
                Size     = $NamedSize.PageSize
                Rotated  = $NamedSize.Rotated
            }
        }
        [PSCustomObject] $Output
    }
}
function Get-PDFFormField {
    [CmdletBinding()]
    param(
        [iText.Kernel.Pdf.PdfDocument]$PDF
    )
    try {
        $PDFAcroForm = [iText.Forms.PdfAcroForm]::getAcroForm($PDF, $true)
        $PDFAcroForm.GetFormFields()
    } catch {
        if ($PSBoundParameters.ErrorAction -eq 'Stop') {
            Write-Error $_
            return
        } else {
            Write-Warning -Message "Get-PDFFormField - There was an error reading forms or no form exists. Exception: $($_.Error.Exception)"
        }
    }
}
function New-PDF {
    [CmdletBinding()]
    param(
        [scriptblock] $PDFContent,
        [Parameter(Mandatory)][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
        }
        FirstPageFound   = $false
        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 -MarginLeft $MarginLeft -MarginTop $MarginTop -MarginBottom $MarginBottom -MarginRight $MarginRight
                        $Script:PDFStart.FirstPageFound = $true
                    }
                    break
                }
            }
            if (-not $Script:PDFStart.FirstPageFound -and $Script:PDFStart.UsedTypes.Count -ne 0) {
                New-PDFPage -PageSize $PageSize -Rotate:$Rotate -MarginLeft $MarginLeft -MarginTop $MarginTop -MarginBottom $MarginBottom -MarginRight $MarginRight
                $Script:PDFStart.FirstPageFound = $true
            }
            $Content
        )
        if ($Elements) {
            $Script:PDF = New-InternalPDF -FilePath $FilePath -Version $Version #-PageSize $PageSize -Rotate:$Rotate
            if (-not $Script:PDF) {
                return
            }
        } else {
            Write-Warning "New-PDF - No content was provided. Terminating."
            return
        }
    } 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
    #[iText.Layout.Document] $Script:Document = New-PDFDocument -PDF $Script:PDF
    #New-InternalPDFOptions -MarginLeft $MarginLeft -MarginRight $MarginRight -MarginTop $MarginTop -MarginBottom $MarginBottom

    Initialize-PDF -Elements $Elements
    if ($Script:Document) {
        $Script:Document.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 {
    [CmdletBinding()]
    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

<#
   TypeName: iText.Layout.Element.AreaBreak
 
Name MemberType Definition
---- ---------- ----------
AddStyle Method iText.Layout.Element.AreaBreak AddStyle(iText.Layout.Style style)
CreateRendererSubTree Method iText.Layout.Renderer.IRenderer CreateRendererSubTree(), iText.Layout.Renderer.IRenderer IElement.CreateRendererSubTree()
DeleteOwnProperty Method void DeleteOwnProperty(int property), void IPropertyContainer.DeleteOwnProperty(int property)
Equals Method bool Equals(System.Object obj)
GetAreaType Method System.Nullable[iText.Layout.Properties.AreaBreakType] GetAreaType()
GetChildren Method System.Collections.Generic.IList[iText.Layout.Element.IElement] GetChildren()
GetDefaultProperty Method T1 GetDefaultProperty[T1](int property), T1 IPropertyContainer.GetDefaultProperty[T1](int property)
GetHashCode Method int GetHashCode()
GetOwnProperty Method T1 GetOwnProperty[T1](int property), T1 IPropertyContainer.GetOwnProperty[T1](int property)
GetPageSize Method iText.Kernel.Geom.PageSize GetPageSize()
GetProperty Method T1 GetProperty[T1](int property), T1 IPropertyContainer.GetProperty[T1](int property)
GetRenderer Method iText.Layout.Renderer.IRenderer GetRenderer(), iText.Layout.Renderer.IRenderer IElement.GetRenderer()
GetSplitCharacters Method iText.Layout.Splitting.ISplitCharacters GetSplitCharacters()
GetStrokeColor Method iText.Kernel.Colors.Color GetStrokeColor()
GetStrokeWidth Method System.Nullable[float] GetStrokeWidth()
GetTextRenderingMode Method System.Nullable[int] GetTextRenderingMode()
GetType Method type GetType()
HasOwnProperty Method bool HasOwnProperty(int property), bool IPropertyContainer.HasOwnProperty(int property)
HasProperty Method bool HasProperty(int property), bool IPropertyContainer.HasProperty(int property)
IsEmpty Method bool IsEmpty()
SetAction Method iText.Layout.Element.AreaBreak SetAction(iText.Kernel.Pdf.Action.PdfAction action)
SetBackgroundColor Method iText.Layout.Element.AreaBreak SetBackgroundColor(iText.Kernel.Colors.Color backgroundColor), iText.Layout.Element.AreaBreak SetBackgroun...
SetBaseDirection Method iText.Layout.Element.AreaBreak SetBaseDirection(System.Nullable[iText.Layout.Properties.BaseDirection] baseDirection)
SetBold Method iText.Layout.Element.AreaBreak SetBold()
SetBorder Method iText.Layout.Element.AreaBreak SetBorder(iText.Layout.Borders.Border border)
SetBorderBottom Method iText.Layout.Element.AreaBreak SetBorderBottom(iText.Layout.Borders.Border border)
SetBorderBottomLeftRadius Method iText.Layout.Element.AreaBreak SetBorderBottomLeftRadius(iText.Layout.Properties.BorderRadius borderRadius)
SetBorderBottomRightRadius Method iText.Layout.Element.AreaBreak SetBorderBottomRightRadius(iText.Layout.Properties.BorderRadius borderRadius)
SetBorderLeft Method iText.Layout.Element.AreaBreak SetBorderLeft(iText.Layout.Borders.Border border)
SetBorderRadius Method iText.Layout.Element.AreaBreak SetBorderRadius(iText.Layout.Properties.BorderRadius borderRadius)
SetBorderRight Method iText.Layout.Element.AreaBreak SetBorderRight(iText.Layout.Borders.Border border)
SetBorderTop Method iText.Layout.Element.AreaBreak SetBorderTop(iText.Layout.Borders.Border border)
SetBorderTopLeftRadius Method iText.Layout.Element.AreaBreak SetBorderTopLeftRadius(iText.Layout.Properties.BorderRadius borderRadius)
SetBorderTopRightRadius Method iText.Layout.Element.AreaBreak SetBorderTopRightRadius(iText.Layout.Properties.BorderRadius borderRadius)
SetCharacterSpacing Method iText.Layout.Element.AreaBreak SetCharacterSpacing(float charSpacing)
SetDestination Method iText.Layout.Element.AreaBreak SetDestination(string destination)
SetFixedPosition Method iText.Layout.Element.AreaBreak SetFixedPosition(float left, float bottom, float width), iText.Layout.Element.AreaBreak SetFixedPosition(f...
SetFont Method iText.Layout.Element.AreaBreak SetFont(iText.Kernel.Font.PdfFont font), iText.Layout.Element.AreaBreak SetFont(string font)
SetFontColor Method iText.Layout.Element.AreaBreak SetFontColor(iText.Kernel.Colors.Color fontColor), iText.Layout.Element.AreaBreak SetFontColor(iText.Kerne...
SetFontFamily Method iText.Layout.Element.AreaBreak SetFontFamily(Params string[] fontFamilyNames), iText.Layout.Element.AreaBreak SetFontFamily(System.Collec...
SetFontKerning Method iText.Layout.Element.AreaBreak SetFontKerning(iText.Layout.Properties.FontKerning fontKerning)
SetFontScript Method iText.Layout.Element.AreaBreak SetFontScript(System.Nullable[iText.IO.Util.UnicodeScript] script)
SetFontSize Method iText.Layout.Element.AreaBreak SetFontSize(float fontSize)
SetHorizontalAlignment Method iText.Layout.Element.AreaBreak SetHorizontalAlignment(System.Nullable[iText.Layout.Properties.HorizontalAlignment] horizontalAlignment)
SetHyphenation Method iText.Layout.Element.AreaBreak SetHyphenation(iText.Layout.Hyphenation.HyphenationConfig hyphenationConfig)
SetItalic Method iText.Layout.Element.AreaBreak SetItalic()
SetLineThrough Method iText.Layout.Element.AreaBreak SetLineThrough()
SetNextRenderer Method void SetNextRenderer(iText.Layout.Renderer.IRenderer renderer), void IElement.SetNextRenderer(iText.Layout.Renderer.IRenderer renderer)
SetOpacity Method iText.Layout.Element.AreaBreak SetOpacity(System.Nullable[float] opacity)
SetPageNumber Method iText.Layout.Element.AreaBreak SetPageNumber(int pageNumber)
SetPageSize Method void SetPageSize(iText.Kernel.Geom.PageSize pageSize)
SetProperty Method void SetProperty(int property, System.Object value), void IPropertyContainer.SetProperty(int property, System.Object value)
SetRelativePosition Method iText.Layout.Element.AreaBreak SetRelativePosition(float left, float top, float right, float bottom)
SetSplitCharacters Method iText.Layout.Element.AreaBreak SetSplitCharacters(iText.Layout.Splitting.ISplitCharacters splitCharacters)
SetStrokeColor Method iText.Layout.Element.AreaBreak SetStrokeColor(iText.Kernel.Colors.Color strokeColor)
SetStrokeWidth Method iText.Layout.Element.AreaBreak SetStrokeWidth(float strokeWidth)
SetTextAlignment Method iText.Layout.Element.AreaBreak SetTextAlignment(System.Nullable[iText.Layout.Properties.TextAlignment] alignment)
SetTextRenderingMode Method iText.Layout.Element.AreaBreak SetTextRenderingMode(int textRenderingMode)
SetUnderline Method iText.Layout.Element.AreaBreak SetUnderline(), iText.Layout.Element.AreaBreak SetUnderline(float thickness, float yPosition), iText.Layou...
SetWordSpacing Method iText.Layout.Element.AreaBreak SetWordSpacing(float wordSpacing)
ToString Method string ToString()
#>

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,
        [ValidateScript( { & $Script:PDFFontValidationList } )][string[]] $Font,
        [ValidateScript( { & $Script:PDFColorValidation } )][string[]] $FontColor,
        [nullable[bool][]] $FontBold
    )

    $Splat = @{ }
    if ($Text) {
        $Splat['Text'] = $Text
    }
    if ($Font) {
        $Splat['Font'] = $Font
    }
    if ($FontColor) {
        $Splat['FontColor'] = $FontColor
    }
    if ($FontBold) {
        $Splat['FontBold'] = $FontBold
    }

    [PSCustomObject] @{
        Type     = 'ListItem'
        Settings = $Splat
    }
}

Register-ArgumentCompleter -CommandName New-PDFListItem -ParameterName Font -ScriptBlock $Script:PDFFontList
Register-ArgumentCompleter -CommandName New-PDFListItem -ParameterName FontColor -ScriptBlock $Script:PDFColor
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     = @{
                    MarginLeft   = $MarginLeft
                    MarginRight  = $MarginRight
                    MarginTop    = $MarginTop
                    MarginBottom = $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:PDFFontValidationList } )][string[]] $Font,
        [ValidateScript( { & $Script:PDFColorValidation } )][string[]] $FontColor,
        [nullable[bool][]] $FontBold
    )
    $Splat = @{ }
    if ($Text) {
        $Splat['Text'] = $Text
    }
    if ($Font) {
        $Splat['Font'] = $Font
    }
    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:PDFFontList
Register-ArgumentCompleter -CommandName New-PDFText -ParameterName FontColor -ScriptBlock $Script:PDFColor
function Register-PDFFont {
    [cmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $FontName,
        [Parameter(Mandatory)][string] $FontPath,
        [ValidateScript( { & $Script:PDFFontEncodingValidation } )][string] $Encoding,
        [switch] $Cached,
        [switch] $Default
    )
    try {
        if ($FontPath -and $Encoding) {
            $Script:Fonts[$FontName] = [iText.Kernel.Font.PdfFontFactory]::CreateFont($FontPath, [iText.IO.Font.PdfEncodings]::$Encoding, $Cached.IsPresent)
        } elseif ($FontPath) {
            $Script:Fonts[$FontName] = [iText.Kernel.Font.PdfFontFactory]::CreateFont($FontPath, $Cached.IsPresent)
        }
    } catch {
        Write-Warning "Register-PDFFont - Font registration failed. Error: $($_.Exception.Message)"
    }
    if (($Default -or $Script:Fonts.Count -eq 0) -and $Script:Fonts[$FontName]) {
        $Script:DefaultFont = $Script:Fonts[$FontName]
    }
}

$Script:Fonts = [ordered] @{}
# Validation for Fonts Encoding
$Script:PDFFontEncoding = {
    ([iText.IO.Font.PdfEncodings] | Get-Member -Static -MemberType Property).Name
}
$Script:PDFFontEncodingValidation = {
    $Array = @(
        (& $Script:PDFFontEncoding)
        ''
    )
    $_ -in $Array
}
Register-ArgumentCompleter -CommandName Register-PDFFont -ParameterName Encoding -ScriptBlock $Script:PDFFontEncoding



Export-ModuleMember -Function @('Close-PDF', 'Convert-PDFToText', 'Get-PDF', 'Get-PDFConstantAction', 'Get-PDFConstantColor', 'Get-PDFConstantFont', 'Get-PDFConstantPageSize', 'Get-PDFConstantVersion', 'Get-PDFDetails', 'Get-PDFFormField', 'Merge-PDF', 'New-PDF', 'New-PDFArea', 'New-PDFDocument', 'New-PDFInfo', 'New-PDFList', 'New-PDFListItem', 'New-PDFOptions', 'New-PDFPage', 'New-PDFTable', 'New-PDFText', 'Register-PDFFont', 'Set-PDFForm', 'Split-PDF') -Alias @()
# SIG # Begin signature block
# MIIdWQYJKoZIhvcNAQcCoIIdSjCCHUYCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUWLNI3sa/WL8Zgz6S8wcXLa0h
# SRKgghhnMIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0B
# AQUFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
# Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg
# +XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT
# XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5
# a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g
# 0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1
# roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
# GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G
# A1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLL
# gjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3
# cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmr
# EthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+
# fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5Q
# Z7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu
# 838fYxAe+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw
# 8jCCBP4wggPmoAMCAQICEA1CSuC+Ooj/YEAhzhQA8N0wDQYJKoZIhvcNAQELBQAw
# cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk
# IElEIFRpbWVzdGFtcGluZyBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAxMDYwMDAw
# MDBaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEgMB4G
# A1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjEwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQDC5mGEZ8WK9Q0IpEXKY2tR1zoRQr0KdXVNlLQMULUmEP4d
# yG+RawyW5xpcSO9E5b+bYc0VkWJauP9nC5xj/TZqgfop+N0rcIXeAhjzeG28ffnH
# bQk9vmp2h+mKvfiEXR52yeTGdnY6U9HR01o2j8aj4S8bOrdh1nPsTm0zinxdRS1L
# sVDmQTo3VobckyON91Al6GTm3dOPL1e1hyDrDo4s1SPa9E14RuMDgzEpSlwMMYpK
# jIjF9zBa+RSvFV9sQ0kJ/SYjU/aNY+gaq1uxHTDCm2mCtNv8VlS8H6GHq756Wwog
# L0sJyZWnjbL61mOLTqVyHO6fegFz+BnW/g1JhL0BAgMBAAGjggG4MIIBtDAOBgNV
# HQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# CDBBBgNVHSAEOjA4MDYGCWCGSAGG/WwHATApMCcGCCsGAQUFBwIBFhtodHRwOi8v
# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHwYDVR0jBBgwFoAU9LbhIB3+Ka7S5GGlsqIl
# ssgXNW4wHQYDVR0OBBYEFDZEho6kurBmvrwoLR1ENt3janq8MHEGA1UdHwRqMGgw
# MqAwoC6GLGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMu
# Y3JsMDKgMKAuhixodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVk
# LXRzLmNybDCBhQYIKwYBBQUHAQEEeTB3MCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
# cC5kaWdpY2VydC5jb20wTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jYWNlcnRzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURUaW1lc3RhbXBpbmdDQS5jcnQw
# DQYJKoZIhvcNAQELBQADggEBAEgc3LXpmiO85xrnIA6OZ0b9QnJRdAojR6OrktIl
# xHBZvhSg5SeBpU0UFRkHefDRBMOG2Tu9/kQCZk3taaQP9rhwz2Lo9VFKeHk2eie3
# 8+dSn5On7UOee+e03UEiifuHokYDTvz0/rdkd2NfI1Jpg4L6GlPtkMyNoRdzDfTz
# ZTlwS/Oc1np72gy8PTLQG8v1Yfx1CAB2vIEO+MDhXM/EEXLnG2RJ2CKadRVC9S0y
# OIHa9GCiurRS+1zgYSQlT7LfySmoc0NR2r1j1h9bm/cuG08THfdKDXF+l7f0P4Tr
# weOjSaH6zqe/Vs+6WXZhiV9+p7SOZ3j5NpjhyyjaW4emii8wggUwMIIEGKADAgEC
# AhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEw
# MjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV
# BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEi
# MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7
# RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p
# 0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj
# 6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grk
# V7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHy
# DxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMB
# AAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAT
# BgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGG
# GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2Nh
# Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCB
# gQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgG
# CmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu
# Y29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1
# DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQEL
# BQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q
# 3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/
# kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dc
# IFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6
# dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT
# +hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggUxMIIEGaADAgECAhAKoSXW1jIbfkHk
# Bdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE
# aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMT
# G0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0z
# MTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0
# IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEB
# AQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5
# fU1ofue2oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb
# 6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU
# 46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM/fDqR9mI
# UF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfx
# FwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAd
# BgNVHQ4EFgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMC
# AYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j
# cnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYDVR0gBEkw
# RzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj
# ZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLp
# UYdWac3v3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQd
# aq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC
# 4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3CzddWThZN+
# tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6H
# USHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIv
# IjayS6JKldj1po5SMIIFPTCCBCWgAwIBAgIQBNXcH0jqydhSALrNmpsqpzANBgkq
# hkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT
# SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDYyNjAwMDAwMFoX
# DTIzMDcwNzEyMDAwMFowejELMAkGA1UEBhMCUEwxEjAQBgNVBAgMCcWabMSFc2tp
# ZTERMA8GA1UEBxMIS2F0b3dpY2UxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlz
# IEVWT1RFQzEhMB8GA1UEAwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7KB3iyBrhkLUbbFe9qxhKKPBYqD
# Bqlnr3AtpZplkiVjpi9dMZCchSeT5ODsShPuZCIxJp5I86uf8ibo3vi2S9F9AlfF
# jVye3dTz/9TmCuGH8JQt13ozf9niHecwKrstDVhVprgxi5v0XxY51c7zgMA2g1Ub
# +3tii0vi/OpmKXdL2keNqJ2neQ5cYly/GsI8CREUEq9SZijbdA8VrRF3SoDdsWGf
# 3tZZzO6nWn3TLYKQ5/bw5U445u/V80QSoykszHRivTj+H4s8ABiforhi0i76beA6
# Ea41zcH4zJuAp48B4UhjgRDNuq8IzLWK4dlvqrqCBHKqsnrF6BmBrv+BXQIDAQAB
# o4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0O
# BBYEFBixNSfoHFAgJk4JkDQLFLRNlJRmMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE
# DDAKBggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2Ny
# bDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUw
# QzA3BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl
# cnQuY29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNp
# Z25pbmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAmr1s
# z4lsLARi4wG1eg0B8fVJFowtect7SnJUrp6XRnUG0/GI1wXiLIeow1UPiI6uDMsR
# XPHUF/+xjJw8SfIbwava2eXu7UoZKNh6dfgshcJmo0QNAJ5PIyy02/3fXjbUREHI
# NrTCvPVbPmV6kx4Kpd7KJrCo7ED18H/XTqWJHXa8va3MYLrbJetXpaEPpb6zk+l8
# Rj9yG4jBVRhenUBUUj3CLaWDSBpOA/+sx8/XB9W9opYfYGb+1TmbCkhUg7TB3gD6
# o6ESJre+fcnZnPVAPESmstwsT17caZ0bn7zETKlNHbc1q+Em9kyBjaQRcEQoQQNp
# ezQug9ufqExx6lHYDjGCBFwwggRYAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYD
# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAv
# BgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EC
# EATV3B9I6snYUgC6zZqbKqcwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAI
# oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB
# CzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFB4vinDzK2BkcexSpsg9
# EIFoSudKMA0GCSqGSIb3DQEBAQUABIIBAGzHOA1+azSk2fct8Pckl50TrYGFaZ2m
# oCVCYnRfj96Ja5YVQeNI9IYGTGWepag2xB7GuyMy+bPJru8yLm3JtEViZiy91B1x
# TBKdcyWbSf2IguazQ6R03zK+KJNZ+rRd7NHmuyHv8P6l47z1kPUIXJdtdqcIgY36
# wBjeaUVQOhq7kLP2DwHKQQpasfLWyDkKso0XKfBoSy8Acdk0dmQf7lv2j+zMNx0b
# aWtOR/6ABpex02aLs5FF74Xlo8+w7W2u6FrplNv1zAOHW0LPb0yPy6zUaCfd7shg
# MG3W5VM2QkZLR0IVH3MLLRsC9H+qaeAgheQCa4WH8v/VlgEYgfKts8+hggIwMIIC
# LAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNV
# BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8G
# A1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQQIQ
# DUJK4L46iP9gQCHOFADw3TANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIxMDUyMzA5NDQyNlowLwYJKoZI
# hvcNAQkEMSIEIMO27c7u3FS2bhkK+P9sRL5fcidscBjZnWIU6lw6Eb8bMA0GCSqG
# SIb3DQEBAQUABIIBAD53nIBQ3v1S/vkmlyVy9cOV3zY+OODPxBGi0M+3zKtzskq+
# Jd2WvwhCOfmB/lZtB3F3eVrw65WKB+4KrBu2RtW0aGZAeRsyOpNZ8MsTyaaOC3Qs
# Qx8/tOEHu1sqkwLrmvMltHiNbHbNDXpbitoAYhaC2Gg3rzU3bsAZna5pq54yPFAn
# XV36dDQjpFn2gDhf5+ZbYMMvfSfTCyQQ13JEfKqBYzYPhxws7ERp3NB0vBguUdVn
# 3eQLDm2HT49f0rKJcNiz4uIx/s2XFZuwJHlXqqZG5gEUfojzEY3s5pm3xaUvXfxk
# /CeFuVqMWndk7EUoqZpSb8HRLhD1/vVCEHR21WU=
# SIG # End signature block