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) 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) { $Hashtable.Remove($Key) } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if ([string]::IsNullOrEmpty($Hashtable[$Key])) { $Hashtable.Remove($Key) } } } else { if ([string]::IsNullOrEmpty($Hashtable[$Key])) { $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) } } 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 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 # 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) { #$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) } } 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: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, [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 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 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 ) $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 #[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 ) [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 = @{ 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: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 @('Close-PDF', 'Convert-PDFToText', 'Get-PDF', 'Get-PDFConstantAction', 'Get-PDFConstantColor', 'Get-PDFConstantFont', 'Get-PDFConstantPageSize', 'Get-PDFConstantVersion', 'Get-PDFDetails', '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 @() # SIG # Begin signature block # MIIgQAYJKoZIhvcNAQcCoIIgMTCCIC0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUPj57mGtM8CYU/4D9MfHQw5aL # q9OgghtvMIIDtzCCAp+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 # 8jCCBTAwggQYoAMCAQICEAQJGBtf1btmdVNDtW+VUAgwDQYJKoZIhvcNAQELBQAw # ZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBS # b290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg # U2lnbmluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPjTsxx/ # DhGvZ3cH0wsxSRnP0PtFmbE620T1f+Wondsy13Hqdp0FLreP+pJDwKX5idQ3Gde2 # qvCchqXYJawOeSg6funRZ9PG+yknx9N7I5TkkSOWkHeC+aGEI2YSVDNQdLEoJrsk # acLCUvIUZ4qJRdQtoaPpiCwgla4cSocI3wz14k1gGL6qxLKucDFmM3E+rHCiq85/ # 6XzLkqHlOzEcz+ryCuRXu0q16XTmK/5sy350OTYNkO/ktU6kqepqCquE86xnTrXE # 94zRICUj6whkPlKWwfIPEvTFjg/BougsUfdzvL2FsWKDc0GCB+Q4i2pzINAPZHM8 # np+mM6n9Gd8lk9ECAwEAAaOCAc0wggHJMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD # VR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHkGCCsGAQUFBwEBBG0w # azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF # BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk # SURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js # ME8GA1UdIARIMEYwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczov # L3d3dy5kaWdpY2VydC5jb20vQ1BTMAoGCGCGSAGG/WwDMB0GA1UdDgQWBBRaxLl7 # KgqjpepxA8Bg+S32ZXUOWDAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823I # DzANBgkqhkiG9w0BAQsFAAOCAQEAPuwNWiSz8yLRFcgsfCUpdqgdXRwtOhrE7zBh # 134LYP3DPQ/Er4v97yrfIFU3sOH20ZJ1D1G0bqWOWuJeJIFOEKTuP3GOYw4TS63X # X0R58zYUBor3nEZOXP+QsRsHDpEV+7qvtVHCjSSuJMbHJyqhKSgaOnEoAjwukaPA # JRHinBRHoXpoaK+bp1wgXNlxsQyPu6j4xRJon89Ay0BEpRPw5mQMJQhCMrI2iiQC # /i9yfhzXSUWW6Fkd6fp0ZGuy62ZD2rOwjNXpDd32ASDOmTFjPQgaGLOBm0/GkxAG # /AeB+ova+YJJ92JuoVP6EpQYhS6SkepobEQysmah5xikmmRR7zCCBT0wggQloAMC # AQICEATV3B9I6snYUgC6zZqbKqcwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQTAeFw0yMDA2MjYwMDAwMDBaFw0yMzA3MDcxMjAwMDBaMHoxCzAJBgNV # BAYTAlBMMRIwEAYDVQQIDAnFmmzEhXNraWUxETAPBgNVBAcTCEthdG93aWNlMSEw # HwYDVQQKDBhQcnplbXlzxYJhdyBLxYJ5cyBFVk9URUMxITAfBgNVBAMMGFByemVt # eXPFgmF3IEvFgnlzIEVWT1RFQzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC # ggEBAL+ygd4sga4ZC1G2xXvasYSijwWKgwapZ69wLaWaZZIlY6YvXTGQnIUnk+Tg # 7EoT7mQiMSaeSPOrn/Im6N74tkvRfQJXxY1cnt3U8//U5grhh/CULdd6M3/Z4h3n # MCq7LQ1YVaa4MYub9F8WOdXO84DANoNVG/t7YotL4vzqZil3S9pHjaidp3kOXGJc # vxrCPAkRFBKvUmYo23QPFa0Rd0qA3bFhn97WWczup1p90y2CkOf28OVOOObv1fNE # EqMpLMx0Yr04/h+LPAAYn6K4YtIu+m3gOhGuNc3B+MybgKePAeFIY4EQzbqvCMy1 # iuHZb6q6ggRyqrJ6xegZga7/gV0CAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrE # uXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBQYsTUn6BxQICZOCZA0CxS0TZSU # ZjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAw # bjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1j # cy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYB # BQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGE # BggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0 # LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQC # MAAwDQYJKoZIhvcNAQELBQADggEBAJq9bM+JbCwEYuMBtXoNAfH1SRaMLXnLe0py # VK6el0Z1BtPxiNcF4iyHqMNVD4iOrgzLEVzx1Bf/sYycPEnyG8Gr2tnl7u1KGSjY # enX4LIXCZqNEDQCeTyMstNv931421ERByDa0wrz1Wz5lepMeCqXeyiawqOxA9fB/ # 106liR12vL2tzGC62yXrV6WhD6W+s5PpfEY/chuIwVUYXp1AVFI9wi2lg0gaTgP/ # rMfP1wfVvaKWH2Bm/tU5mwpIVIO0wd4A+qOhEia3vn3J2Zz1QDxEprLcLE9e3Gmd # G5+8xEypTR23NavhJvZMgY2kEXBEKEEDaXs0LoPbn6hMcepR2A4wggZqMIIFUqAD # AgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTAeFw0xNDEw # MjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQK # EwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1wIFJlc3BvbmRl # cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8s+CCNeDg9sYq # 5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7dy4XpX6X51Id0 # iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGfrvP9Enh1DqZb # FP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5uHzu5uc0LzF3 # gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYuRhDIjegEYNu8 # c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJCkawCwO+k8IkR # j3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYG # A1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgB # hv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v # Q1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAA # dABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQA # dQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQA # aQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIA # ZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcA # aABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQA # IABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4A # IABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSME # GDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpNJLZJMp1KKnka # g0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMHcGCCsGAQUF # BwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEG # CCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRB # c3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+GzNNsiaBXJuG # ziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94GAYw3+puxnSR+ # /iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFBpr1i2fAnPTgd # KG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dxnSHdFMoVXZJB # 2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bIo4sKHOWV2q7E # LlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQKOhvjjz3Kr2qN # e9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJKoZIhvcNAQEF # BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ # RCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowYjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMIIB # IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKVpYBzQHDSnlZU # XKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqvy15r7a2wcTHr # zzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/2DDD/6b3+6LN # b3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE7h6z8rwMK5nQ # xl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKvmPv2zkBdXPao # 8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIUYJX9BwSiCQID # AQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIGCCsGAQUFBwMB # BggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCDCCAdIGA1Ud # IASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6 # Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggr # BgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAA # QwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAA # YQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUA # cgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4A # ZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAA # bABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAA # aQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIA # ZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB/wQIMAYBAf8C # AQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e36K+Vw0rZwLN # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90OPKyXGGinJXD # UOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann4+erYs37iy2Q # wsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qqq8+cdkvtX8JL # FuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD7eeSDY2xaYxP # +1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38KiGtSTGDR5V3 # cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcCAQEwgYYwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2ln # bmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzAJBgUrDgMCGgUAoHgwGAYKKwYBBAGC # NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQU/B/YfTSu # 1GoNR2j+g+Wb25RDd68wDQYJKoZIhvcNAQEBBQAEggEABEsEMJDF4dHSxs5HzTQ8 # afVsgMssNaHve2eSat+b60dZCRKbu6zVz5FM6c1fT2BnpWq6IWeZZUb19Vt03ou5 # HuL6NaY/Nd7MlsIOkkevQi6WLp7O5Ok2bVQM8pE+H5Fln8xZUtQFsezI6Z0BfIe4 # I6y9zs1oCcjDuITczkKhBSG8PzyRL7jYF03b5kx5gehRP4kdqOSgtJdU6xfcuM9O # 6wxtxXnieCebxADbDJUPZHGMqCu/LD1a4DEhUjhdjh06UBMXLP/Cv7gFsrg2sCO+ # evlqYzny5E1R2TgR355t7vpOu0iROp9TJXVlHf7YhD7DSOn+RmChCLolaTt06Z/l # 3aGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr # 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc # BgkqhkiG9w0BCQUxDxcNMjAwNzIxMTcyOTE2WjAjBgkqhkiG9w0BCQQxFgQUAoUR # 4mRrRCRFvvtIrkiEUkOi4GQwDQYJKoZIhvcNAQEBBQAEggEAVQJxvCLos/VtW8v/ # a/ax4o+83FsbPCTCA61VfT/U0wSclMl5+CzgBpY3bPlfynR9drOG1BpHKrFXHP/z # 1rz4sWpEK9HRx08LWqSN+XVMtxQpkEFBkXe07YqT2SX/U+Gi2y2otwwte1dcz/VZ # ahT3HSBt8xxbmErPThkVUAXAnEV3tN94yC4kCm/O9C3Fhbyw2H9G8ucoD0DKY81z # 7D5lR67ZBgwkd9BA/TIFZJjShxyYtynIuZNr9nwIADkbdIMK+GFb9eXyGyKBECgI # 2cWOoM55GBBrc9afO2D0jez+ck2lt7Kw6TN2h4vLdoohSetlt2a4oho5jvrOuPEQ # /kcw3g== # SIG # End signature block |