Src/Plugins/Text/Text.ps1
function ConvertTo-AlignedString { <# .SYNOPSIS Justifies and indents a block of text using the specified alignment properties. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Object[]] $InputObject, ## Tab indents [Parameter(ValueFromPipelineByPropertyName)] [ValidateRange(0,10)] [System.Int32] $Tabs = 0, ## Tab size [Parameter(ValueFromPipelineByPropertyName)] [ValidateRange(0,10)] [System.Int32] $TabSize = 4, ## Text alignment [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Left','Center','Right','Justify')] [string] $Align = 'Left', ## Text width [Parameter(ValueFromPipelineByPropertyName)] [System.Int32] $Width = ($Host.UI.RawUI.BufferSize.Width -1), [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoNewLine ) begin { if ($PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = 'Continue' } } process { $tabSpaces = ''.PadRight(($Tabs * $TabSize), ' ') $usableWidth = $Width - ($Tabs * $TabSize) [System.Text.StringBuilder] $textBuilder = New-Object -TypeName 'System.Text.StringBuilder' [System.Text.StringBuilder] $lineBuilder = New-Object -TypeName 'System.Text.StringBuilder' foreach ($line in ($InputObject -split '\r\n?|\n')) { $currentPosition = 0 [ref] $null = $lineBuilder.Clear() $convertToJustifiedStringParams = @{ Align = $Align Width = $usableWidth } ## Break on spaces and then at maximum "usable" length [System.String[]] $words = $line -split '\s' -split "(.{$usableWidth})" | Where-Object { -not [System.String]::IsNullOrEmpty($_) } for ($w = 0; $w -lt $words.Count; $w++) { $word = $words[$w] $isLastWord = $w -eq ($words.Count -1) Write-Debug "Word: '$word', Length: $($word.Length), IsLastWord: $isLastWord" $newPosition = $currentPosition + $word.Length if ($currentPosition -gt 0) { ## We need to prefix the word with a space if we're not at the start of the line $newPosition += 1 } if ($newPosition -lt $usableWidth) { ## We haven't reached the end of the line, so just append if ($currentPosition -gt 0) { ## We're not at the start of the line, so prefix word with a space [ref] $null = $lineBuilder.Append(' ') } [ref] $null = $lineBuilder.Append($word) if ($isLastWord) { ## Output what we have thus far $convertToJustifiedStringParams['InputObject'] = $lineBuilder.ToString() $justifiedString = ConvertTo-JustifiedString @convertToJustifiedStringParams [ref] $null = $textBuilder.Append($tabSpaces).Append($justifiedString) if (-not $NoNewLine) { [ref] $null = $textBuilder.AppendLine() } } $currentPosition = $newPosition } elseif ($newPosition -eq $usableWidth) { ## We're bang on the end of the line, therefore can't justify if ($currentPosition -gt 0) { [ref] $null = $lineBuilder.Append(' ') } [ref] $null = $lineBuilder.Append($word) [ref] $null = $textBuilder.Append($tabSpaces).Append($lineBuilder.ToString()) if ($isLastWord -and (-not $NoNewLine)) { [ref] $null = $textBuilder.AppendLine() } elseif (-not $isLastWord) { [ref] $null = $textBuilder.AppendLine() } [ref] $null = $lineBuilder.Clear() $currentPosition = 0 } else { ## We're over the end of the line, so justify and start a new line $convertToJustifiedStringParams['InputObject'] = $lineBuilder.ToString() $justifiedString = ConvertTo-JustifiedString @convertToJustifiedStringParams [ref] $null = $textBuilder.Append($tabSpaces).Append($justifiedString) if ($isLastWord) { ## Just a single word on a new line $convertToJustifiedStringParams['InputObject'] = $word $justifiedString = ConvertTo-JustifiedString @convertToJustifiedStringParams [ref] $null = $textBuilder.AppendLine().Append($tabSpaces).Append($justifiedString) if (-not $NoNewLine) { [ref] $null = $textBuilder.AppendLine() } } else { ## Start a new line [ref] $null = $textBuilder.AppendLine() [ref] $null = $lineBuilder.Clear().Append($word) $currentPosition = $word.Length } } } } return $textBuilder.ToString() } } function ConvertTo-IndentedString { <# .SYNOPSIS Indents a block of text using the number of tab stops. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [Object[]] $InputObject, ## Tab indent [Parameter()] [ValidateRange(0,10)] [System.Int32] $Tabs = 0, ## Tab size [Parameter()] [ValidateRange(0,10)] [System.Int32] $TabSize = 4 ) process { $padding = ''.PadRight(($Tabs * $TabSize), ' ') ## Use a StringBuilder to write the table line by line (to indent it) [System.Text.StringBuilder] $builder = New-Object System.Text.StringBuilder foreach ($line in ($InputObject -split '\r\n?|\n')) { [ref] $null = $builder.Append($padding) [ref] $null = $builder.AppendLine($line.TrimEnd()) ## Trim trailing space (#67) } return $builder.ToString() } } function ConvertTo-JustifiedString { <# .SYNOPSIS Justifies a block of text using the specified alignment. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object[]] $InputObject, ## Text alignment [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Left','Center','Right','Justify')] [System.String] $Align = 'Left', ## Text width [Parameter()] [System.Int32] $Width = ($Host.UI.RawUI.BufferSize.Width -1) ) process { foreach ($object in $InputObject) { $objectString = $object.ToString() if (($Align -eq 'Left') -or ($objectString.Length -ge $Width)) { $justifiedString = $objectString } else { $paddingLength = $Width - $objectString.Length if ($Align -eq 'Center') { $paddingLength = ($paddingLength /2) -as [System.Int32] } $padding = ''.PadRight($paddingLength, ' ') $justifiedString = '{0}{1}' -f $padding, $objectString } Write-Output -InputObject $justifiedString } } } function Get-PScriboListItemMaximumLength { <# .SYNOPSIS Renders a List's item numbers to determine the maximum string/render width. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','NumberStyle')] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $List, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Management.Automation.PSObject] $NumberStyle ) process { $List.Items | Where-Object { $_.Type -eq 'PScribo.Item' } | ForEach-Object { $number = ConvertFrom-NumberStyle -Value $_.Index -NumberStyle $numberStyle Write-Output -InputObject $number.Length } | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum } } function Get-TextTableCaption { <# .SYNOPSIS Generates html <p> caption from a PScribo.Table object. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Management.Automation.PSObject] $Table ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption; } } process { $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style $convertToAlignedStringParams = @{ InputObject = '{0} {1} {2}' -f $tableStyle.CaptionPrefix, $Table.CaptionNumber, $Table.Caption Width = $options.TextWidth Tabs = $Table.Tabs Align = $tableStyle.Align } return (ConvertTo-AlignedString @convertToAlignedStringParams) } } function New-PScriboTextOption { <# .SYNOPSIS Sets the text plugin specific formatting/output options. .NOTES All plugin options should be prefixed with the plugin name. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Collections.Hashtable])] param ( ## Text/output width. 0 = none/no wrap. [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Int32] $TextWidth = 120, ## Document header separator character. [Parameter(ValueFromPipelineByPropertyName)] [ValidateLength(1,1)] [System.String] $HeaderSeparator = '=', ## Document section separator character. [Parameter(ValueFromPipelineByPropertyName)] [ValidateLength(1,1)] [System.String] $SectionSeparator = '-', ## Document section separator character. [Parameter(ValueFromPipelineByPropertyName)] [ValidateLength(1,1)] [System.String] $LineBreakSeparator = '_', ## Default header/section separator width. [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Int32] $SeparatorWidth = $TextWidth, ## Text encoding [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('ASCII','Unicode','UTF7','UTF8')] [System.String] $Encoding = 'ASCII' ) process { return @{ TextWidth = $TextWidth HeaderSeparator = $HeaderSeparator SectionSeparator = $SectionSeparator LineBreakSeparator = $LineBreakSeparator SeparatorWidth = $SeparatorWidth Encoding = $Encoding } } } function Out-TextBlankLine { <# .SYNOPSIS Output formatted text blankline. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $BlankLine ) process { $blankLineBuilder = New-Object -TypeName System.Text.StringBuilder for ($i = 0; $i -lt $BlankLine.LineCount; $i++) { [ref] $null = $blankLineBuilder.AppendLine() } return $blankLineBuilder.ToString() } } function Out-TextDocument { <# .SYNOPSIS Text output plugin for PScribo. .DESCRIPTION Outputs a text file representation of a PScribo document object. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','pluginName')] param ( ## ThePScribo document object to convert to a text document [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Document, ## Output directory path for the .txt file [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.String] $Path, ### Hashtable of all plugin supported options [Parameter()] [AllowNull()] [System.Collections.Hashtable] $Options ) process { $pluginName = 'Text' $stopwatch = [Diagnostics.Stopwatch]::StartNew() Write-PScriboMessage -Message ($localized.DocumentProcessingStarted -f $Document.Name) ## Merge the document, text default and specified text options $mergePScriboPluginOptionParams = @{ DefaultPluginOptions = New-PScriboTextOption DocumentOptions = $Document.Options PluginOptions = $Options } $Options = Merge-PScriboPluginOption @mergePScriboPluginOptionParams $script:currentPageNumber = 1 [System.Text.StringBuilder] $textBuilder = New-Object -Type 'System.Text.StringBuilder' $firstPageHeader = Out-TextHeaderFooter -Header -FirstPage [ref] $null = $textBuilder.Append($firstPageHeader) foreach ($subSection in $Document.Sections.GetEnumerator()) { $currentIndentationLevel = 1 if ($null -ne $subSection.PSObject.Properties['Level']) { $currentIndentationLevel = $subSection.Level +1 } Write-PScriboProcessSectionId -SectionId $subSection.Id -SectionType $subSection.Type -Indent $currentIndentationLevel switch ($subSection.Type) { 'PScribo.Section' { [ref] $null = $textBuilder.Append((Out-TextSection -Section $subSection)) } 'PScribo.Paragraph' { [ref] $null = $textBuilder.Append((Out-TextParagraph -Paragraph $subSection)) } 'PScribo.PageBreak' { [ref] $null = $textBuilder.Append((Out-TextPageBreak)) } 'PScribo.LineBreak' { [ref] $null = $textBuilder.Append((Out-TextLineBreak)) } 'PScribo.Table' { [ref] $null = $textBuilder.Append((Out-TextTable -Table $subSection)) } 'PScribo.TOC' { [ref] $null = $textBuilder.Append((Out-TextTOC -TOC $subSection)) } 'PScribo.BlankLine' { [ref] $null = $textBuilder.Append((Out-TextBlankLine -BlankLine $subSection)) } 'PScribo.Image' { [ref] $null = $textBuilder.Append((Out-TextImage -Image $subSection)) } 'PScribo.ListReference' { $textList = Out-TextList -List $Document.Lists[$subSection.Number -1] [ref] $null = $textBuilder.Append($textList) } Default { Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning } } } $pageFooter =Out-TextHeaderFooter -Footer [ref] $null = $textBuilder.Append($pageFooter) $stopwatch.Stop() Write-PScriboMessage -Message ($localized.DocumentProcessingCompleted -f $Document.Name) $destinationPath = Join-Path -Path $Path ('{0}.txt' -f $Document.Name) Write-PScriboMessage -Message ($localized.SavingFile -f $destinationPath) Set-Content -Value ($textBuilder.ToString()) -Path $destinationPath -Encoding $Options.Encoding [ref] $null = $textBuilder 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 the file reference to the pipeline Write-Output (Get-Item -Path $destinationPath) } } function Out-TextHeaderFooter { <# .SYNOPSIS Output formatted header/footer. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DefaultHeader')] [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageHeader')] [System.Management.Automation.SwitchParameter] $Header, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DefaultFooter')] [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageFooter')] [System.Management.Automation.SwitchParameter] $Footer, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageHeader')] [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageFooter')] [System.Management.Automation.SwitchParameter] $FirstPage ) process { $headerFooter = Get-PScriboHeaderFooter @PSBoundParameters if ($null -ne $headerFooter) { [System.Text.StringBuilder] $hfBuilder = New-Object System.Text.StringBuilder foreach ($subSection in $headerFooter.Sections.GetEnumerator()) { ## When replacing tokens (by reference), the tokens are removed $cloneSubSection = $subSection | ConvertTo-Json -Depth 100 -Compress | ConvertFrom-Json switch ($cloneSubSection.Type) { 'PScribo.Paragraph' { $paragraph = Out-TextParagraph -Paragraph $cloneSubSection [ref] $null = $hfBuilder.Append($paragraph) } 'PScribo.Table' { if ($Footer) { ## Add a space before a footer table [ref] $null = $hfBuilder.AppendLine() } $table = Out-TextTable -Table $cloneSubSection [ref] $null = $hfBuilder.Append($table) if ($Header) { ## Add a space after a header table [ref] $null = $hfBuilder.AppendLine() } } 'PScribo.BlankLine' { $blankLine = Out-TextBlankLine -BlankLine $subSection [ref] $null = $hfBuilder.Append($blankLine) } 'PScribo.LineBreak' { [ref] $null = $hfBuilder.Append((Out-TextLineBreak)) } } } return $hfBuilder.ToString() } } } function Out-TextImage { <# .SYNOPSIS Output formatted image text. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Management.Automation.PSObject] $Image ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption } } process { $convertToAlignedStringParams = @{ InputObject = '[Image Text="{0}"]' -f $Image.Text Width = $options.TextWidth } return (ConvertTo-AlignedString @convertToAlignedStringParams) } } function Out-TextLineBreak { <# .SYNOPSIS Output formatted line break text. #> [CmdletBinding()] [OutputType([System.String])] param ( ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption } } process { $convertToAlignedStringParams = @{ InputObject = ''.PadRight($options.SeparatorWidth, $options.LineBreakSeparator) Width = $options.TextWidth } return (ConvertTo-AlignedString @convertToAlignedStringParams) } } function Out-TextList { <# .SYNOPSIS Output formatted text list. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','NumberStyle')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','Options')] param ( ## Section to output [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $List, [Parameter(ValueFromPipelineByPropertyName)] [System.Int32] $Indent = 2, ## Number style [Parameter(ValueFromPipelineByPropertyName)] [System.String] $NumberStyle, ## Bullet style [Parameter(ValueFromPipelineByPropertyName)] [System.String] $BulletStyle ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption } } process { $listBuilder = New-Object -TypeName System.Text.StringBuilder $leader = ''.PadRight($Indent, ' ') if ($List.IsNumbered) { if ($List.HasNumberStyle) { $NumberStyle = $List.NumberStyle } elseif (-not $PSBoundParameters.ContainsKey('Style')) { $NumberStyle = $Document.DefaultNumberStyle } $style = $Document.NumberStyles[$NumberStyle] $maxItemNumberLength = Get-PScriboListItemMaximumLength -List $List -NumberStyle $style $outTextListParams = @{ NumberStyle = $NumberStyle } } else { if ($List.HasBulletStyle) { $BulletStyle = $List.BulletStyle } switch ($BulletStyle) { Circle { $numberString = 'o' break } Dash { $numberString = '-' break } Disc { $numberString = '*' break } Default { ## Square style is not supported in Text so default to the browser's rendering engine $numberString = '*' } } $outTextListParams = @{ BulletStyle = $BulletStyle } } foreach ($item in $List.Items) { if ($item.Type -eq 'PScribo.Item') { if ($List.IsNumbered) { $padding = '' $itemNumber = ConvertFrom-NumberStyle -Value $item.Index -NumberStyle $style $paddingLength = ($maxItemNumberLength) - $itemNumber.Length if ($paddingLength -gt 0) { $padding = ''.PadRight($paddingLength, ' ') } if ($style.Align -eq 'Left') { $numberString = '{0}{1}' -f $itemNumber, $padding } elseif ($style.Align -eq 'Right') { $numberString = '{0}{1}' -f $padding, $itemNumber } } [ref] $null = $listBuilder.AppendFormat('{0}{1} {2}', $leader, $numberString, $item.Text).AppendLine() } else { $newIndent = $Indent + 2 if ($List.IsNumbered) { $newIndent = $Indent + $maxItemNumberLength +1 } $nestedList = Out-TextList -List $item -Indent $newIndent @outTextListParams [ref] $null = $listBuilder.Append($nestedList) } } [ref] $null = $listBuilder.AppendLine() return $listBuilder.ToString() } } function Out-TextPageBreak { <# .SYNOPSIS Output formatted line break text. #> [CmdletBinding()] [OutputType([System.String])] param ( ) process { $pagebreakBuilder = New-Object -TypeName System.Text.StringBuilder $isFirstPage = $currentPageNumber -eq 1 $pageFooter = Out-TextHeaderFooter -Footer -FirstPage:$isFirstPage [ref] $null = $pageBreakBuilder.Append($pageFooter) $script:currentPageNumber++ [ref] $null = $pagebreakBuilder.Append((Out-TextLineBreak)).AppendLine() $pageHeader = Out-TextHeaderFooter -Header [ref] $null = $pageBreakBuilder.Append($pageHeader) return $pageBreakBuilder.ToString() } } function Out-TextParagraph { <# .SYNOPSIS Output formatted paragraph run. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Management.Automation.PSObject] $Paragraph ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption; } } process { $convertToAlignedStringParams = @{ Width = $options.TextWidth Tabs = $Paragraph.Tabs Align = 'Left' } if (-not [System.String]::IsNullOrEmpty($Paragraph.Style)) { $paragraphStyle = Get-PScriboDocumentStyle -Style $Paragraph.Style $convertToAlignedStringParams['Align'] = $paragraphStyle.Align } [System.Text.StringBuilder] $paragraphBuilder = New-Object -TypeName 'System.Text.StringBuilder' foreach ($paragraphRun in $Paragraph.Sections) { $text = Resolve-PScriboToken -InputObject $paragraphRun.Text [ref] $null = $paragraphBuilder.Append($text) if (($paragraphRun.IsParagraphRunEnd -eq $false) -and ($paragraphRun.NoSpace -eq $false)) { [ref] $null = $paragraphBuilder.Append(' ') } } $convertToAlignedStringParams['InputObject'] = $paragraphBuilder.ToString() return (ConvertTo-AlignedString @convertToAlignedStringParams) } } function Out-TextSection { <# .SYNOPSIS Output formatted text section. #> [CmdletBinding()] param ( ## Section to output [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Section ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption } } process { $padding = ''.PadRight(($Section.Tabs * 4), ' ') $sectionBuilder = New-Object -TypeName System.Text.StringBuilder if ($Document.Options['EnableSectionNumbering']) { [string] $sectionName = '{0} {1}' -f $Section.Number, $Section.Name } else { [string] $sectionName = '{0}' -f $Section.Name } [ref] $null = $sectionBuilder.AppendLine() [ref] $null = $sectionBuilder.Append($padding) [ref] $null = $sectionBuilder.AppendLine($sectionName.TrimStart()) [ref] $null = $sectionBuilder.Append($padding) [ref] $null = $sectionBuilder.AppendLine(''.PadRight(($options.SeparatorWidth - $padding.Length), $options.SectionSeparator)) foreach ($subSection in $Section.Sections.GetEnumerator()) { $currentIndentationLevel = 1 if ($null -ne $subSection.PSObject.Properties['Level']) { $currentIndentationLevel = $subSection.Level +1 } Write-PScriboProcessSectionId -SectionId $subSection.Id -SectionType $subSection.Type -Indent $currentIndentationLevel switch ($subSection.Type) { 'PScribo.Section' { [ref] $null = $sectionBuilder.Append((Out-TextSection -Section $subSection)) } 'PScribo.Paragraph' { [ref] $null = $sectionBuilder.Append((Out-TextParagraph -Paragraph $subSection)) } 'PScribo.PageBreak' { [ref] $null = $sectionBuilder.Append((Out-TextPageBreak)) ## Page breaks implemented as line break with extra padding } 'PScribo.LineBreak' { [ref] $null = $sectionBuilder.Append((Out-TextLineBreak)) } 'PScribo.Table' { [ref] $null = $sectionBuilder.Append((Out-TextTable -Table $subSection)) } 'PScribo.BlankLine' { [ref] $null = $sectionBuilder.Append((Out-TextBlankLine -BlankLine $subSection)) } 'PScribo.Image' { [ref] $null = $sectionBuilder.Append((Out-TextImage -Image $subSection)) } 'PScribo.ListReference' { $textList = Out-TextList -List $Document.Lists[$subSection.Number -1] [ref] $null = $sectionBuilder.Append($textList) } Default { Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning } } } return $sectionBuilder.ToString() } } function Out-TextTable { <# .SYNOPSIS Output formatted text table. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Table ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption } } process { $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style $tableBuilder = New-Object -TypeName System.Text.StringBuilder $tableRenderWidth = $options.TextWidth - ($Table.Tabs * 4) ## We need to rewrite arrays for text formatting and we don't want to ## alter the source documnent (#126) $cloneTable = $Table | ConvertTo-Json -Depth 100 -Compress | ConvertFrom-Json ## We need to flatten arrays and replace page numbers before outputting the table foreach ($row in $cloneTable.Rows) { foreach ($property in $row.PSObject.Properties) { if ($property.Value -is [System.Array]) { $property.Value = [System.String]::Join(' ', $property.Value) } $property.Value = Resolve-PScriboToken -InputObject $property.Value } } ## We've got to render the table first to determine how wide it is ## before we can justify it if ($Table.IsKeyedList) { ## Create new objects with headings as properties $tableText = (ConvertTo-PSObjectKeyedListTable -Table $cloneTable | Select-Object -Property * -ExcludeProperty '*__Style' | Format-Table -Wrap -AutoSize | Out-String -Width $tableRenderWidth).Trim([System.Environment]::NewLine) } elseif ($Table.IsList) { $tableText = ($cloneTable.Rows | Select-Object -Property * -ExcludeProperty '*__Style' | Format-List | Out-String -Width $tableRenderWidth).Trim([System.Environment]::NewLine) } else { ## Don't trim tabs for table headers ## Tables set to AutoSize as otherwise rendering is different between PoSh v4 and v5 $tableText = ($cloneTable.Rows | Select-Object -Property * -ExcludeProperty '*__Style' | Format-Table -Wrap -AutoSize | Out-String -Width $tableRenderWidth).Trim([System.Environment]::NewLine) } if ($Table.HasCaption -and ($tableStyle.CaptionLocation -eq 'Above')) { $justifiedCaption = Get-TextTableCaption -Table $Table [ref] $null = $tableBuilder.AppendLine($justifiedCaption) [ref] $null = $tableBuilder.AppendLine() } ## We don't want to wrap table contents so just justify it $convertToJustifiedStringParams = @{ InputObject = $tableText Width = $tableRenderWidth Align = $tableStyle.Align } $justifiedTableText = ConvertTo-JustifiedString @convertToJustifiedStringParams [ref] $null = $tableBuilder.Append($justifiedTableText) if ($Table.HasCaption -and ($tableStyle.CaptionLocation -eq 'Below')) { $justifiedCaption = Get-TextTableCaption -Table $Table [ref] $null = $tableBuilder.AppendLine() [ref] $null = $tableBuilder.AppendLine() [ref] $null = $tableBuilder.Append($justifiedCaption) } $convertToIndentedStringParams = @{ InputObject = $tableBuilder.ToString() Tabs = $Table.Tabs } return (ConvertTo-IndentedString @convertToIndentedStringParams) } } function Out-TextTOC { <# .SYNOPSIS Output formatted Table of Contents #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $TOC ) begin { ## Fix Set-StrictMode if (-not (Test-Path -Path Variable:\Options)) { $options = New-PScriboTextOption } } process { $tocBuilder = New-Object -TypeName System.Text.StringBuilder [ref] $null = $tocBuilder.AppendLine($TOC.Name) [ref] $null = $tocBuilder.AppendLine(''.PadRight($options.SeparatorWidth, $options.SectionSeparator)) if ($Options.ContainsKey('EnableSectionNumbering')) { $maxSectionNumberLength = $Document.TOC.Number | ForEach-Object { $_.Length } | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum foreach ($tocEntry in $Document.TOC) { $sectionNumberPaddingLength = $maxSectionNumberLength - $tocEntry.Number.Length $sectionNumberIndent = ''.PadRight($tocEntry.Level, ' ') $sectionPadding = ''.PadRight($sectionNumberPaddingLength, ' ') [ref] $null = $tocBuilder.AppendFormat('{0}{1} {2}{3}', $tocEntry.Number, $sectionPadding, $sectionNumberIndent, $tocEntry.Name).AppendLine() } } else { $maxSectionNumberLength = $Document.TOC.Level | Sort-Object | Select-Object -Last 1 foreach ($tocEntry in $Document.TOC) { $sectionNumberIndent = ''.PadRight($tocEntry.Level, ' ') [ref] $null = $tocBuilder.AppendFormat('{0}{1}', $sectionNumberIndent, $tocEntry.Name).AppendLine() } } return $tocBuilder.ToString() } } # SIG # Begin signature block # MIIuuwYJKoZIhvcNAQcCoIIurDCCLqgCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCjSdTPrsM3RvSy # w9wnFqSMnkGYFwyXS6wddO/3eU5Zd6CCE6QwggWQMIIDeKADAgECAhAFmxtXno4h # 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/byWBypExghptMIIaaQIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYD # VQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBH # NCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAh8dPej8V5j # mSw73gBSFOYwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA # oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w # DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg2RDM/0kO+9Syr7nN29sM4rn4 # 5EYYqGh/8hMUPKRKiC0wDQYJKoZIhvcNAQEBBQAEggIAbJqWhN8TngaYHfktSBcd # 7aXMcCOLvZd1qgQR8tHJjdo2xRMPeicppaix8r02P4rMjUpFKo8e6T0l89wly9+5 # nmOeI/NBwcFwQyWDN3FEs9ncjcM0Z+A589gezvZ8tPln4SWjVXOxIg+NB0uTyEWd # 8reX7plEsCycE3mqSRDeUkvSFwnNOrBMm/lUGI9/4O/zwnUUcOV3gIyzEwMjwoQS # Iq8/Q1hmma30F6Ow6q1RIWEbOIgXiq+uVyX3XxSDylg7XnRku++mQ+5lro9SmWGm # jAP97DgMl01C1O0vyM2Si2KIxhqWVLjk92wP4weIw/oyqKpEFBRnHWvgxgOehAxB # sp6pWLi9MWzuFWTxyqMp9Ggi3MKdB2q7hjFRKQwYkvA94ax5BN+ZeUZbvpA90QzB # QuSfS+FxibJ99iBokNa2F05e6oHrN4HGEJWPgylKifoBYLx7xM+z1KD2YemTZsKu # t2YhTpt/X/MLTecVfKr1DZ1jfEDqasYj/nkVHmG/aoy0CqgcsTZ9V4k5+amz3222 # CWfifo+cCIXxiR9RDXf/FW2n1JtkMGq0p20EjohigqCD+6v27sWIzYXmyMF3eyBa # i2kAxAKETvBW4JfxBwKpaukQNbu93maSFXi5jYwdDrBR8na4cNOqKhtrCuTYynhn # uq8NzerdJcNOfMOLCvSs2hqhghc6MIIXNgYKKwYBBAGCNwMDATGCFyYwghciBgkq # hkiG9w0BBwKgghcTMIIXDwIBAzEPMA0GCWCGSAFlAwQCAQUAMHgGCyqGSIb3DQEJ # EAEEoGkEZzBlAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgxFeRGdOd # BVsOufpnGoRftIUdNYQ/sjy9Fb8OA0PWz6ICEQCaLr5YHfsOUdwmyDQP2wVaGA8y # MDI1MDMwNzA4NTUwNlqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz # BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl # cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT # SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz # NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD # ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP # ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I # xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO # 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp # LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8 # hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5 # WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup # WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5 # XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq # U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd # 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1 # rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj # ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E # DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw # HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3 # DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh # bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw # Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l # U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD # kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv # qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU # y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ # KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt # gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ # mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7 # cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL # DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M # hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH # NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN # c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe # WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl # cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp # Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1 # OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5 # BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0 # YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz # wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL # f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C # 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5 # n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd # zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH # po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/ # oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV # A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg # 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM # DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E # VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB # /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud # IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV # HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0 # dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy # dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f # BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1 # c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB # MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT # zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E # UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm # niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw # cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH # wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/ # JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9 # Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm # 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB # tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw # ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2 # 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM # BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE # CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ # RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC # pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf # 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x # 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio # ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax # xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ # OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ # l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz # 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH # 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb # 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ # 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD # VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC # MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI # KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3 # aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v # dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j # b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g # ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs # 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq # 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/ # Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9 # /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj # ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB # MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD # VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt # cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq # hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDMwNzA4 # NTUwNlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG # W0UwLwYJKoZIhvcNAQkEMSIEIFU1hB7VX+4DtYD7MxsuHuvQGqhLqCsHkH/codjm # FqDXMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz # 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAGQlxX6lNjrI30b3dGnIs8iR # 3JEfmgPVDvjFeRbxONaqc44Ff4+VCPgAkq9536RWdJQKCHe4RKAH7zYXJWAB56Ke # pVrTBaghV2JlWEeV85c9Z3iPy06ctLD2qzmtoswFnEwHhAnJgB0D5bBMXCJ1Cb89 # zrV45/br/R1zmrPPwWXSWf48CSKkxsxIVH9xAN75A6ZGkz6h37XdYsHViCX+Xv6e # r3dAAr4fS9bkw2Lif+Bv4IKmOyHbFYxGw2PG0kx6WueYHNWqd1DZQxL5FekT4h1+ # O8Jpbtgh/yPbHOKJn2gYBfN1JWsYZx1LhDX75KPrtnFDJdU72u5hFItagLFBZUc9 # x1/yhhjB0bIS5jKbL9BgVaZX2926riHtzvC/xVwbQII7mzOOeqhre2AzgAuTiE/6 # g+8m3ipy9IL8+UoLqFues7j+FG/FbqgwJkKKCdzXXjvSUdWvW3XBwkpRV146I5q8 # 1MMEpskQR5M4HJLAKW5ZlwQiUiSWvRdzJ+NspQqY7t8VSHY3pInbqgRYkoFIKnPx # RSeyrADmPMllXoR+SYivaxhXR1eDs4Ob92W8xP+YTAvW2zAt4aomI3DXHaB+Ayf1 # zizwJ7fLk2R4yVWdzzOV2kYC+TqELwhY3rVoXeYYTC2c8mCP09pGmL3b6jqASFzy # phzuooqMhrmagGCNHGLz # SIG # End signature block |