Src/Public/Public.ps1
function BlankLine { <# .SYNOPSIS Initializes a new PScribo blank line object. #> [CmdletBinding()] param ( [Parameter(ValueFromPipeline, Position = 0)] [System.UInt32] $Count = 1 ) process { Write-PScriboMessage -Message $localized.ProcessingBlankLine return (New-PScriboBlankLine @PSBoundParameters) } } function Document { <# .SYNOPSIS Initializes a new PScribo document object. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','pluginName')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo document name [Parameter(Mandatory, Position = 0)] [System.String] $Name, ## PScribo document DSL script block containing Section, Paragraph and/or Table etc. commands. [Parameter(Position = 1)] [System.Management.Automation.ScriptBlock] $ScriptBlock = $(throw $localized.NoScriptBlockProvidedError), ## PScribo document Id [Parameter()] [System.String] $Id = $Name.Replace(' ','') ) process { $pluginName = 'Document' $stopwatch = [Diagnostics.Stopwatch]::StartNew() $pscriboDocument = New-PScriboDocument -Name $Name -Id $Id $pscriboDocument.Properties['Pages']++ $script:currentOrientation = $pscriboDocument.Options['PageOrientation'] ## Call the Document script block foreach ($result in & $ScriptBlock) { ## Ensure we don't have something errant passed down the pipeline (#29) if ($result -is [System.Management.Automation.PSObject]) { if (('Id' -in $result.PSObject.Properties.Name) -and ('Type' -in $result.PSObject.Properties.Name) -and ($result.Type -match '^PScribo.')) { [ref] $null = $pscriboDocument.Sections.Add($result) } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectWarning -f $Name) -IsWarning } } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), $Name) -IsWarning } } Invoke-PScriboSection ## Process IsSectionBreakEnd (for Word plugin) if ($pscriboDocument.Sections.Count -gt 0) { $previousPScriboSection = $pscriboDocument.Sections[0] for ($i = 0; $i -lt $pscriboDocument.Sections.Count; $i++) { $pscriboSection = $pscriboDocument.Sections[$i] if ($pscriboSection.Type -in 'PScribo.Section','PScribo.Paragraph','PScriboTable') { if (($null -ne $pscriboSection.PSObject.Properties['IsSectionBreak']) -and ($pscriboSection.IsSectionBreak)) { if (($previousPScriboSection.Type -in 'PScribo.Paragraph','PScribo.Table') -or ($previousPScriboSection.Sections.Count -eq 0)) { ## Set the last childless section, paragraph or table as the section end $previousPScriboSection.IsSectionBreakEnd = $true } else { ## Set the last child section/paragraph element as the section end Set-PScriboSectionBreakEnd -Section $previousPScriboSection } } $previousPScriboSection = $pscriboSection } } } Write-PScriboMessage -Message ($localized.DocumentProcessingCompleted -f $pscriboDocument.Name) $stopwatch.Stop() if ($stopwatch.Elapsed.TotalSeconds -gt 90) { Write-PScriboMessage -Message ($localized.TotalProcessingTimeMinutes -f $stopwatch.Elapsed.TotalMinutes) } else { Write-PScriboMessage -Message ($localized.TotalProcessingTimeSeconds -f $stopwatch.Elapsed.TotalSeconds) } return $pscriboDocument } } function DocumentOption { <# .SYNOPSIS Initializes a new PScribo global/document options/settings. .NOTES Options are reset upon each invocation. #> [CmdletBinding(DefaultParameterSetName = 'Margin')] [Alias('GlobalOption')] param ( ## Forces document header to be displayed in upper case. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $ForceUppercaseHeader, ## Forces all section headers to be displayed in upper case. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $ForceUppercaseSection, ## Enable section/heading numbering [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $EnableSectionNumbering, ## Default space replacement separator [Parameter(ValueFromPipelineByPropertyName)] [Alias('Separator')] [AllowNull()] [ValidateLength(0,1)] [System.String] $SpaceSeparator, ## Default page top, bottom, left and right margin (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Margin')] [System.UInt16] $Margin = 72, ## Default page top and bottom margins (pt) [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'CustomMargin')] [System.UInt16] $MarginTopAndBottom, ## Default page left and right margins (pt) [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'CustomMargin')] [System.UInt16] $MarginLeftAndRight, ## Default page size [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('A4','Legal','Letter')] [System.String] $PageSize = 'A4', ## Page orientation [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Portrait','Landscape')] [System.String] $Orientation = 'Portrait', ## Default font(s) used for new style definitions [Parameter(ValueFromPipelineByPropertyName)] [System.String[]] $DefaultFont = @('Calibri','Candara','Segoe','Segoe UI','Optima','Arial','Sans-Serif') ) process { $localized.DocumentOptions | Write-PScriboMessage if ($SpaceSeparator) { Write-PScriboMessage -Message ($localized.DocumentOptionSpaceSeparator -f $SpaceSeparator) $pscriboDocument.Options['SpaceSeparator'] = $SpaceSeparator } if ($ForceUppercaseHeader) { $localized.DocumentOptionUppercaseHeadings | Write-PScriboMessage $pscriboDocument.Options['ForceUppercaseHeader'] = $true $pscriboDocument.Name = $pscriboDocument.Name.ToUpper() } if ($ForceUppercaseSection) { $localized.DocumentOptionUppercaseSections | Write-PScriboMessage $pscriboDocument.Options['ForceUppercaseSection'] = $true } if ($EnableSectionNumbering) { $localized.DocumentOptionSectionNumbering | Write-PScriboMessage $pscriboDocument.Options['EnableSectionNumbering'] = $true } if ($DefaultFont) { Write-PScriboMessage -Message ($localized.DocumentOptionDefaultFont -f ([System.String]::Join(', ', $DefaultFont))) $pscriboDocument.Options['DefaultFont'] = $DefaultFont } if ($PSCmdlet.ParameterSetName -eq 'CustomMargin') { if ($MarginTopAndBottom -eq 0) { $MarginTopAndBottom = 72 } if ($MarginLeftAndRight -eq 0) { $MarginTopAndBottom = 72 } $pscriboDocument.Options['MarginTop'] = ConvertTo-Mm -Point $MarginTopAndBottom $pscriboDocument.Options['MarginBottom'] = $pscriboDocument.Options['MarginTop'] $pscriboDocument.Options['MarginLeft'] = ConvertTo-Mm -Point $MarginLeftAndRight $pscriboDocument.Options['MarginRight'] = $pscriboDocument.Options['MarginLeft'] } else { $pscriboDocument.Options['MarginTop'] = ConvertTo-Mm -Point $Margin $pscriboDocument.Options['MarginBottom'] = $pscriboDocument.Options['MarginTop'] $pscriboDocument.Options['MarginLeft'] = $pscriboDocument.Options['MarginTop'] $pscriboDocument.Options['MarginRight'] = $pscriboDocument.Options['MarginTop'] } Write-PScriboMessage -Message ($localized.DocumentOptionPageTopMargin -f $pscriboDocument.Options['MarginTop']) Write-PScriboMessage -Message ($localized.DocumentOptionPageRightMargin -f $pscriboDocument.Options['MarginRight']) Write-PScriboMessage -Message ($localized.DocumentOptionPageBottomMargin -f $pscriboDocument.Options['MarginBottom']) Write-PScriboMessage -Message ($localized.DocumentOptionPageLeftMargin -f $pscriboDocument.Options['MarginLeft']) ## Convert page size ($localized.DocumentOptionPageSize -f $PageSize) | Write-PScriboMessage switch ($PageSize) { 'A4' { $pscriboDocument.Options['PageWidth'] = 210.0 $pscriboDocument.Options['PageHeight'] = 297.0 } 'Legal' { $pscriboDocument.Options['PageWidth'] = 215.9 $pscriboDocument.Options['PageHeight'] = 355.6 } 'Letter' { $pscriboDocument.Options['PageWidth'] = 215.9 $pscriboDocument.Options['PageHeight'] = 279.4 } } ## Convert page size ($localized.DocumentOptionPageOrientation -f $Orientation) | Write-PScriboMessage $pscriboDocument.Options['PageOrientation'] = $Orientation $script:currentOrientation = $Orientation ($localized.DocumentOptionPageHeight -f $pscriboDocument.Options['PageHeight']) | Write-PScriboMessage ($localized.DocumentOptionPageWidth -f $pscriboDocument.Options['PageWidth']) | Write-PScriboMessage } } function Export-Document { <# .SYNOPSIS Exports a PScribo document object to one or more output formats. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock','')] [OutputType([System.IO.FileInfo])] param ( ## PScribo document object [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [System.Management.Automation.PSObject] $Document, ## Output file path [Parameter(Position = 2, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Path = (Get-Location -PSProvider FileSystem), ## PScribo document export option [Parameter(Position = 3, ValueFromPipelineByPropertyName)] [AllowNull()] [System.Collections.Hashtable] $Options, [Parameter(Position = 4, ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $PassThru ) DynamicParam { ## Adds a dynamic -Format parameter that returns the available plugins $parameterAttribute = New-Object -TypeName 'System.Management.Automation.ParameterAttribute' $parameterAttribute.ParameterSetName = '__AllParameterSets' $parameterAttribute.Mandatory = $true $parameterAttribute.Position = 1 $attributeCollection = New-Object -TypeName 'System.Collections.ObjectModel.Collection[System.Attribute]' $attributeCollection.Add($parameterAttribute) $validateSetAttribute = New-Object -TypeName 'System.Management.Automation.ValidateSetAttribute' -ArgumentList (Get-PScriboPlugin) $attributeCollection.Add($validateSetAttribute) $runtimeParameter = New-Object -TypeName 'System.Management.Automation.RuntimeDefinedParameter' -ArgumentList @('Format', [System.String[]], $attributeCollection) $runtimeParameterDictionary = New-Object -TypeName 'System.Management.Automation.RuntimeDefinedParameterDictionary' $runtimeParameterDictionary.Add('Format', $runtimeParameter) return $runtimeParameterDictionary } begin { try { $Path = Resolve-Path $Path -ErrorAction SilentlyContinue; } catch { } if ( $(Test-CharsInPath -Path $Path -SkipCheckCharsInFileNamePart -Verbose:$false) -eq 2 ) { throw $localized.IncorrectCharsInPathError } if (-not (Test-Path $Path -PathType Container)) { ## Check $Path is a directory throw ($localized.InvalidDirectoryPathError -f $Path) } } process { foreach ($f in $PSBoundParameters['Format']) { Write-PScriboMessage -Message ($localized.DocumentInvokePlugin -f $f) -Plugin 'Export'; ## Dynamically generate the output format function name $outputFormat = 'Out-{0}Document' -f $f $outputParams = @{ Document = $Document Path = $Path } if ($PSBoundParameters.ContainsKey('Options')) { $outputParams['Options'] = $Options } $fileInfo = & $outputFormat @outputParams Write-PScriboMessage -Message ($localized.DocumentExportPluginComplete -f $f) -Plugin 'Export' if ($PassThru) { Write-Output -InputObject $fileInfo } } } } function Footer { <# .SYNOPSIS Initializes a new PScribo footer object. .NOTES Footer sections can only contain Paragraph and Table elements. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo document script block. [Parameter(ValueFromPipelineByPropertyName, Position = 0)] [ValidateNotNull()] [System.Management.Automation.ScriptBlock] $ScriptBlock = $(throw $localized.NoScriptBlockProvidedError), ## Set the default page footer. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.SwitchParameter] $Default, ## Set the First page footer. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'FirstPage')] [System.Management.Automation.SwitchParameter] $FirstPage, ## PScribo inserts a default blank line between the footer and main docoument. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoSpace, ## Include default footer on the first page. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.SwitchParameter] $IncludeOnFirstPage ) process { Write-PScriboMessage -Message $localized.ProcessingFooterStarted $pscriboFooter = New-PScriboHeaderFooter -Footer if (-not $NoSpace) { ## Add a blank line before the footer text [ref] $null = $pscriboFooter.Sections.Add((New-PScriboBlankLine)) } foreach ($result in & $ScriptBlock) { ## Headers/footers only support paragraphs and tables if ($result -is [System.Management.Automation.PSObject]) { if (('Id' -in $result.PSObject.Properties.Name) -and ('Type' -in $result.PSObject.Properties.Name) -and ($result.Type -in 'PScribo.Paragraph','PScribo.Table','PScribo.LineBreak')) { [ref] $null = $pscriboFooter.Sections.Add($result) } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectWarning -f 'Footer') -IsWarning } } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), 'Footer') -IsWarning } } if ($FirstPage -or $IncludeOnFirstPage) { if ($pscriboDocument.Footer.HasFirstPageFooter -eq $true) { Write-PScriboMessage $localized.FirstPageFooterOverwriteWarning -IsWarning } $pscriboDocument.Footer.HasFirstPageFooter = $true $pscriboDocument.Footer.FirstPageFooter = $pscriboFooter } if ($Default) { if ($pscriboDocument.Footer.HasDefaultFooter -eq $true) { Write-PScriboMessage $localized.DefaultFooterOverwriteWarning -IsWarning } $pscriboDocument.Footer.HasDefaultFooter = $true $pscriboDocument.Footer.DefaultFooter = $pscriboFooter } Write-PScriboMessage -Message $localized.ProcessingFooterCompleted } } function Header { <# .SYNOPSIS Initializes a new PScribo header object. .NOTES Header sections can only contain Paragraph and Table elements. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo document script block. [Parameter(ValueFromPipelineByPropertyName, Position = 0)] [ValidateNotNull()] [System.Management.Automation.ScriptBlock] $ScriptBlock = $(throw $localized.NoScriptBlockProvidedError), ## Set the default page header. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.SwitchParameter] $Default, ## Set the First page header. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'FirstPage')] [System.Management.Automation.SwitchParameter] $FirstPage, ## PScribo inserts a default blank line between the header and main docoument. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoSpace, ## Include default header on the first page. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.SwitchParameter] $IncludeOnFirstPage ) process { Write-PScriboMessage -Message $localized.ProcessingHeaderStarted $pscriboHeader = New-PScriboHeaderFooter -Header foreach ($result in & $ScriptBlock) { ## Headers/footers only support paragraphs and tables if ($result -is [System.Management.Automation.PSObject]) { if (('Id' -in $result.PSObject.Properties.Name) -and ('Type' -in $result.PSObject.Properties.Name) -and ($result.Type -in 'PScribo.Paragraph','PScribo.Table','PScribo.LineBreak')) { [ref] $null = $pscriboHeader.Sections.Add($result) } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectWarning -f 'Header') -IsWarning } } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), 'Header') -IsWarning } } if (-not $NoSpace) { ## Add a blank line after the header text [ref] $null = $pscriboHeader.Sections.Add((New-PScriboBlankLine)) } if ($FirstPage -or $IncludeOnFirstPage) { if ($pscriboDocument.Header.HasFirstPageHeader -eq $true) { Write-PScriboMessage $localized.FirstPageHeaderOverwriteWarning -IsWarning } $pscriboDocument.Header.HasFirstPageHeader = $true $pscriboDocument.Header.FirstPageHeader = $pscriboHeader } if ($Default) { if ($pscriboDocument.Header.HasDefaultHeader -eq $true) { Write-PScriboMessage $localized.DefaultHeaderOverwriteWarning -IsWarning } $pscriboDocument.Header.HasDefaultHeader = $true $pscriboDocument.Header.DefaultHeader = $pscriboHeader } Write-PScriboMessage -Message $localized.ProcessingHeaderCompleted } } function Image { <# .SYNOPSIS Initializes a new PScribo Image object. #> [CmdletBinding(DefaultParameterSetName = 'PathSize')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## Local file path to the image [Parameter(Mandatory, ParameterSetName = 'PathSize')] [Parameter(Mandatory, ParameterSetName = 'PathPercent')] [System.String] $Path, ## Remote image web URL path [Parameter(Mandatory, ParameterSetName = 'UriSize')] [Parameter(Mandatory, ParameterSetName = 'UriPercent')] [System.String] $Uri, ## Base64 encoded image file [Parameter(Mandatory, ParameterSetName = 'Base64Size')] [Parameter(Mandatory, ParameterSetName = 'Base64Percent')] [System.String] $Base64, ## Specifies required the image pixel width [Parameter(ParameterSetName = 'PathSize')] [Parameter(ParameterSetName = 'UriSize')] [Parameter(ParameterSetName = 'Base64Size')] [System.UInt32] $Height, ## Specifies required the image pixel width [Parameter(ParameterSetName = 'PathSize')] [Parameter(ParameterSetName = 'UriSize')] [Parameter(ParameterSetName = 'Base64Size')] [System.UInt32] $Width, ## Specifies the required scaling percentage [Parameter(Mandatory, ParameterSetName = 'PathPercent')] [Parameter(Mandatory, ParameterSetName = 'UriPercent')] [Parameter(Mandatory, ParameterSetName = 'Base64Percent')] [System.UInt32] $Percent, ## Image alignment [Parameter()] [ValidateSet('Left','Center','Right')] [System.String] $Align = 'Left', ## Accessibility image description [Parameter(Mandatory, ParameterSetName = 'Base64Size')] [Parameter(Mandatory, ParameterSetName = 'Base64Percent')] [Parameter(ParameterSetName = 'PathSize')] [Parameter(ParameterSetName = 'PathPercent')] [Parameter(ParameterSetName = 'UriSize')] [Parameter(ParameterSetName = 'UriPercent')] [System.String] $Text, ## Internal image Id [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Id = [System.Guid]::NewGuid().ToString() ) process { if ($PSBoundParameters.ContainsKey('Path')) { $Uri = Resolve-ImageUri -Path $Path $null = $PSBoundParameters.Remove('Path') $PSBoundParameters['Uri'] = $Uri } elseif ($PSBoundParameters.ContainsKey('Uri')) { $Uri = Resolve-ImageUri -Path $Uri } elseif ($PSBoundParameters.ContainsKey('Base64')) { $Uri = Resolve-ImageUri -Path 'about:blank' } if (-not ($PSBoundParameters.ContainsKey('Text'))) { $Text = $Uri } $imageDisplayName = $Text if ($Text.Length -gt 40) { $imageDisplayName = '{0}[..]' -f $Text.Substring(0, 36) } Write-PScriboMessage -Message ($localized.ProcessingImage -f $ImageDisplayName) return (New-PScriboImage @PSBoundParameters) } } function Item { <# .SYNOPSIS Initializes a new PScribo list Item object. #> [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## List item text. [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [ValidateNotNull()] [System.String] $Text, ## List item style Name/Id reference. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Style')] [System.String] $Style, ## Override the bold style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [System.Management.Automation.SwitchParameter] $Bold, ## Override the italic style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [System.Management.Automation.SwitchParameter] $Italic, ## Override the underline style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [System.Management.Automation.SwitchParameter] $Underline, ## Override the font name(s) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [ValidateNotNullOrEmpty()] [System.String[]] $Font, ## Override the font size (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [AllowNull()] [System.UInt16] $Size = $null, ## Override the font color/colour [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [AllowNull()] [System.String] $Color = $null ) begin { $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '<ScriptBlock>' } if ($psCallStack[1].FunctionName -ne 'List<Process>') { throw $localized.ItemRootError } } process { return (New-PScriboItem @PSBoundParameters) } } function LineBreak { <# .SYNOPSIS Initializes a new PScribo line break object. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Id = [System.Guid]::NewGuid().ToString() ) process { Write-PScriboMessage -Message $localized.ProcessingLineBreak return (New-PScriboLineBreak @PSBoundParameters) } } function List { <# .SYNOPSIS Initializes a new PScribo bulleted or numbered List object. #> [CmdletBinding(DefaultParameterSetName = 'BulletItem')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## List item(s). [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ItemBullet')] [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ItemNumbered')] [ValidateNotNull()] [System.String[]] $Item, ## PScribo nested list/items. [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName, ParameterSetName = 'ListBullet')] [Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName, ParameterSetName = 'ListNumbered')] [ValidateNotNull()] [System.Management.Automation.ScriptBlock] $ScriptBlock, ## Display name used in verbose output when processing. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Name, ## List style Name/Id reference. [Parameter(Position = 1, ValueFromPipelineByPropertyName)] [System.String] $Style, ## Numbered list. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ItemNumbered')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ListNumbered')] [System.Management.Automation.SwitchParameter] $Numbered, ## Numbered list style. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ItemNumbered')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ListNumbered')] [System.String] $NumberStyle, ## Bullet list style. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ItemBullet')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ListBullet')] [ValidateSet('Circle', 'Dash', 'Disc', 'Square')] [System.String] $BulletStyle = 'Disc' ) begin { $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '<ScriptBlock>' } if ($psCallStack[1].FunctionName -notin 'List<Process>','Document<Process>','Section<Process>') { Write-Warning $psCallStack[1].FunctionName throw $localized.ListRootError } } process { $null = $PSBoundParameters.Remove('ScriptBlock') $null = $PSBoundParameters.Remove('Item') $pscriboList = New-PScriboList @PSBoundParameters if ($PSCmdlet.ParameterSetName -in 'ItemBullet','ItemNumbered') { foreach ($listItem in $Item) { $pscriboListItem = New-PScriboItem -Text $listItem [ref] $null = $pscriboList.Items.Add($pscriboListItem) } } elseif ($PSCmdlet.ParameterSetName -in 'ListBullet','ListNumbered') { foreach ($result in & $ScriptBlock) { ## Ensure we don't have something errant passed down the pipeline (#29) if ($result -is [System.Management.Automation.PSObject]) { if (('Id' -in $result.PSObject.Properties.Name) -and ('Type' -in $result.PSObject.Properties.Name) -and ($result.Type -match '^PScribo.')) { [ref] $null = $pscriboList.Items.Add($result) } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectWarning -f $Name) -IsWarning } } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), $Name) -IsWarning } } } ## Only process list levels on the root list object if ($psCallStack[1].FunctionName -in 'Document<Process>','Section<Process>') { Write-PScriboMessage -Message ($localized.ProcessingList -f $pscriboList.Name) $hasItem = $false $itemNumber = 0 foreach ($listItem in $pscriboList.Items) { if ($listItem.Type -eq 'PScribo.Item') { $itemNumber++ $listItem.Level = 1 $listItem.Index = $itemNumber $listItem.Number = $itemNumber.ToString() $hasItem = $true } elseif ($listItem.Type -eq 'PScribo.List') { if ($hasItem) { $pscriboList.IsMultiLevel = $true Invoke-PScriboListLevel -List $listItem -Number $itemNumber.ToString() } else { Write-PScriboMessage -Message $localized.NoPriorListItemWarning -IsWarning } } } ## Store lists for Word numbering.xml $pscriboDocument.Properties['Lists']++ $pscriboList.Number = $pscriboDocument.Properties['Lists'] [ref] $null = $pscriboDocument.Lists.Add($pscriboList) ## Return list reference $pscriboListReference = New-PScriboListReference -Name $pscriboList.Name -Number $pscriboList.Number return $pscriboListReference } else { return $pscriboList } } } function NumberStyle { <# .SYNOPSIS Defines a new PScribo numbered list formatting style. .DESCRIPTION Creates a number list formatting style that can be applied to the PScribo 'List' keyword. .NOTES Not all plugins support all options. #> [CmdletBinding(DefaultParameterSetName = 'Predefined')] param ( ## Table Style name/id [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Predefined')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Custom')] [ValidateNotNullOrEmpty()] [Alias('Name')] [System.String] $Id, ## NOTE: Only supported in Text, Html and Word output. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')] [ValidateSet('Number','Letter','Roman')] [System.String] $Format, ## Custom number 'XYZ-###' NOTE: Only supported in Text and Word output. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')] [System.String] $Custom, ## Number format suffix, e.g. '.' or ')'. NOTE: Only supported in text and Word output [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')] [ValidateLength(1, 1)] [System.String] $Suffix = '.', ## Only applicable to 'Letter' and 'Roman' formats [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')] [System.Management.Automation.SwitchParameter] $Uppercase, ## Set as default table style. NOTE: Cannot set custom styles as default as they're not supported by all plugins. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')] [System.Management.Automation.SwitchParameter] $Default, ## Number alignment. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')] [ValidateSet('Left', 'Right')] [System.String] $Align = 'Right', ## Override the default Word indentation level. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')] [System.Int32] $Indent, ## Override the default Word hanging indentation level. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Predefined')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Custom')] [System.Int32] $Hanging ) process { Write-PScriboMessage -Message ($localized.ProcessingNumberStyle -f $Id) Add-PScriboNumberStyle @PSBoundParameters } } function PageBreak { <# .SYNOPSIS Creates a PScribo page break object. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Id = [System.Guid]::NewGuid().ToString() ) process { Write-PScriboMessage -Message $localized.ProcessingPageBreak return (New-PScriboPageBreak -Id $Id) } } function Paragraph { <# .SYNOPSIS Initializes a new PScribo paragraph object. #> [CmdletBinding(DefaultParameterSetName = 'Legacy')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo paragraph run script block. [Parameter(Mandatory, Position = 0, ParameterSetName = 'ParagraphRun')] [ValidateNotNull()] [System.Management.Automation.ScriptBlock] $ScriptBlock, ## Paragraph Id and Xml element name [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Legacy')] [ValidateNotNullOrEmpty()] [System.String] $Name, ## Paragraph text. If empty $Name/Id will be used. [Parameter(ValueFromPipelineByPropertyName, Position = 1, ParameterSetName = 'Legacy')] [AllowNull()] [System.String] $Text = $null, ## Output value override, i.e. for Xml elements. If empty $Text will be used. [Parameter(ValueFromPipelineByPropertyName, Position = 2, ParameterSetName = 'Legacy')] [AllowNull()] [System.String] $Value = $null, ## Paragraph style Name/Id reference. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ParagraphRun')] [AllowNull()] [System.String] $Style = $null, ## DEPRECATED - Use paragraph runs (Text) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [System.Management.Automation.SwitchParameter] $NoNewLine, ## Override the bold style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [System.Management.Automation.SwitchParameter] $Bold, ## Override the italic style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [System.Management.Automation.SwitchParameter] $Italic, ## Override the underline style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [System.Management.Automation.SwitchParameter] $Underline, ## Override the font name(s) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [ValidateNotNullOrEmpty()] [System.String[]] $Font, ## Override the font size (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [AllowNull()] [System.UInt16] $Size = $null, ## Override the font color/colour [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [AllowNull()] [System.String] $Color = $null, ## Tab indent [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Legacy')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'ParagraphRun')] [ValidateRange(0,10)] [System.Int32] $Tabs = 0 ) process { if ($PSCmdlet.ParameterSetName -eq 'Legacy') { $paragraphId = $Name $paragraphDisplayName = $Name if ($Name.Length -gt 40) { $paragraphId = $Name.Substring(0,40) $paragraphDisplayName = '{0}[..]' -f$Name.Substring(0,36) } Write-PScriboMessage -Message ($localized.ProcessingParagraph -f $paragraphDisplayName) if ($NoNewLine -eq $true) { Write-PScriboMessage -Message $localized.NoNewLineDeprecatedWarning -IsWarning } if ($PSBoundParameters.ContainsKey('Value')) { Write-PScriboMessage -Message $localized.ValueParameterRemovedWarning -IsWarning } ## Create paragraph $newPScriboParagraphParams = @{ Id = $paragraphId ScriptBlock = { } Tabs = $Tabs } if ($PSBoundParameters.ContainsKey('Style')) { $newPScriboParagraphParams['Style'] = $Style } $paragraph = New-PScriboParagraph @newPScriboParagraphParams ## Create a single run $paragraphRunText = $Name if ($PSBoundParameters.ContainsKey('Text')) { $paragraphRunText = $Text } $null = $PSBoundParameters.Remove('Name') $null = $PSBoundParameters.Remove('Text') $null = $PSBoundParameters.Remove('Value') $null = $PSBoundParameters.Remove('Style') $null = $PSBoundParameters.Remove('Tabs') $null = $PSBoundParameters.Remove('NoNewLine') $paragraphRun = New-PScriboParagraphRun -Text $paragraphRunText @PSBoundParameters $paragraphRun.IsParagraphRunEnd = $true $null = $paragraph.Sections.Add($paragraphRun) return $paragraph } elseif ($PSCmdlet.ParameterSetName -eq 'ParagraphRun') { Write-PScriboMessage -Message ($localized.ProcessingParagraphRunsStarted) $paragraph = New-PScriboParagraph @PSBoundParameters foreach ($result in & $ScriptBlock) { ## Ensure we don't have something errant passed down the pipeline if ($result -is [System.Management.Automation.PSObject]) { if (('Type' -in $result.PSObject.Properties.Name) -and ($result.Type -eq 'PScribo.ParagraphRun')) { [ref] $null = $paragraph.Sections.Add($result) } elseif (('Type' -in $result.PSObject.Properties.Name) -and ($result.Type -match '^PScribo\.')) { Write-PScriboMessage -Message ($localized.UnsupportedPScriboTypeWarning -f $result.Type, 'Paragraph') -IsWarning } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), 'Paragraph') -IsWarning } } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), 'Paragraph') -IsWarning } } if ($paragraph.Sections.Count -gt 0) { ## Set the paragraph Id to the text of the first run if ($paragraph.Sections[0].Text.Length -gt 40) { $paragraphDisplayName = '{0}[..]' -f $paragraph.Sections[0].Text.Substring(0,36) } else { $paragraphDisplayName = $paragraph.Sections[0].Text } $paragraph.Id = $paragraphDisplayName $paragraph.Sections[-1].IsParagraphRunEnd = $true } Write-PScriboMessage -Message ($localized.ProcessingParagraphRunsCompleted) return $paragraph } Write-PScriboMessage -Message ($localized.ProcessingParagraph -f $paragraphDisplayName) return (New-PScriboParagraph @PSBoundParameters) } } function Section { <# .SYNOPSIS Initializes a new PScribo section object. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo section heading/name. [Parameter(Mandatory, Position = 0)] [System.String] $Name, ## PScribo document script block. [Parameter(Position = 1)] [ValidateNotNull()] [System.Management.Automation.ScriptBlock] $ScriptBlock = $(throw $localized.NoScriptBlockProvidedError), ## PScribo style applied to document section. [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()] [System.String] $Style = $null, ## Section is excluded from TOC/section numbering. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $ExcludeFromTOC, ## Tab indent [Parameter()] [ValidateRange(0,10)] [System.Int32] $Tabs = 0, ## Section orientation [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Portrait','Landscape')] [System.String] $Orientation ) process { Write-PScriboMessage -Message ($localized.ProcessingSectionStarted -f $Name) $newPScriboSectionParams = @{ Name = $Name Style = $Style IsExcluded = $ExcludeFromTOC Tabs = $Tabs } if ($PSBoundParameters.ContainsKey('Orientation')) { $newPScriboSectionParams['Orientation'] = $Orientation } $pscriboSection = New-PScriboSection @newPScriboSectionParams foreach ($result in & $ScriptBlock) { ## Ensure we don't have something errant passed down the pipeline (#29) if ($result -is [System.Management.Automation.PSObject]) { if (('Id' -in $result.PSObject.Properties.Name) -and ('Type' -in $result.PSObject.Properties.Name) -and ($result.Type -match '^PScribo.')) { [ref] $null = $pscriboSection.Sections.Add($result) } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectWarning -f $Name) -IsWarning } } else { Write-PScriboMessage -Message ($localized.UnexpectedObjectTypeWarning -f $result.GetType(), $Name) -IsWarning } } Write-PScriboMessage -Message ($localized.ProcessingSectionCompleted -f $Name) return $pscriboSection; } } function Set-Style { <# .SYNOPSIS Sets the style for an individual table row or cell. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidMultipleTypeAttributes','')] [OutputType([System.Object])] param ( ## PSCustomObject to apply the style to [Parameter(Mandatory, ValueFromPipeline)] [System.Object[]] [Ref] $InputObject, ## PScribo style Id to apply [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $Style, ## Property name(s) to apply the selected style to. Leave blank to apply the style to the entire row. [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String[]] $Property = '', ## Passes the modified object back to the pipeline [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $PassThru ) begin { if (-not (Test-PScriboStyle -Name $Style)) { Write-Error ($localized.UndefinedStyleError -f $Style) return } } process { foreach ($object in $InputObject) { foreach ($p in $Property) { ## If $Property not set, __Style will apply to the whole row. $propertyName = '{0}__Style' -f $p $object | Add-Member -MemberType NoteProperty -Name $propertyName -Value $Style -Force } } if ($PassThru) { return $object } } } function Style { <# .SYNOPSIS Defines a new PScribo formatting style. .DESCRIPTION Creates a standard format formatting style that can be applied to PScribo document keywords, e.g. a combination of font style, font weight and font size. .NOTES Not all plugins support all options. #> [CmdletBinding()] param ( ## Style name [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## Font size (pt) [Parameter(ValueFromPipelineByPropertyName, Position = 1)] [System.UInt16] $Size = 11, ## Font color/colour [Parameter(ValueFromPipelineByPropertyName)] [Alias('Colour')] [ValidateNotNullOrEmpty()] [System.String] $Color = '000', ## Background color/colour [Parameter(ValueFromPipelineByPropertyName)] [Alias('BackgroundColour')] [ValidateNotNullOrEmpty()] [System.String] $BackgroundColor, ## Bold typeface [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Bold, ## Italic typeface [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Italic, ## Underline typeface [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Underline, ## Text alignment [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Left','Center','Right','Justify')] [System.String] $Align = 'Left', ## Set as default style [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Default, ## Style id [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Id = $Name -Replace(' ',''), ## Font name (array of names for HTML output) [Parameter(ValueFromPipelineByPropertyName)] [System.String[]] $Font, ## Html CSS class id - to override Style.Id in HTML output. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $ClassId = $Id, ## Hide style from UI (Word) [Parameter(ValueFromPipelineByPropertyName)] [Alias('Hide')] [System.Management.Automation.SwitchParameter] $Hidden ) process { Write-PScriboMessage -Message ($localized.ProcessingStyle -f $Id) Add-PScriboStyle @PSBoundParameters } } function Table { <# .SYNOPSIS Defines a new PScribo document table. .PARAMETER Name .PARAMETER InputObject .PARAMETER Hashtable .PARAMETER Columns .PARAMETER ColumnWidths .PARAMETER Headers .PARAMETER Style .PARAMETER Width .PARAMETER Tabs .PARAMETER List .PARAMETER Key .PARAMETER Caption .EXAMPLE Table -Name 'Table 1' -InputObject $(Get-Service) -Columns 'Name','DisplayName','Status' -ColumnWidths 40,20,40 #> [CmdletBinding(DefaultParameterSetName = 'InputObject')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## Table name/Id [Parameter(ValueFromPipelineByPropertyName, Position = 0)] [ValidateNotNullOrEmpty()] [string] $Name = ([System.Guid]::NewGuid().ToString()), # Array of objects [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'InputObject')] [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'InputObjectList')] [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'InputObjectListKey')] [Alias('CustomObject','Object')] [ValidateNotNullOrEmpty()] [System.Object[]] $InputObject, # Array of Hashtables [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Hashtable')] [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'HashtableList')] [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'HashtableListKey')] [ValidateNotNullOrEmpty()] [System.Collections.Specialized.OrderedDictionary[]] $Hashtable, # Array of Hashtable key names or object/PSCustomObject property names to include, in display order. # If not supplied then all Hashtable keys or all object properties will be used. [Parameter(ValueFromPipelineByPropertyName, Position = 1)] [Alias('Properties')] [AllowNull()] [System.String[]] $Columns = $null, ## Column widths as percentages. Total should not exceed 100. [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()] [System.UInt16[]] $ColumnWidths, # Array of custom table header strings in display order. [Parameter(ValueFromPipelineByPropertyName, Position = 2)] [AllowNull()] [System.String[]] $Headers = $null, ## Table style [Parameter(ValueFromPipelineByPropertyName, Position = 3)] [ValidateNotNullOrEmpty()] [System.String] $Style = $pscriboDocument.DefaultTableStyle, ## Table width (%), 0 = Autofit [Parameter(ValueFromPipelineByPropertyName)] [ValidateRange(0,100)] [System.UInt16] $Width = 100, ## Indent table [Parameter(ValueFromPipelineByPropertyName)] [ValidateRange(0,10)] [System.UInt16] $Tabs, ## List view [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'InputObjectList')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'HashtableList')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'InputObjectListKey')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'HashtableListKey')] [System.Management.Automation.SwitchParameter] $List, ## Combine list view based upon the specified key property name [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'InputObjectListKey')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'HashtableListKey')] [System.String] $Key, ## Hide the key name in the table output. Note: Key name will always be displayed in text output. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'InputObjectListKey')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'HashtableListKey')] [System.Management.Automation.SwitchParameter] $HideKey, ## Table caption [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Caption ) begin { Write-Debug ('Using parameter set "{0}".' -f $PSCmdlet.ParameterSetName) [System.Collections.ArrayList] $rows = New-Object -TypeName System.Collections.ArrayList Write-PScriboMessage -Message ($localized.ProcessingTable -f $Name) if ($Headers -and (-not $Columns)) { Write-PScriboMessage -Message $localized.TableHeadersWithNoColumnsWarning -IsWarning $Headers = $Columns } elseif (($null -ne $Columns) -and ($null -ne $Headers)) { ## Check the number of -Headers matches the number of -Properties if ($Headers.Count -ne $Columns.Count) { Write-PScriboMessage -Message $localized.TableHeadersCountMismatchWarning -IsWarning $Headers = $Columns } } if ($ColumnWidths) { $columnWidthsSum = $ColumnWidths | Measure-Object -Sum | Select-Object -ExpandProperty Sum if ($columnWidthsSum -ne 100) { Write-PScriboMessage -Message ($localized.TableColumnWidthSumWarning -f $columnWidthsSum) -IsWarning $ColumnWidths = $null } elseif ($List -and (-not $Key) -and ($ColumnWidths.Count -ne 2)) { Write-PScriboMessage -Message $localized.ListTableColumnCountWarning -IsWarning $ColumnWidths = $null } elseif (($PSCmdlet.ParameterSetName -eq 'Hashtable') -and (-not $List) -and ($Hashtable[0].Keys.Count -ne $ColumnWidths.Count)) { Write-PScriboMessage -Message $localized.TableColumnWidthMismatchWarning -IsWarning $ColumnWidths = $null } elseif (($PSCmdlet.ParameterSetName -eq 'HashtableListKey') -and (($Hashtable.Count + 1) -ne $ColumnWidths.Count)) { Write-PScriboMessage -Message $localized.TableColumnWidthMismatchWarning -IsWarning $ColumnWidths = $null } elseif (($PSCmdlet.ParameterSetName -eq 'InputObject') -and (-not $List)) { ## Columns might not have been passed and there is no object in the pipeline here, so check $Columns is an array. if (($Columns -is [System.Object[]]) -and ($Columns.Count -ne $ColumnWidths.Count)) { Write-PScriboMessage -Message $localized.TableColumnWidthMismatchWarning -IsWarning $ColumnWidths = $null } } } } process { if ($null -eq $Columns) { ## Use all available properties if ($PSCmdlet.ParameterSetName -in 'Hashtable','HashtableList','HashtableListKey') { $Columns = $Hashtable | Select-Object -First 1 -ExpandProperty Keys | Where-Object { $_ -notlike '*__Style' } } elseif ($PSCmdlet.ParameterSetName -in 'InputObject','InputObjectList','InputObjectListKey') { ## Pipeline objects are not available in the begin scriptblock $object = $InputObject | Select-Object -First 1 if ($object -is [System.Management.Automation.PSCustomObject]) { $Columns = $object.PSObject.Properties | Where-Object Name -notlike '*__Style' | Select-Object -ExpandProperty Name } elseif ($object -is [System.Collections.Specialized.OrderedDictionary]) { $Columns = $object.Keys | Where-Object { $_ -notlike '*__Style' } } else { $Columns = Get-Member -InputObject $object -MemberType Properties | Where-Object Name -notlike '*__Style' | Select-Object -ExpandProperty Name } } } if ($PSCmdlet.ParameterSetName -in 'Hashtable','HashtableList','HashtableListKey') { foreach ($nestedHashtable in $Hashtable) { $customObject = New-PScriboTableRow -Hashtable $nestedHashtable [ref] $null = $rows.Add($customObject) } #end foreach nested hashtable entry } elseif ($PSCmdlet.ParameterSetName -in 'InputObject','InputObjectList','InputObjectListKey') { foreach ($object in $InputObject) { if ($object -is [System.Collections.Specialized.OrderedDictionary]) { $customObject = New-PScriboTableRow -Hashtable $object } else { $customObject = New-PScriboTableRow -InputObject $object -Properties $Columns -Headers $Headers } [ref] $null = $rows.Add($customObject) } } } end { if (($PSBoundParameters.ContainsKey('Key')) -and ($PSBoundParameters.ContainsKey('Headers'))) { ## Update the key name to match the rewritten property display name $Key = $Headers[$Columns.IndexOf($Key)] } ## Update the column names as the objects have been have been rewritten with their display names (Headers) if ($Headers) { $Columns = $Headers } $newPScriboTableParams = @{ Name = $Name Columns = $Columns ColumnWidths = $ColumnWidths Rows = $rows List = $List Style = $Style Width = $Width Tabs = $Tabs } if ($PSBoundParameters.ContainsKey('Key')) { $newPScriboTableParams['ListKey'] = $Key $newPScriboTableParams['DisplayListKey'] = -not $HideKey } if ($PSBoundParameters.ContainsKey('Caption')) { $newPScriboTableParams['Caption'] = $Caption } return (New-PScriboTable @newPScriboTableParams) } } function TableStyle { <# .SYNOPSIS Defines a new PScribo table formatting style. .DESCRIPTION Creates a standard table formatting style that can be applied to the PScribo table keyword, e.g. a combination of header and row styles and borders. .NOTES Not all plugins support all options. #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( ## Table Style name/id [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Padding')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = 'Default')] [ValidateNotNullOrEmpty()] [Alias('Name')] [System.String] $Id, ## Header Row Style Id [Parameter(ValueFromPipelineByPropertyName, Position = 1, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, Position = 1, ParameterSetName = 'Default')] [ValidateNotNullOrEmpty()] [System.String] $HeaderStyle = 'Default', ## Row Style Id [Parameter(ValueFromPipelineByPropertyName, Position = 2, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, Position = 2, ParameterSetName = 'Default')] [ValidateNotNullOrEmpty()] [System.String] $RowStyle = 'Default', ## Alternating Row Style Id [Parameter(ValueFromPipelineByPropertyName, Position = 3, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, Position = 3, ParameterSetName = 'Default')] [AllowNull()] [Alias('AlternatingRowStyle')] [System.String] $AlternateRowStyle = $RowStyle, ## Table border size/width (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [AllowNull()] [System.Single] $BorderWidth = 0, ## Table border colour [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [ValidateNotNullOrEmpty()] [Alias('BorderColour')] [System.String] $BorderColor = '000', ## Table cell padding (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [ValidateNotNull()] [System.Single] $Padding, ## Table cell top padding (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [ValidateNotNull()] [System.Single] $PaddingTop = 1.0, ## Table cell left padding (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [ValidateNotNull()] [System.Single] $PaddingLeft = 4.0, ## Table cell bottom padding (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [ValidateNotNull()] [System.Single] $PaddingBottom = 0.0, ## Table cell right padding (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [ValidateNotNull()] [System.Single] $PaddingRight = 4.0, ## Table alignment [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [ValidateSet('Left','Center','Right')] [System.String] $Align = 'Left', ## Table caption prefix [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.String] $CaptionPrefix = 'Table', ## Table caption prefix [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.String] $CaptionStyle = 'Caption', ## Table caption display location. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [ValidateSet('Above', 'Below')] [System.String] $CaptionLocation = 'Below', ## Set as default table style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Padding')] [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.SwitchParameter] $Default ) process { if ($PSBoundParameters.ContainsKey('Padding')) { $PSBoundParameters['PaddingTop'] = $Padding $PSBoundParameters['PaddingLeft'] = $Padding $PSBoundParameters['PaddingBottom'] = $Padding $PSBoundParameters['PaddingRight'] = $Padding $null = $PSBoundParameters.Remove('Padding') } Write-PScriboMessage -Message ($localized.ProcessingTableStyle -f $Id) Add-PScriboTableStyle @PSBoundParameters } } function Text { <# .SYNOPSIS Paragraphs can be made up of text "blocks" (runs) concatenated together to form a body of text. Separating the text into runs permits alteration of styling element within a single paragraph. .NOTES The "Text" block command can only be used within a Paragraph -ScriptBlock { } #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)] [AllowEmptyString()] [System.String] $Text, [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Style')] [System.String] $Style, ## No space applied between this text block and the next [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoSpace, ## Override the bold style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [System.Management.Automation.SwitchParameter] $Bold, ## Override the italic style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [System.Management.Automation.SwitchParameter] $Italic, ## Override the underline style [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [System.Management.Automation.SwitchParameter] $Underline, ## Override the font name(s) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [ValidateNotNullOrEmpty()] [System.String[]] $Font, ## Override the font size (pt) [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [AllowNull()] [System.UInt16] $Size = $null, ## Override the font color/colour [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Inline')] [AllowNull()] [System.String] $Color = $null ) begin { $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '<ScriptBlock>' } if ($psCallStack[1].FunctionName -ne 'Paragraph<Process>') { throw $localized.ParagraphRunRootError } } process { $paragraphRunDisplayName = $Text if ($Text.Length -gt 40) { $paragraphRunDisplayName = '{0}[..]' -f $Text.Substring(0,36) } Write-PScriboMessage -Message ($localized.ProcessingParagraphRun -f $paragraphRunDisplayName) return (New-PScriboParagraphRun @PSBoundParameters) } } function TOC { <# .SYNOPSIS Initializes a new PScribo Table of Contents (TOC) object. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(ValueFromPipeline)] [ValidateNotNullOrEmpty()] [System.String] $Name = 'Contents', [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $ClassId = 'TOC' ) process { Write-PScriboMessage -Message ($localized.ProcessingTOC -f $Name) return (New-PScriboTOC @PSBoundParameters) } } function Write-PScriboMessage { <# .SYNOPSIS Writes PScribo-formatted message to the verbose, warning or debug streams. Output is prefixed with the time and PScribo plugin name. #> [CmdletBinding(DefaultParameterSetName = 'Verbose')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','IsWarning')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','IsDebug')] param ( ## Message to send to the stream [Parameter(Position = 0, ValueFromPipeline, ParameterSetName = 'Verbose')] [Parameter(Position = 0, ValueFromPipeline, ParameterSetName = 'Warning')] [Parameter(Position = 0, ValueFromPipeline, ParameterSetName = 'Debug')] [ValidateNotNullOrEmpty()] [System.String] $Message, ## PScribo plugin name [Parameter(Position = 1, ValueFromPipelineByPropertyName)] [System.String] $Plugin, ## Redirect message to the Warning stream [Parameter(ParameterSetName = 'Warning')] [System.Management.Automation.SwitchParameter] $IsWarning, ## Redirect message to the Debug stream [Parameter(ParameterSetName = 'Debug')] [System.Management.Automation.SwitchParameter] $IsDebug, ## Padding/indent section level [Parameter(ValueFromPipeline, ParameterSetName = 'Verbose')] [Parameter(ValueFromPipeline, ParameterSetName = 'Warning')] [Parameter(ValueFromPipeline, ParameterSetName = 'Debug')] [ValidateNotNullOrEmpty()] [System.UInt16] $Indent ) process { if ([System.String]::IsNullOrEmpty($Plugin)) { ## Attempt to resolve the plugin name from the parent scope if (Test-Path -Path Variable:\pluginName) { $Plugin = Get-Variable -Name pluginName -ValueOnly } else { $Plugin = 'Unknown' } } ## Center plugin name $pluginPaddingSize = [System.Math]::Floor((10 - $Plugin.Length) / 2) $pluginPaddingString = ''.PadRight($pluginPaddingSize) $Plugin = '{0}{1}' -f $pluginPaddingString, $Plugin $Plugin = $Plugin.PadRight(10) $date = Get-Date $sectionLevelPadding = ''.PadRight($Indent) $formattedMessage = '[ {0} ] [{1}] - {2}{3}' -f $date.ToString('HH:mm:ss:fff'), $Plugin, $sectionLevelPadding, $Message switch ($PSCmdlet.ParameterSetName) { 'Warning' { Write-Warning -Message $formattedMessage } 'Debug' { Write-Debug -Message $formattedMessage } Default { Write-Verbose -Message $formattedMessage } } } } # SIG # Begin signature block # MIIuugYJKoZIhvcNAQcCoIIuqzCCLqcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBx/cTkbYPqv84Z # LaHdvstlcyV4HZWYtLI3/bGu+q2AnKCCE6QwggWQMIIDeKADAgECAhAFmxtXno4h # MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV # BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z # ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB # AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z # G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ # anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s # Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL # 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb # BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 # JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c # AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx # YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 # viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL # T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud # EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf # Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk # aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS # PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK # 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB # cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp # 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg # dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri # RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 # 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 # nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 # i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H # EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G # CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C # 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce # 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da # E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T # SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA # FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh # D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM # 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z # 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 # huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY # mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP # /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T # AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD # VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG # A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV # HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU # cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN # BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry # sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL # IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf # Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh # OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh # dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV # 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j # wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH # Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC # XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l # /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW # eE4wggdYMIIFQKADAgECAhAIfHT3o/FeY5ksO94AUhTmMA0GCSqGSIb3DQEBCwUA # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwHhcNMjMxMDE4MDAwMDAwWhcNMjYxMjE2MjM1OTU5WjBgMQsw # CQYDVQQGEwJHQjEPMA0GA1UEBxMGTG9uZG9uMR8wHQYDVQQKExZWaXJ0dWFsIEVu # Z2luZSBMaW1pdGVkMR8wHQYDVQQDExZWaXJ0dWFsIEVuZ2luZSBMaW1pdGVkMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtyhrsCMi6pgLcX5sWY7I09dO # WKweRHfDwW5AN6ffgLCYO9dqWWxvqu95FqnNVRyt1VNzEl3TevKVhRE0GGdirei3 # VqnFFjLDwD2jHhGY8qoSYyfffj/WYq2DkvNI62C3gUwSeP3FeqKRalc2c3V2v4jh # yEYhrgG3nfnWQ/Oq2xzuiCqHy1E4U+IKKDtrXls4JX2Z4J/uAHZIAyKfrcTRQOhZ # R4ZS1cQkeSBU9Urx578rOmxL0si0GAoaYQC49W7OimRelbahxZw/R+f5ch+C1ycU # CpeXLg+bFhpa0+EXnkGidlILZbJiZJn7qvMQTZgipQKZ8nhX3rtJLqTeodPWzcCk # tXQUy0q5fxhR3e6Ls7XQesq/G2yMcCMTCd6eltze0OgabvL6Xkhir5lATHaJtnmw # FlcKzRr1YXK1k1D84hWfKSAdUg8T1O72ztimbqFLg6WoC8M2qqsHcm2DOc9hM3i2 # CWRpegikitRvZ9i1wkA9arGh7+a7UD+nLh2hnGmO06wONLNqABOEn4JOVnNrQ1gY # eDeH9FDx7IYuAvMsfXG9Bo+I97TR2VfwDAx+ccR+UQLON3aQyFZ3BefYnvUu0gUR # ikEAnAS4Jnc3BHizgb0voz0iWRDjFoTTmCmrInCVDGc+5KMy0xyoUwdQvYvRGAWB # 61OCWnXBXbAEPniTZ80CAwEAAaOCAgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hf # EYb7/mF7CIhl9E5CMB0GA1UdDgQWBBRuAv58K4EDYLmb7WNcxt5+r4NfnzA+BgNV # HSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2lj # ZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMD # MIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEu # Y3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYB # BQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv # bTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD # ZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQw # CQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnXMg6efkBrwLIvd1Xmuh0dam # 9FhUtDEj+P5SIqdP/U4veOv66NEQhBHLbW2Dvrdm6ec0HMj9b4e8pt4ylKFzHIPj # fpuRffHVR9JQSx8qpryN6pP49DfCkAYeZGqjY3pGRzd/xQ0cfwcuYbUF+vwVk7tj # q8c93VHCM0rb5M4N2hD1Ze1pvZxtaf9QnFKFzgXZjr02K6bswQc2+n5jFCp7zV1f # KTstyb68rhSJBWKK1tMeFk6a6HXr5buTD3skluC0oyPmD7yAd97r2owjDMEveEso # kADP/z7XQk7wqbwbpi4W6Uju2qHK/9UUsVRF5KTVEAIzVw2V1Aq/Jh3JuSV7b7C1 # 4CghNekltBb+w7YVp8/IFcj7axqnpNQ/+f7RVc3A5hyjV+MkoSwn8Sg7a7hn6SzX # jec/TfRVvWCmG94MQHko+6206uIXrZnmQ6UQYFyOHRlyKDEozzkZhIcVlsZloUjL # 3FZ5V/l8TIIzbc3bkEnu4iByksNvRxI6c5264OLauYlWv50ZUPwXmZ9gX8bs3BqZ # avbGUrOW2PIjtWhtvs4zHhMBCoU1Z0OMvXcF9rUDqefmVCZK46xz3DGKVkDQnxY6 # UWQ3GL60/lEzju4YS99LJVQks2UGmP6LAzlEZ1dnGqi1aQ51OidCYEs39B75PsvO # By2iAR8pBVi/byWBypExghpsMIIaaAIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYD # VQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBH # NCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAh8dPej8V5j # mSw73gBSFOYwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA # oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w # DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg0oCkTDH7XZZ/echJZG6k0l/i # fxdcPaBqtbzT+9AB5qMwDQYJKoZIhvcNAQEBBQAEggIAon+JHIBPGH+sG0JTDob5 # UHvoClLebkVHI/niXnnfPNpUdXa7ScSKI5Y+yM80RVF/LIIGjOWliYXQ6z1q8FkB # U5EdZnrIWyaUCOCcMf4dsq1Z3MZ5cAKdc/4n5LsWgRXxjsCmkBUaFf9iLNJPS+h6 # qJAEpJBqwGZNq9HedYmeEDjfOTZa+A08Pb6u8LPHJBozd6lwsLhig2AXyeEwAWYB # 33smnGyPhQT5XRhUOH+G9BpCEMrZ1EDStXToVOcIgynvzLKui3sps7+ZOxRB95px # 5pzP9LDDG+N56t+LLmbv64NgOArHiv//9Q30xDvDnLcLSm0GKzNWjBIAS6ogJtbC # a79Vdymi+EGGmn784f2W/F7RR0GMqe08dvHVcRGy3yUy83djgUJjley94c3g/iFm # T+8Xkp3sseAfacpZxJZ1Qe4yb+1T8m19DGYZIGU+oVN+9kqwHIPOB3pZTK6AV7RX # SkSN1LvBpS9cMlI0sc7V7L2JTRux4jEUmYBhKYmg3FYvltWfI7WhOI/V79VBae3M # 5gcKK+tIOFY3HlNmtVDW3i2xCFB0L5J0qxLB+lhmHHQ3gBHu9FfQe74JP960W+/1 # e3WHTAsHrumAqOeTEDTlplDw6j9PBm5slLnmJc4xpTEle3S8bmSpaKbMgEPbElPX # TO4bbkfnDzrl/HRNHcWvjXWhghc5MIIXNQYKKwYBBAGCNwMDATGCFyUwghchBgkq # hkiG9w0BBwKgghcSMIIXDgIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3DQEJ # EAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgOJsDy0wG # m38ZkGsGoavkiJWmGjiYB5dEYGzNaP+oRToCEBWrUMdVNi8EAr2WDgFm5NAYDzIw # MjUwMzA0MTI0MjA0WqCCEwMwgga8MIIEpKADAgECAhALrma8Wrp/lYfG+ekE4zME # MA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy # dCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNI # QTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjQwOTI2MDAwMDAwWhcNMzUxMTI1MjM1 # OTU5WjBCMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxIDAeBgNVBAMT # F0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDI0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A # MIICCgKCAgEAvmpzn/aVIauWMLpbbeZZo7Xo/ZEfGMSIO2qZ46XB/QowIEMSvgjE # dEZ3v4vrrTHleW1JWGErrjOL0J4L0HqVR1czSzvUQ5xF7z4IQmn7dHY7yijvoQ7u # jm0u6yXF2v1CrzZopykD07/9fpAT4BxpT9vJoJqAsP8YuhRvflJ9YeHjes4fduks # THulntq9WelRWY++TFPxzZrbILRYynyEy7rS1lHQKFpXvo2GePfsMRhNf1F41nyE # g5h7iOXv+vjX0K8RhUisfqw3TTLHj1uhS66YX2LZPxS4oaf33rp9HlfqSBePejlY # eEdU740GKQM7SaVSH3TbBL8R6HwX9QVpGnXPlKdE4fBIn5BBFnV+KwPxRNUNK6lY # k2y1WSKour4hJN0SMkoaNV8hyyADiX1xuTxKaXN12HgR+8WulU2d6zhzXomJ2Ple # I9V2yfmfXSPGYanGgxzqI+ShoOGLomMd3mJt92nm7Mheng/TBeSA2z4I78JpwGpT # RHiT7yHqBiV2ngUIyCtd0pZ8zg3S7bk4QC4RrcnKJ3FbjyPAGogmoiZ33c1HG93V # p6lJ415ERcC7bFQMRbxqrMVANiav1k425zYyFMyLNyE1QulQSgDpW9rtvVcIH7Wv # G9sqYup9j8z9J1XqbBZPJ5XLln8mS8wWmdDLnBHXgYly/p1DhoQo5fkCAwEAAaOC # AYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM # MAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATAf # BgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4EFgQUn1csA3cO # KBWQZqVjXu5Pkh92oFswWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybDMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFt # cGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUHMAGGGGh0dHA6 # Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDovL2NhY2VydHMu # ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVT # dGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAPa0eH3aZW+M4hBJH2UOR # 9hHbm04IHdEoT8/T3HuBSyZeq3jSi5GXeWP7xCKhVireKCnCs+8GZl2uVYFvQe+p # PTScVJeCZSsMo1JCoZN2mMew/L4tpqVNbSpWO9QGFwfMEy60HofN6V51sMLMXNTL # fhVqs+e8haupWiArSozyAmGH/6oMQAh078qRh6wvJNU6gnh5OruCP1QUAvVSu4kq # VOcJVozZR5RRb/zPd++PGE3qF1P3xWvYViUJLsxtvge/mzA75oBfFZSbdakHJe2B # VDGIGVNVjOp8sNt70+kEoMF+T6tptMUNlehSR7vM+C13v9+9ZOUKzfRUAYSyyEmY # tsnpltD/GWX8eM70ls1V6QG/ZOB6b6Yum1HvIiulqJ1Elesj5TMHq8CWT/xrW7tw # ipXTJ5/i5pkU5E16RSBAdOp12aw8IQhhA/vEbFkEiF2abhuFixUDobZaA0VhqAsM # HOmaT3XThZDNi5U2zHKhUs5uHHdG6BoQau75KiNbh0c+hatSF+02kULkftARjsyE # pHKsF7u5zKRbt5oK5YGwFvgc4pEVUNytmB3BpIiowOIIuDgP5M9WArHYSAR16gc0 # dP2XdkMEP5eBsX7bf/MGN4K3HP50v/01ZHo/Z5lGLvNwQ7XHBx1yomzLP8lx4Q1z # ZKDyHcp4VQJLu2kWTsKsOqQwggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5b # MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5 # NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG # A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh # bXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPB # PXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/ # nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLc # Z47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mf # XazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3N # Ng1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yem # j052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g # 3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD # 4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDS # LFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwM # O1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU # 7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/ # BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0j # BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E # PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw # DQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPO # vxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQ # TGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWae # LJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPBy # oyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfB # wWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8l # Y5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/ # O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbb # bxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3 # OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBl # dkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt # 1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwF # ADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL # ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElE # IFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYD # VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln # aWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKn # JS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/W # BTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHi # LQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhm # V1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHE # tWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 # MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mX # aXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZ # xd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfh # vbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvl # EFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn1 # 5GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV # HQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4Ix # LVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggr # BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdo # dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290 # Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAA # MA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzs # hV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre # +i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8v # C6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38 # dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNr # Iv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYIDdjCCA3ICAQEw # dzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNV # BAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1w # aW5nIENBAhALrma8Wrp/lYfG+ekE4zMEMA0GCWCGSAFlAwQCAQUAoIHRMBoGCSqG # SIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjUwMzA0MTI0 # MjA0WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBTb04XuYtvSPnvk9nFIUIck1YZb # RTAvBgkqhkiG9w0BCQQxIgQgyTIDqTXJ+rMQ5vXt2sW9QVKSLC1Nc8W2hu0m3rag # U0EwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgdnafqPJjLx9DCzojMK7WVnX+13Pb # BdZluQWTmEOPmtswDQYJKoZIhvcNAQEBBQAEggIALCsSo98mEbBXQFDe6KWPxHGc # Y8ZSVSIRvN/tMX2QI0SrTyvBZ5bFXRmReCcrd3QGSnyCsXI1uampORd961VYQ6IT # GfkG3P/iWGe9qXU3NueqR3R0Wlm3TLfymBtWrZylIjravoAm1AfHN7YRga/+2slb # ciEoDHbOA9Jld0pgkLfBzCxD87weZBuXmKkqz4JKZeK/dqrktFn1TEE4rtftvkED # Ou9wwUBTSjGYGUAYr7FgHmZHpI5VaInt2wiHFuSOiVddk/0JaMgrFpOXVTnXuItt # JSev437tzmHORila2t4QYngOnMrEZ4GvnBKL2rHjRni2PjimBTP+tNFPlmgvK9z2 # 63LnHMh6Pr69CgN9dgM6aIrDZ8fsFRJdk8hOWnH2qLFfUTP/0CdO7XHvop/1qOMS # AZYURfTeBEXuFVYyc3m8kJL/5H++QsuHmS0FZa0noBZFXklzCtRiDJudYbzAGcMN # /FQdS/rdIFY+wf8w+Zpc603lEeLdJj6J3BO3Ynn/YJwlM9QSDlk8VzEMGgqzjbxn # xDladqEmgZ5VvkvMY7cLmdbF/oYM0gUknedkWXBF7agodzEBFsA9VHO1xS6rzjsV # 49cGG6OTGjcIRbY5/KHjwX5yeVt3n6TBRisXkIcEul8j0fzk8bpMMtMQfMOf5JuD # W4Rw03ClW7GzfBtMpnM= # SIG # End signature block |