Src/Private/Private.ps1
function Add-PScriboNumberStyle { <# .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()] param ( ## Number formatting 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 ) begin { if ($Custom) { if (-not $Custom.Contains('%')) { throw ($localized.InvalidCustomNumberStyleError -f $Custom) } } } process { $pscriboDocument.Properties['NumberStyles']++ $numberStyle = [PSCustomObject] @{ Id = $Id.Replace(' ', $pscriboDocument.Options['SpaceSeparator']) Name = $Id Format = if ($PSBoundParameters.ContainsKey('Format')) { $Format } else { 'Custom' } Custom = $Custom Align = $Align Uppercase = $Uppercase Suffix = $Suffix Indent = $Indent Hanging = $Hanging } $pscriboDocument.NumberStyles[$Id] = $numberStyle if ($Default) { $pscriboDocument.DefaultNumberStyle = $numberStyle.Id } } } function Add-PScriboStyle { <# .SYNOPSIS Initializes a new PScribo style object. #> [CmdletBinding()] param ( ## Style name [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## Style id [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $Id = $Name -Replace(' ',''), ## Font size (pt) [Parameter(ValueFromPipelineByPropertyName)] [System.UInt16] $Size = 11, ## Font name (array of names for HTML output) [Parameter(ValueFromPipelineByPropertyName)] [System.String[]] $Font, ## Font color/colour [Parameter(ValueFromPipelineByPropertyName)] [Alias('Colour')] [ValidateNotNullOrEmpty()] [System.String] $Color = 'Black', ## 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')] [string] $Align = 'Left', ## 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, ## Set as default style [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Default ) begin { if (-not (Test-PScriboStyleColor -Color $Color)) { throw ($localized.InvalidHtmlColorError -f $Color); } if ($BackgroundColor) { if (-not (Test-PScriboStyleColor -Color $BackgroundColor)) { throw ($localized.InvalidHtmlBackgroundColorError -f $BackgroundColor); } else { $BackgroundColor = Resolve-PScriboStyleColor -Color $BackgroundColor; } } if (-not ($Font)) { $Font = $pscriboDocument.Options['DefaultFont']; } } process { $pscriboDocument.Properties['Styles']++; $style = [PSCustomObject] @{ Id = $Id; Name = $Name; Font = $Font; Size = $Size; Color = (Resolve-PScriboStyleColor -Color $Color).ToLower(); BackgroundColor = $BackgroundColor.ToLower(); Bold = $Bold.ToBool(); Italic = $Italic.ToBool(); Underline = $Underline.ToBool(); Align = $Align; ClassId = $ClassId; Hidden = $Hidden.ToBool(); } $pscriboDocument.Styles[$Id] = $style; if ($Default) { $pscriboDocument.DefaultStyle = $style.Id; } } } function Add-PScriboTableStyle { <# .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()] param ( ## Table Style name/id [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0)] [ValidateNotNullOrEmpty()] [Alias('Name')] [System.String] $Id, ## Header Row Style Id [Parameter(ValueFromPipelineByPropertyName, Position = 1)] [ValidateNotNullOrEmpty()] [System.String] $HeaderStyle = 'Normal', ## Row Style Id [Parameter(ValueFromPipelineByPropertyName, Position = 2)] [ValidateNotNullOrEmpty()] [System.String] $RowStyle = 'Normal', ## Alternating Row Style Id [Parameter(ValueFromPipelineByPropertyName, Position = 3)] [AllowNull()] [Alias('AlternatingRowStyle')] [System.String] $AlternateRowStyle = $RowStyle, ## Caption Style Id [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $CaptionStyle = 'Normal', ## Table border size/width (pt) [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()] [System.Single] $BorderWidth = 0, ## Table border colour [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [Alias('BorderColour')] [System.String] $BorderColor = '000', ## Table cell top padding (pt) [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Single] $PaddingTop = 1.0, ## Table cell left padding (pt) [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Single] $PaddingLeft = 4.0, ## Table cell bottom padding (pt) [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Single] $PaddingBottom = 0.0, ## Table cell right padding (pt) [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Single] $PaddingRight = 4.0, ## Table alignment [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Left','Center','Right')] [System.String] $Align = 'Left', ## Caption prefix [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CaptionPrefix = 'Table', [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Above', 'Below')] [System.String] $CaptionLocation = 'Below', ## Set as default table style [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Default ) begin { if ($BorderWidth -gt 0) { $borderStyle = 'Solid' } else { $borderStyle = 'None' } if (-not ($pscriboDocument.Styles.ContainsKey($HeaderStyle))) { throw ($localized.UndefinedTableHeaderStyleError -f $HeaderStyle) } if (-not ($pscriboDocument.Styles.ContainsKey($RowStyle))) { throw ($localized.UndefinedTableRowStyleError -f $RowStyle) } if (-not ($pscriboDocument.Styles.ContainsKey($AlternateRowStyle))) { throw ($localized.UndefinedAltTableRowStyleError -f $AlternateRowStyle) } if (-not (Test-PScriboStyleColor -Color $BorderColor)) { throw ($localized.InvalidTableBorderColorError -f $BorderColor) } } process { $pscriboDocument.Properties['TableStyles']++; $tableStyle = [PSCustomObject] @{ Id = $Id.Replace(' ', $pscriboDocument.Options['SpaceSeparator']) Name = $Id HeaderStyle = $HeaderStyle RowStyle = $RowStyle AlternateRowStyle = $AlternateRowStyle CaptionStyle = $CaptionStyle PaddingTop = ConvertTo-Mm -Point $PaddingTop PaddingLeft = ConvertTo-Mm -Point $PaddingLeft PaddingBottom = ConvertTo-Mm -Point $PaddingBottom PaddingRight = ConvertTo-Mm -Point $PaddingRight Align = $Align BorderWidth = ConvertTo-Mm -Point $BorderWidth BorderStyle = $borderStyle BorderColor = Resolve-PScriboStyleColor -Color $BorderColor CaptionPrefix = $CaptionPrefix CaptionLocation = $CaptionLocation } $pscriboDocument.TableStyles[$Id] = $tableStyle if ($Default) { $pscriboDocument.DefaultTableStyle = $tableStyle.Id } } } function ConvertFrom-NumberStyle { <# .SYNOPSIS Converts a number to its string representation, based upon the number style #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Int32] $Value, [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $NumberStyle ) process { switch ($NumberStyle.Format) { 'Number' { $numberString = $Value.ToString() } 'Letter' { $numberString = [System.Char] ($Value + 64) } 'Roman' { $numberString = ConvertTo-RomanNumeral -Value $Value } 'Custom' { $numberCount = 0 $customStringChars = $numberStyle.Custom.ToCharArray() $customStringLength = $numberStyle.Custom.Length for ($n = $customStringLength - 1; $n -ge 0; $n--) { if ($customStringChars[$n] -eq '%') { $numberCount++ } } $searchString = ''.PadRight($numberCount, '%') $replacementString = $Value.ToString().PadLeft($numberCount, '0') return $numberStyle.Custom.Replace($searchString, $replacementString) } } $numberString = '{0}{1}' -f $numberString, $NumberStyle.Suffix if ($NumberStyle.Uppercase) { return $numberString.ToUpper() } else { return $numberString.ToLower() } } } function ConvertTo-Em { <# .SYNOPSIS Convert pixels or millimeters into EMU #> [CmdletBinding()] [OutputType([System.Single])] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Pixel')] [Alias('px')] [System.Single] $Pixel, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Millimeter')] [Alias('mm','Millimetre')] [System.Single] $Millimeter ) process { if ($PSCmdlet.ParameterSetName -eq 'Pixel') { $em = $pixel * 9525 return [System.Math]::Round($em, 0) } elseif ($PSCmdlet.ParameterSetName -eq 'Millimeter') { $em = $Millimeter / 4.23333333333333 return [System.Math]::Round($em, 2) } } } function ConvertTo-Image { <# .SYNOPSIS Creates an image from a byte[] #> [CmdletBinding()] [OutputType([System.Drawing.Image])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Byte[]] $Bytes ) process { try { [System.IO.MemoryStream] $memoryStream = New-Object -TypeName 'System.IO.MemoryStream' -ArgumentList @(,$Bytes) [System.Drawing.Image] $image = [System.Drawing.Image]::FromStream($memoryStream) Write-Output -InputObject $image } catch { $_ } finally { if ($null -ne $memoryStream) { $memoryStream.Close() } } } } function ConvertTo-In { <# .SYNOPSIS Convert millimeters into inches #> [CmdletBinding()] [OutputType([System.Single])] param ( [Parameter(Mandatory, ValueFromPipeline)] [Alias('mm','Millimetre')] [System.Single] $Millimeter ) process { $in = $Millimeter / 25.4 return [System.Math]::Round($in, 2) } } function ConvertTo-InvariantCultureString { <# .SYNOPSIS Convert to a number to a string with a culture-neutral representation #6, #42. #> [CmdletBinding()] param ( ## The sinle/double [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.Object] $Object, ## Format string [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Format ) process { if ($PSBoundParameters.ContainsKey('Format')) { return $Object.ToString($Format, [System.Globalization.CultureInfo]::InvariantCulture); } else { return $Object.ToString([System.Globalization.CultureInfo]::InvariantCulture); } } } function ConvertTo-Mm { <# .SYNOPSIS Convert points, inches or pixels into millimeters #> [CmdletBinding()] [OutputType([System.Single])] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Point')] [Alias('pt')] [System.Single] $Point, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Inch')] [Alias('in')] [System.Single] $Inch, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Pixel')] [Alias('px')] [System.Single] $Pixel, [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Pixel')] [System.Int16] $Dpi = 96 ) process { if ($PSCmdlet.ParameterSetName -eq 'Point') { return [System.Math]::Round(($Point / 72) * 25.4, 2) } elseif ($PSCmdlet.ParameterSetName -eq 'Inch') { $mm = $Inch * 25.4 return [System.Math]::Round($mm, 2) } elseif ($PSCmdlet.ParameterSetName -eq 'Pixel') { $mm = (25.4 / $Dpi) * $Pixel return [System.Math]::Round($mm, 2) } } } function ConvertTo-Octips { <# .SYNOPSIS Convert millimeters into octips .NOTES 1 "octip" = 1/8th pt #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')] [OutputType([System.Single])] param ( [Parameter(Mandatory, ValueFromPipeline)] [Alias('mm','Millimetre')] [System.Single] $Millimeter ) process { $octips = (ConvertTo-In -Millimeter $Millimeter) * 576 return [System.Math]::Round($octips, 2) } } function ConvertTo-PScriboFormattedKeyedListTable { <# .SYNOPSIS Creates a formatted keyed list table (a key'd column per object) for plugin output/rendering. .NOTES Maintains backwards compatibility with other plugins that do not require styling/formatting. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Table ) begin { $hasColumnWidths = ($null -ne $Table.ColumnWidths) $objectKey = $Table.ListKey } process { $formattedTable = New-PScriboFormattedTable -Table $Table -HasHeaderRow -HasHeaderColumn ## Output the header cells $headerRow = New-PScriboFormattedTableRow -TableStyle $Table.Style -IsHeaderRow if ($Table.DisplayListKey) { $newPScriboFormattedTableRowHeaderCellParams = @{ Content = $Table.ListKey } } else { $newPScriboFormattedTableRowHeaderCellParams = @{ Content = ' ' } } $listKeyHeaderCell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableRowHeaderCellParams $null = $headerRow.Cells.Add($listKeyHeaderCell) ## Add all the object key values for ($o = 0; $o -lt $Table.Rows.Count; $o++) { $newPScriboFormattedTableRowHeaderCellParams = @{ Content = $Table.Rows[$o].PSObject.Properties[$objectKey].Value } $objectKeyCell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableRowHeaderCellParams $null = $headerRow.Cells.Add($objectKeyCell) } $null = $formattedTable.Rows.Add($headerRow) $isAlternateRow = $false ## Output remaining object properties (one property per row) foreach ($column in $Table.Columns) { if ((-not $column.EndsWith('__Style', 'CurrentCultureIgnoreCase')) -and ($column -ne $objectKey)) { ## Add the object property column $newPScriboFormattedTableRowParams = @{ TableStyle = $Table.Style; IsAlternateRow = $isAlternateRow } $row = New-PScriboFormattedTableRow @newPScriboFormattedTableRowParams ## Output the column header cell (property name) as header style $newPScriboFormattedTableColumnCellParams = @{ Content = $column } if ($hasColumnWidths) { $newPScriboFormattedTableColumnCellParams['Width'] = $Table.ColumnWidths[0] } $columnCell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableColumnCellParams $null = $row.Cells.Add($columnCell) ## Add the property value for all other objects for ($o = 0; $o -lt $Table.Rows.Count; $o++) { $propertyValue = $Table.Rows[$o].PSObject.Properties[$column].Value $newPScriboFormattedTableRowValueCellParams = @{ Content = $propertyValue } if ([System.String]::IsNullOrEmpty($propertyValue)) { $newPScriboFormattedTableRowValueCellParams['Content'] = $null } $propertyStyleName = '{0}__Style' -f $column $hasStyleProperty = $Table.Rows[$o].PSObject.Properties.Name.Contains($propertyStyleName) if ($hasStyleProperty) { $newPScriboFormattedTableRowValueCellParams['Style'] = $Table.Rows[$o].PSObject.Properties[$propertyStyleName].Value } if ($hasColumnWidths) { $newPScriboFormattedTableRowValueCellParams['Width'] = $Table.ColumnWidths[$o+1] } $valueCell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableRowValueCellParams $null = $row.Cells.Add($valueCell) } $null = $formattedTable.Rows.Add($row) $isAlternateRow = -not $isAlternateRow } } Write-Output -InputObject $formattedTable } } function ConvertTo-PScriboFormattedListTable { <# .SYNOPSIS Creates a formatted list table for plugin output/rendering. .NOTES Maintains backwards compatibility with other plugins that do not require styling/formatting. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Table ) begin { $hasColumnWidths = ($null -ne $Table.ColumnWidths) } process { for ($r = 0; $r -lt $Table.Rows.Count; $r++) { ## We have one table per object $formattedTable = New-PScriboFormattedTable -Table $Table -HasHeaderColumn $objectProperties = $Table.Rows[$r].PSObject.Properties for ($c = 0; $c -lt $Table.Columns.Count; $c++) { $column = $Table.Columns[$c] $newPScriboFormattedTableRowParams = @{ TableStyle = $Table.Style; Style = $Table.Rows[$r].'__Style' IsAlternateRow = ($c % 2) -ne 0 } $row = New-PScriboFormattedTableRow @newPScriboFormattedTableRowParams ## Add each property name (as header style) $newPScriboFormattedTableRowHeaderCellParams = @{ Content = $column } if ($hasColumnWidths) { $newPScriboFormattedTableRowHeaderCellParams['Width'] = $Table.ColumnWidths[0] } $headerCell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableRowHeaderCellParams $null = $row.Cells.Add($headerCell) ## Add each property value $propertyValue = $objectProperties[$column].Value if ([System.String]::IsNullOrEmpty($propertyValue)) { $propertyValue = $null } $newPScriboFormattedTableRowValueCellParams = @{ Content = $propertyValue } $propertyStyleName = '{0}__Style' -f $column $hasStyleProperty = $objectProperties.Name.Contains($propertyStyleName) if ($hasStyleProperty) { $newPScriboFormattedTableRowValueCellParams['Style'] = $objectProperties[$propertyStyleName].Value } if ($hasColumnWidths) { $newPScriboFormattedTableRowValueCellParams['Width'] = $Table.ColumnWidths[1] } $valueCell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableRowValueCellParams $null = $row.Cells.Add($valueCell) $null = $formattedTable.Rows.Add($row) } Write-Output -InputObject $formattedTable } } } function ConvertTo-PScriboFormattedTable { <# .SYNOPSIS Creates a formatted standard table for plugin output/rendering. .NOTES Maintains backwards compatibility with other plugins that do not require styling/formatting. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Table ) begin { $hasColumnWidths = ($null -ne $Table.ColumnWidths) } process { $formattedTable = New-PScriboFormattedTable -Table $Table -HasHeaderRow # Output the header row and header cells $headerRow = New-PScriboFormattedTableRow -TableStyle $Table.Style -IsHeaderRow for ($h = 0; $h -lt $Table.Columns.Count; $h++) { $newPScriboFormattedTableHeaderCellParams = @{ Content = $Table.Columns[$h] } if ($hasColumnWidths) { $newPScriboFormattedTableHeaderCellParams['Width'] = $Table.ColumnWidths[$h] } $cell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableHeaderCellParams $null = $headerRow.Cells.Add($cell) } $null = $formattedTable.Rows.Add($headerRow) ## Output each object row for ($r = 0; $r -lt $Table.Rows.Count; $r++) { $objectProperties = $Table.Rows[$r].PSObject.Properties $newPScriboFormattedTableRowParams = @{ TableStyle = $Table.Style; Style = $Table.Rows[$r].'__Style' IsAlternateRow = ($r % 2 -ne 0 ) } $row = New-PScriboFormattedTableRow @newPScriboFormattedTableRowParams ## Output object row's cells for ($c = 0; $c -lt $Table.Columns.Count; $c++) { $propertyName = $Table.Columns[$c] $propertyStyleName = '{0}__Style' -f $propertyName; $hasStyleProperty = $objectProperties.Name.Contains($propertyStyleName) $propertyValue = $objectProperties[$propertyName].Value $newPScriboFormattedTableRowCellParams = @{ Content = $propertyValue } if ([System.String]::IsNullOrEmpty($propertyValue)) { $newPScriboFormattedTableRowCellParams['Content'] = $null } if ($hasColumnWidths) { $newPScriboFormattedTableRowCellParams['Width'] = $Table.ColumnWidths[$c] } if ($hasStyleProperty) { $newPScriboFormattedTableRowCellParams['Style'] = $objectProperties[$propertyStyleName].Value # | Where-Object Name -eq $propertyStyleName | Select-Object -ExpandProperty Value } $cell = New-PScriboFormattedTableRowCell @newPScriboFormattedTableRowCellParams $null = $row.Cells.Add($cell) } $null = $formattedTable.Rows.Add($row) } Write-Output -InputObject $formattedTable } } function ConvertTo-PScriboPreformattedTable { <# .SYNOPSIS Creates a formatted table based upon table type for plugin output/rendering. .NOTES Maintains backwards compatibility with other plugins that do not require styling/formatting. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Table ) process { if ($Table.IsKeyedList) { Write-Output -InputObject (ConvertTo-PScriboFormattedKeyedListTable -Table $Table) } elseif ($Table.IsList) { Write-Output -InputObject (ConvertTo-PScriboFormattedListTable -Table $Table) } else { Write-Output -InputObject (ConvertTo-PScriboFormattedTable -Table $Table) } } } function ConvertTo-PSObjectKeyedListTable { <# .SYNOPSIS Converts a PScribo.Table to a [PSCustomObject] collection representing a keyed list table. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Table ) process { $listKey = $Table.ListKey $rowHeaders = $Table.Rows | Select-Object -ExpandProperty $listKey $columnHeaders = $Table.Rows | Select-Object -First 1 -Property * -ExcludeProperty '*__Style' | ForEach-Object { $_.PSObject.Properties.Name } | Where-Object { $_ -ne $listKey } foreach ($columnHeader in $columnHeaders) { $psCustomObjectParams = [Ordered] @{ $listKey = $columnHeader } foreach ($rowHeader in $rowHeaders) { $psCustomObjectParams[$rowHeader] = $Table.Rows | Where-Object { $_.$listKey -eq $rowHeader } | Select-Object -ExpandProperty $columnHeader } $psCustomObject = [PSCustomObject] $psCustomObjectParams Write-Output -InputObject $psCustomObject } } } function ConvertTo-Pt { <# .SYNOPSIS Convert millimeters into points #> [CmdletBinding()] [OutputType([System.Single])] param ( [Parameter(Mandatory, ValueFromPipeline)] [Alias('mm','Millimetre')] [System.Single] $Millimeter ) process { $pt = (ConvertTo-In -Millimeter $Millimeter) / 0.0138888888888889 return [System.Math]::Round($pt, 2) } } function ConvertTo-Px { <# .SYNOPSIS Convert millimeters into pixels (default 96dpi) #> [CmdletBinding()] [OutputType([System.Int16])] param ( [Parameter(Mandatory, ValueFromPipeline)] [Alias('mm','Millimetre')] [System.Single] $Millimeter, [Parameter(ValueFromPipelineByPropertyName)] [System.Int16] $Dpi = 96 ) process { $px = [System.Int16] ((ConvertTo-In -Millimeter $Millimeter) * $Dpi) if ($px -lt 1) { return (1 -as [System.Int16]) } else { return $px } } } function ConvertTo-RomanNumeral { <# .SYNOPSIS Converts a decimal number to Roman numerals. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.Int32] $Value ) begin { $conversionTable = [ordered] @{ 1000 = 'M' 900 = 'CM' 500 = 'D' 400 = 'CD' 100 = 'C' 90 = 'XC' 50 = 'L' 40 = 'XL' 10 = 'X' 9 = 'IX' 5 = 'V' 4 = 'IV' 1 = 'I' } } process { $romanNumeralBuilder = New-Object -TypeName System.Text.StringBuilder do { foreach ($romanNumeral in $conversionTable.GetEnumerator()) { if ($Value -ge $romanNumeral.Key) { [ref] $null = $romanNumeralBuilder.Append($romanNumeral.Value) $Value -= $romanNumeral.Key break } } } until ($Value -eq 0) return $romanNumeralBuilder.ToString() } } function ConvertTo-Twips { <# .SYNOPSIS Convert millimeters into twips .NOTES 1 twip = 1/20th pt #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')] [OutputType([System.Single])] param ( [Parameter(Mandatory, ValueFromPipeline)] [Alias('mm','Millimetre')] [System.Single] $Millimeter ) process { $twips = (ConvertTo-In -Millimeter $Millimeter) * 1440 return [System.Math]::Round($twips, 2) } } function Copy-Object { <# .SYNOPSIS Clones a .NET object by serializing and deserializing it. .NOTES PowerShell 7.4 throws a "Type 'System.Management.Automation.PSObject' is not marked as serializable." exception. This function has been replaced by 'ConvertTo-Json | ConvertFrom-Json' to serialize and deserialize into a "clone" object. This works for PScribo objects but may not work for other nested objects in script blocks. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.Object] $InputObject ) process { try { $stream = New-Object IO.MemoryStream $formatter = New-Object Runtime.Serialization.Formatters.Binary.BinaryFormatter $formatter.Serialize($stream, $InputObject) $stream.Position = 0 return $formatter.Deserialize($stream) } catch { Write-Error -ErrorRecord $_ } finally { $stream.Dispose() } } } function Get-ImageMimeType { <# .SYNOPSIS Returns an image's Mime type #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Drawing.Image] $Image ) process { if ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Jpeg)) { return 'image/jpeg' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Png)) { return 'image/png' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Bmp)) { return 'image/bmp' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Emf)) { return 'image/emf' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Gif)) { return 'image/gif' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Icon)) { return 'image/icon' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Tiff)) { return 'image/tiff' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Wmf)) { return 'image/wmf' } elseif ($Image.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Exif)) { return 'image/exif' } else { return 'image/unknown' } } } function Get-PScriboDocumentStyle { <# .SYNOPSIS Returns document style or table style. .NOTES Enables testing without having to generate a mock document object! #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'Style')] [System.String] $Style, [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'TableStyle')] [System.String] $TableStyle ) process { if ($PSCmdlet.ParameterSetName -eq 'Style') { return $Document.Styles[$Style] } elseif ($PSCmdlet.ParameterSetName -eq 'TableStyle') { return $Document.TableStyles[$TableStyle] } } } function Get-PScriboHeaderFooter { <# .SYNOPSIS Returns the specified PScribo header/footer object. #> [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 { if ($FirstPage) { if ($Header -and ($Document.Header.HasFirstPageHeader)) { return $Document.Header.FirstPageHeader } elseif ($Footer -and ($Document.Footer.HasFirstPageFooter)) { return $Document.Footer.FirstPageFooter } } else { if ($Header -and ($Document.Header.HasDefaultHeader)) { return $Document.Header.DefaultHeader } elseif ($Footer -and ($Document.Footer.HasDefaultFooter)) { return $Document.Footer.DefaultFooter } } } } function Get-PScriboImage { <# .SYNOPSIS Retrieves PScribo.Images in a document/section #> [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.Management.Automation.PSObject[]] $Section, [Parameter(ValueFromPipelineByPropertyName)] [System.String[]] $Id ) process { foreach ($subSection in $Section) { if ($subSection.Type -eq 'PScribo.Image') { if ($PSBoundParameters.ContainsKey('Id')) { if ($subSection.Id -in $Id) { Write-Output -InputObject $subSection } } else { Write-Output -InputObject $subSection } } elseif ($subSection.Type -eq 'PScribo.Section') { if ($subSection.Sections.Count -gt 0) { ## Recursively search subsections $PSBoundParameters['Section'] = $subSection.Sections Get-PScriboImage @PSBoundParameters } } } } } function Get-PScriboParagraphRun { <# .SYNOPSIS Converts a string of text into multiple strings to create Word paragraph runs for PageNumber and TotalPages field replacements. #> [CmdletBinding()] param ( ## Paragraph text to convert into Word paragraph runs for field replacement [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.String] $Text, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Xml.XmlDocument] $XmlDocument, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Xml.XmlElement] $XmlElement ) process { $xmlns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' $pageNumberMatch = '<!#\s*PageNumber\s*#!>' $totalPagesMatch = '<!#\s*TotalPages\s*#!>' $paragraphRuns = New-Object -TypeName 'System.Collections.ArrayList' $pageNumbers = $Text -split $pageNumberMatch for ($pn = 0; $pn -lt $pageNumbers.Count; $pn++) { $totalPages = $pageNumbers[$pn] -split $totalPagesMatch for ($tp = 0; $tp -lt $totalPages.Count; $tp++) { $null = $paragraphRuns.Add($totalPages[$tp]) if ($tp -lt ($totalPages.Count -1)) { $null = $paragraphRuns.Add('<!#TOTALPAGES#!>') } } if ($pn -lt ($pageNumbers.Count -1)) { $null = $paragraphRuns.Add('<!#PAGENUMBER#!>') } } foreach ($run in $paragraphRuns) { if ($run -match '<!#(TOTALPAGES|PAGENUMBER)#!>') { $r1 = $XmlElement.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar1 = $r1.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar1.SetAttribute('fldCharType', $xmlns, 'begin') $r2 = $XmlElement.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $instrText = $r2.AppendChild($XmlDocument.CreateElement('w', 'instrText', $xmlns)) [ref] $null = $instrText.SetAttribute('space', 'http://www.w3.org/XML/1998/namespace', 'preserve') if ($run -match '<!#PAGENUMBER#!>') { [ref] $null = $instrText.AppendChild($XmlDocument.CreateTextNode(' PAGE \* MERGEFORMAT ')) } elseif ($run -match '<!#TOTALPAGES#!>') { [ref] $null = $instrText.AppendChild($XmlDocument.CreateTextNode(' NUMPAGES \* MERGEFORMAT ')) } $r3 = $XmlElement.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar2 = $r3.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar2.SetAttribute('fldCharType', $xmlns, 'separate') $r4 = $XmlElement.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $t2 = $r4.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t2.AppendChild($XmlDocument.CreateTextNode('1')) $r5 = $XmlElement.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $fldChar3 = $r5.AppendChild($XmlDocument.CreateElement('w', 'fldChar', $xmlns)) [ref] $null = $fldChar3.SetAttribute('fldCharType', $xmlns, 'end') } else { $r = $XmlElement.AppendChild($XmlDocument.CreateElement('w', 'r', $xmlns)) $t = $r.AppendChild($XmlDocument.CreateElement('w', 't', $xmlns)) [ref] $null = $t.SetAttribute('space', 'http://www.w3.org/XML/1998/namespace', 'preserve') [ref] $null = $t.AppendChild($XmlDocument.CreateTextNode($run)) } } } } function Get-PScriboPlugin { <# .SYNOPSIS Returns available PScribo plugins. #> [CmdletBinding()] param ( ) process { Get-ChildItem -Path (Join-Path -Path $pscriboRoot -ChildPath '\Src\Plugins') | Select-Object -ExpandProperty Name } } function Get-UriBytes { <# .SYNOPSIS Gets an image's content as a byte[] #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')] [OutputType([System.Byte[]])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.Uri] $Uri ) process { try { $webClient = New-Object -TypeName 'System.Net.WebClient' [System.IO.Stream] $contentStream = $webClient.OpenRead($uri.AbsoluteUri) [System.IO.MemoryStream] $memoryStream = New-Object System.IO.MemoryStream $contentStream.CopyTo($memoryStream) return $memoryStream.ToArray() } catch { $_ } finally { if ($null -ne $memoryStream) { $memoryStream.Close() } if ($null -ne $contentStream) { $contentStream.Close() } if ($null -ne $webClient) { $webClient.Dispose() } } } } function Invoke-PScriboListLevel { <# .SYNOPSIS Nested function that processes each nested list numbering. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNull()] [System.Management.Automation.PSObject] $List, [Parameter(Mandatory)] [AllowEmptyString()] [System.String] $Number ) process { $level = $Number.Split('.').Count +1 $processingListPadding = ''.PadRight($level -1, ' ') $processingListMessage = $localized.ProcessingList -f $List.Name Write-PScriboMessage -Message ('{0}{1}' -f $processingListPadding, $processingListMessage) $itemNumber = 0 $numberString = $Number $hasItem = $false foreach ($item in $List.Items) { if ($item.Type -eq 'PScribo.Item') { $itemNumber++ $item.Level = $level $item.Index = $itemNumber $item.Number = ('{0}.{1}' -f $Number, $itemNumber).TrimStart('.') $numberString = $item.Number $hasItem = $true } elseif ($item.Type -eq 'PScribo.List') { if ($hasItem) { Invoke-PScriboListLevel -List $item -Number $numberString } else { Write-PScriboMessage -Message $localized.NoPriorListItemWarning -IsWarning } } } } } function Invoke-PScriboSection { <# .SYNOPSIS Processes the document/TOC section versioning each level, i.e. 1.2.2.3 .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] param ( ) process { $majorNumber = 1; foreach ($s in $pscriboDocument.Sections) { if ($s.Type -like '*.Section') { if ($pscriboDocument.Options['ForceUppercaseSection']) { $s.Name = $s.Name.ToUpper(); } if (-not $s.IsExcluded) { Invoke-PScriboSectionLevel -Section $s -Number $majorNumber; $majorNumber++; } } } } } function Invoke-PScriboSectionLevel { <# .SYNOPSIS Nested function that processes each document/TOC nested section #> [CmdletBinding()] param ( [Parameter(Mandatory)] [ValidateNotNull()] [System.Management.Automation.PSObject] $Section, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $Number ) process { if ($pscriboDocument.Options['ForceUppercaseSection']) { $Section.Name = $Section.Name.ToUpper(); } ## Set this section's level $Section.Number = $Number; $Section.Level = $Number.Split('.').Count -1; ### Add to the TOC $tocEntry = [PScustomObject] @{ Id = $Section.Id; Number = $Number; Level = $Section.Level; Name = $Section.Name; } [ref] $null = $pscriboDocument.TOC.Add($tocEntry); ## Set sub-section level seed $minorNumber = 1; foreach ($s in $Section.Sections) { if ($s.Type -like '*.Section' -and -not $s.IsExcluded) { $sectionNumber = ('{0}.{1}' -f $Number, $minorNumber).TrimStart('.'); ## Calculate section version Invoke-PScriboSectionLevel -Section $s -Number $sectionNumber; $minorNumber++; } } #end foreach section } } function Merge-PScriboPluginOption { <# .SYNOPSIS Merges the specified options along with the plugin-specific default options. #> [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( ## Default/document options [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [AllowNull()] [System.Collections.Hashtable] $DocumentOptions, ## Default plugin options to merge [Parameter(ValueFromPipelineByPropertyName)] [System.Collections.Hashtable] $DefaultPluginOptions, ## Specified runtime plugin options [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()] [System.Collections.Hashtable] $PluginOptions ) process { $mergedOptions = $DocumentOptions.Clone(); if ($null -ne $DefaultPluginOptions) { ## Overwrite the default document option with the plugin default option/value foreach ($option in $DefaultPluginOptions.GetEnumerator()) { $mergedOptions[$($option.Key)] = $option.Value; } } if ($null -ne $PluginOptions) { ## Overwrite the default document/plugin default option/value with the specified/runtime option foreach ($option in $PluginOptions.GetEnumerator()) { $mergedOptions[$($option.Key)] = $option.Value; } } return $mergedOptions; } } function New-PScriboBlankLine { <# .SYNOPSIS Initializes a new PScribo blank line break. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(ValueFromPipeline)] [System.UInt32] $Count = 1 ) process { $typeName = 'PScribo.BlankLine'; $pscriboDocument.Properties['BlankLines']++; $pscriboBlankLine = [PSCustomObject] @{ Id = [System.Guid]::NewGuid().ToString(); LineCount = $Count; Type = $typeName; } return $pscriboBlankLine; } } function New-PScriboDocument { <# .SYNOPSIS Initializes a new PScript document object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseLiteralInitializerForHashtable','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo document name [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## PScribo document Id [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Id = $Name.Replace(' ','') ) begin { if ($(Test-CharsInPath -Path $Name -SkipCheckCharsInFolderPart -Verbose:$false) -eq 3 ) { throw -Message ($localized.IncorrectCharsInName) } } process { Write-PScriboMessage -Message ($localized.DocumentProcessingStarted -f $Name) $typeName = 'PScribo.Document' $pscriboDocument = [PSCustomObject] @{ Id = $Id.ToUpper() Type = $typeName Name = $Name Sections = New-Object -TypeName System.Collections.ArrayList Options = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase) Properties = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase) Styles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase) TableStyles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase) NumberStyles = New-Object -TypeName System.Collections.Hashtable([System.StringComparer]::InvariantCultureIgnoreCase) Lists = New-Object -TypeName System.Collections.ArrayList # Store all list references for Word numbering.xml generation DefaultStyle = $null DefaultTableStyle = $null DefaultNumberStyle = $null Header = [PSCustomObject] @{ HasFirstPageHeader = $false HasDefaultHeader = $false FirstPageHeader = $null DefaultHeader = $null } Footer = [PSCustomObject] @{ HasFirstPageFooter = $false HasDefaultFooter = $false FirstPageFooter = $null DefaultFooter = $null } TOC = New-Object -TypeName System.Collections.ArrayList } $defaultDocumentOptionParams = @{ MarginTopAndBottom = 72 MarginLeftAndRight = 54 Orientation = 'Portrait' PageSize = 'A4' DefaultFont = 'Calibri','Candara','Segoe','Segoe UI','Optima','Arial','Sans-Serif' } DocumentOption @defaultDocumentOptionParams -Verbose:$false ## Set "default" styles Style -Name Normal -Size 11 -Default -Verbose:$false Style -Name Title -Size 28 -Color '0072af' -Verbose:$false Style -Name TOC -Size 16 -Color '0072af' -Hide -Verbose:$false Style -Name 'Heading 1' -Size 16 -Color '0072af' -Verbose:$false Style -Name 'Heading 2' -Size 14 -Color '0072af' -Verbose:$false Style -Name 'Heading 3' -Size 12 -Color '0072af' -Verbose:$false Style -Name 'Heading 4' -Size 11 -Color '2f5496' -Italic -Verbose:$false Style -Name 'Heading 5' -Size 11 -Color '2f5496' -Verbose:$false Style -Name 'Heading 6' -Size 11 -Color '1f3763' -Verbose:$false Style -Name TableDefaultHeading -Size 11 -Color 'fff' -BackgroundColor '4472c4' -Bold -Verbose:$false Style -Name TableDefaultRow -Size 11 -Verbose:$false Style -Name TableDefaultAltRow -Size 11 -BackgroundColor 'd0ddee' -Verbose:$false Style -Name Caption -Size 11 -Italic -Verbose:$false $tableDefaultStyleParams = @{ Id = 'TableDefault' BorderWidth = 1 BorderColor = '2a70be' HeaderStyle = 'TableDefaultHeading' RowStyle = 'TableDefaultRow' AlternateRowStyle = 'TableDefaultAltRow' CaptionStyle = 'Caption' } TableStyle @tableDefaultStyleParams -Default -Verbose:$false NumberStyle -Id 'Number' -Format Number -Default -Verbose:$false NumberStyle -Id 'Letter' -Format Letter -Verbose:$false NumberStyle -Id 'Roman' -Format Roman -Verbose:$false return $pscriboDocument } } function New-PScriboFormattedTable { <# .SYNOPSIS Creates a formatted table with row and column styling for plugin output/rendering. .NOTES Maintains backwards compatibility with other plugins that do not require styling/formatting. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSObject])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Table, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $HasHeaderRow, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $HasHeaderColumn ) process { $tableStyle = $document.TableStyles[$Table.Style] return [PSCustomObject] @{ Id = $Table.Id Name = $Table.Name Number = $Table.Number Type = $Table.Type ColumnWidths = $Table.ColumnWidths Rows = New-Object -TypeName System.Collections.ArrayList Width = $Table.Width Tabs = if ($null -eq $Table.Tabs) { 0 } else { $Table.Tabs } PaddingTop = $tableStyle.PaddingTop PaddingLeft = $tableStyle.PaddingLeft PaddingBottom = $tableStyle.PaddingBottom; PaddingRight = $tableStyle.PaddingRight Align = $tableStyle.Align BorderWidth = $tableStyle.BorderWidth BorderStyle = $tableStyle.BorderStyle BorderColor = $tableStyle.BorderColor Style = $Table.Style HasHeaderRow = $HasHeaderRow ## First row has header style applied (list/combo list tables) HasHeaderColumn = $HasHeaderColumn ## First column has header style applied (list/combo list tables) HasCaption = $Table.HasCaption Caption = $Table.Caption IsList = $Table.IsList IsKeyedList = $Table.IsKeyedList } } } function New-PScriboFormattedTableRow { <# .SYNOPSIS Creates a formatted table row for plugin output/rendering. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSObject])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.String] $TableStyle, [Parameter(ValueFromPipeline)] [AllowNull()] [System.String] $Style = $null, [Parameter(ValueFromPipeline, ParameterSetName = 'Header')] [System.Management.Automation.SwitchParameter] $IsHeaderRow, [Parameter(ValueFromPipeline, ParameterSetName = 'Row')] [System.Management.Automation.SwitchParameter] $IsAlternateRow ) process { if (-not ([System.String]::IsNullOrEmpty($Style))) { ## Use the explictit style $IsStyleInherited = $false } elseif ($IsHeaderRow) { $Style = $document.TableStyles[$TableStyle].HeaderStyle $IsStyleInherited = $true } elseif ($IsAlternateRow) { $Style = $document.TableStyles[$TableStyle].AlternateRowStyle $IsStyleInherited = $true } else { $Style = $document.TableStyles[$TableStyle].RowStyle $IsStyleInherited = $true } return [PSCustomObject] @{ Style = $Style; IsStyleInherited = $IsStyleInherited; Cells = New-Object -TypeName System.Collections.ArrayList; } } } function New-PScriboFormattedTableRowCell { <# .SYNOPSIS Creates a formatted table cell for plugin output/rendering. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSObject])] param ( [Parameter(ValueFromPipeline)] [AllowNull()] [System.String[]] $Content, [Parameter(ValueFromPipelineByPropertyName)] [System.Uint16] $Width, [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()] [System.String] $Style = $null ) process { $isStyleInherited = $true $combinedContent = $Content if ($Content -is [System.Array]) { $combinedContent = [System.String]::Join([System.Environment]::NewLine, $Content) } if (-not ([System.String]::IsNullOrEmpty($Style))) { ## Use the explictit style $isStyleInherited = $false } return [PSCustomObject] @{ Content = $combinedContent Width = $Width Style = $Style IsStyleInherited = $isStyleInherited } } } function New-PScriboHeaderFooter { <# .SYNOPSIS Initializes a new PScribo header/footer object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','Footer')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Header')] [System.Management.Automation.SwitchParameter] $Header, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Footer')] [System.Management.Automation.SwitchParameter] $Footer ) begin { ## Ignores dot-sourced script blocks, i.e. style scripts $psCallStack = Get-PSCallStack | Where-Object { $_.FunctionName -ne '<ScriptBlock>' } if ($psCallStack[2].FunctionName -ne 'Document<Process>') { throw $localized.HeaderFooterDocumentRootError } } process { $pscriboHeaderFooter = [PSCustomObject] @{ Type = if ($Header) { 'PScribo.Header' } else { 'PScribo.Footer' } Sections = (New-Object -TypeName System.Collections.ArrayList) } return $pscriboHeaderFooter } } function New-PScriboImage { <# .SYNOPSIS Initializes a new PScribo Image object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding(DefaultParameterSetName = 'UriSize')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Mandatory, ParameterSetName = 'UriSize')] [Parameter(Mandatory, ParameterSetName = 'UriPercent')] [System.String] $Uri, [Parameter(Mandatory, ParameterSetName = 'Base64Size')] [Parameter(Mandatory, ParameterSetName = 'Base64Percent')] [System.String] $Base64, [Parameter(ParameterSetName = 'UriSize')] [Parameter(ParameterSetName = 'Base64Size')] [System.UInt32] $Height, [Parameter(ParameterSetName = 'UriSize')] [Parameter(ParameterSetName = 'Base64Size')] [System.UInt32] $Width, [Parameter(Mandatory, ParameterSetName = 'UriPercent')] [Parameter(Mandatory, ParameterSetName = 'Base64Percent')] [System.UInt32] $Percent, [Parameter()] [ValidateSet('Left','Center','Right')] [System.String] $Align = 'Left', [Parameter(Mandatory, ParameterSetName = 'Base64Size')] [Parameter(Mandatory, ParameterSetName = 'Base64Percent')] [Parameter(ParameterSetName = 'UriSize')] [Parameter(ParameterSetName = 'UriPercent')] [System.String] $Text = $Uri, [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $Id = [System.Guid]::NewGuid().ToString() ) process { $imageNumber = [System.Int32] $pscriboDocument.Properties['Images']++; if ($PSBoundParameters.ContainsKey('Uri')) { $imageBytes = Get-UriBytes -Uri $Uri } elseif ($PSBoundParameters.ContainsKey('Base64')) { $imageBytes = [System.Convert]::FromBase64String($Base64) } $image = ConvertTo-Image -Bytes $imageBytes if ($PSBoundParameters.ContainsKey('Percent')) { $Width = ($image.Width / 100) * $Percent $Height = ($image.Height / 100) * $Percent } elseif (-not ($PSBoundParameters.ContainsKey('Width')) -and (-not $PSBoundParameters.ContainsKey('Height'))) { $Width = $image.Width $Height = $image.Height } $pscriboImage = [PSCustomObject] @{ Id = $Id; ImageNumber = $imageNumber; Text = $Text Type = 'PScribo.Image'; Bytes = $imageBytes; Uri = $Uri; Name = 'Image{0}' -f $imageNumber; Align = $Align; MIMEType = Get-ImageMimeType -Image $image WidthEm = ConvertTo-Em -Pixel $Width; HeightEm = ConvertTo-Em -Pixel $Height; Width = $Width; Height = $Height; } return $pscriboImage; } } function New-PScriboItem { <# .SYNOPSIS Initializes new PScribo list item object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding(DefaultParameterSetName = 'Default')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.String] $Text, [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 ) process { $pscriboItem = [PSCustomObject] @{ Id = [System.Guid]::NewGuid().ToString() Level = 0 Index = 0 Number = '' Text = $Text Type = 'PScribo.Item' Style = $Style Bold = $Bold Italic = $Italic Underline = $Underline Font = $Font Size = $Size Color = $Color IsStyleInherited = $PSCmdlet.ParameterSetName -eq 'Default' HasStyle = $PSCmdlet.ParameterSetName -eq 'Style' HasInlineStyle = $PSCmdlet.ParameterSetName -eq 'Inline' } return $pscriboItem } } function New-PScriboLineBreak { <# .SYNOPSIS Initializes a new PScribo line break object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Id = [System.Guid]::NewGuid().ToString() ) process { $typeName = 'PScribo.LineBreak'; $pscriboDocument.Properties['LineBreaks']++; $pscriboLineBreak = [PSCustomObject] @{ Id = $Id; Type = $typeName; } return $pscriboLineBreak; } } function New-PScriboList { <# .SYNOPSIS Initializes new PScribo list object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## Display name used in verbose output when processing. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Name, ## List style Name/Id reference. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Style, ## Numbered list. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Numbered, ## Numbered list style. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $NumberStyle = $pscriboDocument.DefaultNumberStyle, ## Bullet list style. [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Circle', 'Dash', 'Disc', 'Square')] [System.String] $BulletStyle = 'Disc', [Parameter(ValueFromPipelineByPropertyName)] [System.Int32] $Level = 1 ) process { $pscriboList = [PSCustomObject] @{ Id = [System.Guid]::NewGuid().ToString() Name = $Name Type = 'PScribo.List' Items = (New-Object -TypeName System.Collections.ArrayList) Number = 0 Level = $Level IsNumbered = $Numbered.ToBool() Style = $Style BulletStyle = $BulletStyle NumberStyle = $NumberStyle IsMultiLevel = $false IsSectionBreak = $false IsSectionBreakEnd = $false IsStyleInherited = -not $PSBoundParameters.ContainsKey('Style') HasStyle = $PSBoundParameters.ContainsKey('Style') HasBulletStyle = -not $Numbered.ToBool() HasNumberStyle = $PSBoundParameters.ContainsKey('NumberStyle') } return $pscriboList } } function New-PScriboListReference { <# .SYNOPSIS Initializes new PScribo list reference object. .DESCRIPTION Creates a placeholder reference to a list stored in $pscriboDocument.Lists. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## Display name used in verbose output when processing. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Name, [Parameter(ValueFromPipelineByPropertyName)] [System.Int32] $Number ) process { $pscriboListReference = [PSCustomObject] @{ Id = [System.Guid]::NewGuid().ToString() Name = $Name Type = 'PScribo.ListReference' Number = $Number } return $pscriboListReference } } function New-PScriboPageBreak { <# .SYNOPSIS Creates a PScribo page break object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Id = [System.Guid]::NewGuid().ToString() ) process { $typeName = 'PScribo.PageBreak'; $pscriboDocument.Properties['PageBreaks']++; $pscriboDocument.Properties['Pages']++; $pscriboPageBreak = [PSCustomObject] @{ Id = $Id; Type = $typeName; } return $pscriboPageBreak; } } function New-PScriboParagraph { <# .SYNOPSIS Initializes a new PScribo paragraph object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter','ScriptBlock')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo paragraph run script block. [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Management.Automation.ScriptBlock] $ScriptBlock, ## Paragraph Id. [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.String] $Id = [System.Guid]::NewGuid().ToString(), ## Paragraph style Name/Id reference. [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()] [System.String] $Style = $null, ## Tab indent [Parameter(ValueFromPipelineByPropertyName)] [ValidateRange(0,10)] [System.Int32] $Tabs = 0, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoIncrementCounter ) process { if (-not $NoIncrementCounter) { $pscriboDocument.Properties['Paragraphs']++ } $pscriboParagraph = [PSCustomObject] @{ Id = $Id Type = 'PScribo.Paragraph' Style = $Style Tabs = $Tabs Sections = (New-Object -TypeName System.Collections.ArrayList) Orientation = $script:currentOrientation IsSectionBreakEnd = $false } return $pscriboParagraph } } function New-PScriboParagraphRun { <# .SYNOPSIS Initializes a new PScribo paragraph run (text) object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding(DefaultParameterSetName = 'Default')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] 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 ) process { $pscriboParagraphRun = [PSCustomObject] @{ Type = 'PScribo.ParagraphRun' Text = $Text Style = $Style NoSpace = $NoSpace Bold = $Bold Italic = $Italic Underline = $Underline Font = $Font Size = $Size Color = $Color IsStyleInherited = $PSCmdlet.ParameterSetName -eq 'Default' HasStyle = $PSCmdlet.ParameterSetName -eq 'Style' HasInlineStyle = $PSCmdlet.ParameterSetName -eq 'Inline' IsParagraphRunEnd = $false Name = $null # For legacy Xml output Value = $null # For legacy Xml output } return $pscriboParagraphRun } } function New-PScriboSection { <# .SYNOPSIS Initializes new PScribo section object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PScribo section heading/name. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $Name, ## 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] $IsExcluded, ## Tab indent [Parameter()] [ValidateRange(0,10)] [System.Int32] $Tabs = 0, ## Section orientation [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Portrait','Landscape')] [System.String] $Orientation ) begin { ## Ensure we only have one 'Section' in the call stack (#121) $psCallStack = @(Get-PSCallStack | Where-Object { $_.FunctionName -eq 'Section<Process>' }) if ($PSBoundParameters.ContainsKey('Orientation') -and ($psCallStack.Count -gt 1)) { Write-PScriboMessage -Message $localized.CannotSetOrientationWarning -IsWarning; $null = $PSBoundParameters.Remove('Orientation') } } process { $typeName = 'PScribo.Section'; $pscriboDocument.Properties['Sections']++; $pscriboSection = [PSCustomObject] @{ Id = [System.Guid]::NewGuid().ToString(); Level = 0; Number = ''; Name = $Name; Type = $typeName; Style = $Style; Tabs = $Tabs; IsExcluded = $IsExcluded; Sections = (New-Object -TypeName System.Collections.ArrayList); Orientation = if ($PSBoundParameters.ContainsKey('Orientation')) { $Orientation } else { $script:currentOrientation } IsSectionBreak = $false; IsSectionBreakEnd = $false; } ## Has the orientation changed from the parent scope if ($PSBoundParameters.ContainsKey('Orientation') -and ($Orientation -ne $script:currentOrientation)) { $pscriboSection.IsSectionBreak = $true; $pscriboDocument.Properties['Pages']++; $script:currentOrientation = $Orientation; } return $pscriboSection; } } function New-PScriboTable { <# .SYNOPSIS Initializes a new PScribo table object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## Table name/Id [Parameter(ValueFromPipelineByPropertyName, Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Name = ([System.Guid]::NewGuid().ToString()), ## Table columns/display order [Parameter(Mandatory)] [AllowNull()] [System.String[]] $Columns, ## Table columns widths [Parameter(Mandatory)] [AllowNull()] [System.UInt16[]] $ColumnWidths, ## Collection of PScriboTableObjects for table rows [Parameter(Mandatory)] [ValidateNotNull()] [System.Collections.ArrayList] $Rows, ## Table style [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $Style, # List view [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'List')] [System.Management.Automation.SwitchParameter] $List, ## Combine list view based upon the specified key/property name [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'List')] [System.String] $ListKey = $null, ## Display the key name in the table output [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'List')] [System.Management.Automation.SwitchParameter] $DisplayListKey, ## Table width (%), 0 = Autofit [Parameter(ValueFromPipelineByPropertyName)] [ValidateRange(0, 100)] [System.UInt16] $Width = 100, ## Indent table [Parameter(ValueFromPipelineByPropertyName)] [ValidateRange(0, 10)] [System.UInt16] $Tabs, [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Caption ) process { $typeName = 'PScribo.Table' $pscriboDocument.Properties['Tables']++ $pscriboTable = [PSCustomObject] @{ Id = $Name.Replace(' ', $pscriboDocument.Options['SpaceSeparator']).ToUpper() Name = $Name Number = $pscriboDocument.Properties['Tables'] Type = $typeName Columns = $Columns ColumnWidths = $ColumnWidths Rows = $Rows Style = $Style Width = $Width Tabs = $Tabs HasCaption = $PSBoundParameters.ContainsKey('Caption') Caption = $Caption CaptionNumber = 0 IsList = $List IsKeyedList = $PSBoundParameters.ContainsKey('ListKey') ListKey = $ListKey DisplayListKey = $DisplayListKey.ToBool() Orientation = $script:currentOrientation IsSectionBreakEnd = $false } ## Remove captions from List tables where there is more than one row if ($pscriboTable.HasCaption) { if (($pscriboTable.IsList) -and (-not $pscriboTable.IsKeyedList)) { if ($pscriboTable.Rows.Count -gt 1) { Write-PScriboMessage -Message ($localized.ListTableCaptionRemovedWarning -f $Name) -IsWarning $pscriboTable.HasCaption = $false $pscriboTable.Caption = $null } } ## Do we still have a caption?! if ($pscriboTable.HasCaption) { $pscriboDocument.Properties['TableCaptions']++ $pscriboTable.CaptionNumber = $pscriboDocument.Properties['TableCaptions'] } } return $pscriboTable } } function New-PScriboTableRow { <# .SYNOPSIS Defines a new PScribo document table row from an object or hashtable. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding(DefaultParameterSetName = 'InputObject')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( ## PSCustomObject to create PScribo table row [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'InputObject')] [System.Object] $InputObject, ## PSCutomObject properties to include in the table row [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'InputObject')] [AllowNull()] [System.String[]] $Properties, # Custom table header strings (in Display Order). Used for property names. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'InputObject')] [AllowNull()] [System.String[]] $Headers = $null, ## Array of ordered dictionaries (hashtables) to create PScribo table row [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Hashtable')] [System.Collections.Specialized.OrderedDictionary] $Hashtable ) begin { Write-Debug ('Using parameter set "{0}.' -f $PSCmdlet.ParameterSetName) } process { switch ($PSCmdlet.ParameterSetName) { 'Hashtable' { if (-not $Hashtable.Contains('__Style')) { $Hashtable['__Style'] = $null } ## Create and return custom object from hashtable $psCustomObject = [PSCustomObject] $Hashtable return $psCustomObject } Default { $objectProperties = [Ordered] @{ } if ($Properties -notcontains '__Style') { $Properties += '__Style' } ## Build up hashtable of required property names for ($i = 0; $i -lt $Properties.Count; $i++) { $propertyName = $Properties[$i] $propertyStyleName = '{0}__Style' -f $propertyName if ($InputObject.PSObject.Properties[$propertyStyleName]) { if ($Headers) { ## Rename the style property to match the header $headerStyleName = '{0}__Style' -f $Headers[$i] $objectProperties[$headerStyleName] = $InputObject.$propertyStyleName } else { $objectProperties[$propertyStyleName] = $InputObject.$propertyStyleName } } if ($Headers -and $PropertyName -notlike '*__Style') { if ($InputObject.PSObject.Properties[$propertyName]) { $objectProperties[$Headers[$i]] = $InputObject.$propertyName } } else { if ($InputObject.PSObject.Properties[$propertyName]) { $objectProperties[$propertyName] = $InputObject.$propertyName } else { $objectProperties[$propertyName] = $null } } } ## Create and return custom object return ([PSCustomObject] $objectProperties) } } } } function New-PScriboTOC { <# .SYNOPSIS Initializes a new PScribo Table of Contents (TOC) object. .NOTES This is an internal function and should not be called directly. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(ValueFromPipeline)] [ValidateNotNullOrEmpty()] [System.String] $Name = 'Contents', [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.String] $ClassId = 'TOC' ) process { $typeName = 'PScribo.TOC'; if ($pscriboDocument.Options['ForceUppercaseSection']) { $Name = $Name.ToUpper(); } $pscriboDocument.Properties['TOCs']++; $pscriboTOC = [PSCustomObject] @{ Id = [System.Guid]::NewGuid().ToString(); Name = $Name; Type = $typeName; ClassId = $ClassId; } return $pscriboTOC; } } function Resolve-ImageUri { <# .SYNOPSIS Converts an image path into a Uri. .NOTES A Uri includes information about whether the path is local etc. This is useful for plugins to be able to determine whether to embed images or not. #> [CmdletBinding()] [OutputType([System.Uri])] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.String] $Path ) process { if (Test-Path -Path $Path) { $Path = Resolve-Path -Path $Path } $uri = New-Object -TypeName System.Uri -ArgumentList @($Path) return $uri } } function Resolve-PScriboStyleColor { <# .SYNOPSIS Resolves a HTML color format or Word color constant to a RGB value #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline, Position = 0)] [ValidateNotNull()] [System.String] $Color ) begin { # http://www.jadecat.com/tuts/colorsplus.html $wordColorConstants = @{ AliceBlue = 'F0F8FF' AntiqueWhite = 'FAEBD7' Aqua = '00FFFF' Aquamarine = '7FFFD4' Azure = 'F0FFFF' Beige = 'F5F5DC' Bisque = 'FFE4C4' Black = '000000' BlanchedAlmond = 'FFEBCD' Blue = '0000FF' BlueViolet = '8A2BE2' Brown = 'A52A2A' BurlyWood = 'DEB887' CadetBlue = '5F9EA0' Chartreuse = '7FFF00' Chocolate = 'D2691E' Coral = 'FF7F50' CornflowerBlue = '6495ED' Cornsilk = 'FFF8DC' Crimson = 'DC143C' Cyan = '00FFFF' DarkBlue = '00008B' DarkCyan = '008B8B' DarkGoldenrod = 'B8860B' DarkGray = 'A9A9A9' DarkGreen = '006400' DarkKhaki = 'BDB76B' DarkMagenta = '8B008B' DarkOliveGreen = '556B2F' DarkOrange = 'FF8C00' DarkOrchid = '9932CC' DarkRed = '8B0000' DarkSalmon = 'E9967A' DarkSeaGreen = '8FBC8F' DarkSlateBlue = '483D8B' DarkSlateGray = '2F4F4F' DarkTurquoise = '00CED1' DarkViolet = '9400D3' DeepPink = 'FF1493' DeepSkyBlue = '00BFFF' DimGray = '696969' DodgerBlue = '1E90FF' Firebrick = 'B22222' FloralWhite = 'FFFAF0' ForestGreen = '228B22' Fuchsia = 'FF00FF' Gainsboro = 'DCDCDC' GhostWhite = 'F8F8FF' Gold = 'FFD700' Goldenrod = 'DAA520' Gray = '808080' Green = '008000' GreenYellow = 'ADFF2F' Honeydew = 'F0FFF0' HotPink = 'FF69B4' IndianRed = 'CD5C5C' Indigo = '4B0082' Ivory = 'FFFFF0' Khaki = 'F0E68C' Lavender = 'E6E6FA' LavenderBlush = 'FFF0F5' LawnGreen = '7CFC00' LemonChiffon = 'FFFACD' LightBlue = 'ADD8E6' LightCoral = 'F08080' LightCyan = 'E0FFFF' LightGoldenrodYellow = 'FAFAD2' LightGreen = '90EE90' LightGrey = 'D3D3D3' LightPink = 'FFB6C1' LightSalmon = 'FFA07A' LightSeaGreen = '20B2AA' LightSkyBlue = '87CEFA' LightSlateGray = '778899' LightSteelBlue = 'B0C4DE' LightYellow = 'FFFFE0' Lime = '00FF00' LimeGreen = '32CD32' Linen = 'FAF0E6' Magenta = 'FF00FF' Maroon = '800000' McMintGreen = 'BED6C9' MediumAuqamarine = '66CDAA' MediumBlue = '0000CD' MediumOrchid = 'BA55D3' MediumPurple = '9370D8' MediumSeaGreen = '3CB371' MediumSlateBlue = '7B68EE' MediumSpringGreen = '00FA9A' MediumTurquoise = '48D1CC' MediumVioletRed = 'C71585' MidnightBlue = '191970' MintCream = 'F5FFFA' MistyRose = 'FFE4E1' Moccasin = 'FFE4B5' NavajoWhite = 'FFDEAD' Navy = '000080' OldLace = 'FDF5E6' Olive = '808000' OliveDrab = '688E23' Orange = 'FFA500' OrangeRed = 'FF4500' Orchid = 'DA70D6' PaleGoldenRod = 'EEE8AA' PaleGreen = '98FB98' PaleTurquoise = 'AFEEEE' PaleVioletRed = 'D87093' PapayaWhip = 'FFEFD5' PeachPuff = 'FFDAB9' Peru = 'CD853F' Pink = 'FFC0CB' Plum = 'DDA0DD' PowderBlue = 'B0E0E6' Purple = '800080' Red = 'FF0000' RosyBrown = 'BC8F8F' RoyalBlue = '4169E1' SaddleBrown = '8B4513' Salmon = 'FA8072' SandyBrown = 'F4A460' SeaGreen = '2E8B57' Seashell = 'FFF5EE' Sienna = 'A0522D' Silver = 'C0C0C0' SkyBlue = '87CEEB' SlateBlue = '6A5ACD' SlateGray = '708090' Snow = 'FFFAFA' SpringGreen = '00FF7F' SteelBlue = '4682B4' Tan = 'D2B48C' Teal = '008080' Thistle = 'D8BFD8' Tomato = 'FF6347' Turquoise = '40E0D0' Violet = 'EE82EE' Wheat = 'F5DEB3' White = 'FFFFFF' WhiteSmoke = 'F5F5F5' Yellow = 'FFFF00' YellowGreen = '9ACD32' } } process { $pscriboColor = $Color if ($wordColorConstants.ContainsKey($pscriboColor)) { return $wordColorConstants[$pscriboColor].ToLower() } elseif ($pscriboColor.Length -eq 6 -or $pscriboColor.Length -eq 3) { $pscriboColor = '#{0}' -f $pscriboColor } elseif ($pscriboColor.Length -eq 7 -or $pscriboColor.Length -eq 4) { if (-not ($pscriboColor.StartsWith('#'))) { return $null } } if ($pscriboColor -notmatch '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$') { return $null } return $pscriboColor.TrimStart('#').ToLower() } } function Resolve-PScriboToken { <# .SYNOPSIS Replaces page number tokens with their (approximated) values. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [AllowEmptyString()] [System.String] $InputObject ) process { $tokenisedText = $InputObject -replace '<!#\s?PageNumber\s?#!>', $script:currentPageNumber -replace '<!#\s?TotalPages\s?#!>', $Document.Properties.Pages return $tokenisedText } } function Set-PScriboSectionBreakEnd { <# .SYNOPSIS Sets the IsSectionBreakEnd on the last (nested) paragraph/subsection (required by Word plugin) #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.PSObject] $Section ) process { $Section.Sections | Where-Object { $_.Type -in 'PScribo.Section','PScribo.Paragraph','PScribo.Table' } | Select-Object -Last 1 | ForEach-Object { if ($PSItem.Type -in 'PScribo.Paragraph','PScribo.Table') { $PSItem.IsSectionBreakEnd = $true } else { Set-PScriboSectionBreakEnd -Section $PSItem } } } } Function Test-CharsInPath { <# .SYNOPSIS PowerShell function intended to verify if in the string what is the path to file or folder are incorrect chars. .DESCRIPTION PowerShell function intended to verify if in the string what is the path to file or folder are incorrect chars. Exit codes - 0 - everything OK - 1 - nothing to check - 2 - an incorrect char found in the path part - 3 - an incorrect char found in the file name part - 4 - incorrect chars found in the path part and in the file name part .PARAMETER Path Specifies the path to an item for what path (location on the disk) need to be checked. The Path can be an existing file or a folder on a disk provided as a PowerShell object or a string e.g. prepared to be used in file/folder creation. .PARAMETER SkipCheckCharsInFolderPart Skip checking in the folder part of path. .PARAMETER SkipCheckCharsInFileNamePart Skip checking in the file name part of path. .PARAMETER SkipDividingForParts Skip dividing provided path to a directory and a file name. Used usually in conjuction with SkipCheckCharsInFolderPart or SkipCheckCharsInFileNamePart. .EXAMPLE [PS] > Test-CharsInPath -Path $(Get-Item C:\Windows\Temp\new.csv') -Verbose VERBOSE: The path provided as a string was devided to, directory part: C:\Windows\Temp ; file name part: new.csv 0 Testing existing file. Returned code means that all chars are acceptable in the name of folder and file. .EXAMPLE [PS] > Test-CharsInPath -Path "C:\newfolder:2\nowy|.csv" -Verbose VERBOSE: The path provided as a string was devided to, directory part: C:\newfolder:2\ ; file name part: nowy|.csv VERBOSE: The incorrect char | with the UTF code [124] found in FileName part 3 Testing the string if can be used as a file name. The returned value means that can't do to an unsupported char in the file name. .OUTPUTS Exit code as an integer number. See description section to find the exit codes descriptions. .LINK https://github.com/it-praktyk/New-OutputObject .LINK https://www.linkedin.com/in/sciesinskiwojciech .NOTES AUTHOR: Wojciech Sciesinski, wojciech[at]sciesinski[dot]net KEYWORDS: PowerShell, FileSystem REMARKS: # For Windows - based on the Power Tips # Finding Invalid File and Path Characters # http://community.idera.com/powershell/powertips/b/tips/posts/finding-invalid-file-and-path-characters # For PowerShell Core # https://docs.microsoft.com/en-us/dotnet/api/system.io.path.getinvalidpathchars?view=netcore-2.0 # https://www.dwheeler.com/essays/fixing-unix-linux-filenames.html # [char]0 = NULL CURRENT VERSION - 0.6.1 - 2017-07-23 HISTORY OF VERSIONS https://github.com/it-praktyk/New-OutputObject/CHANGELOG.md #> [cmdletbinding()] [OutputType([System.Int32])] param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] $Path, [parameter(Mandatory = $false)] [switch]$SkipCheckCharsInFolderPart, [parameter(Mandatory = $false)] [switch]$SkipCheckCharsInFileNamePart, [parameter(Mandatory = $false)] [switch]$SkipDividingForParts ) BEGIN { If (($PSVersionTable.ContainsKey('PSEdition')) -and ($PSVersionTable.PSEdition -eq 'Core') -and $IsLinux) { #[char]0 = NULL $PathInvalidChars = [char]0 $FileNameInvalidChars = @([char]0, '/') $PathSeparators = @('/') } Elseif (($PSVersionTable.ContainsKey('PSEdition')) -and ($PSVersionTable.PSEdition -eq 'Core') -and $IsMacOS) { $PathInvalidChars = [char]58 $FileNameInvalidChars = [char]58 $PathSeparators = @('/') } #Windows Else { $PathInvalidChars = [System.IO.Path]::GetInvalidPathChars() #36 chars $FileNameInvalidChars = [System.IO.Path]::GetInvalidFileNameChars() #41 chars #$FileOnlyInvalidChars = @(':', '*', '?', '\', '/') #5 chars - as a difference $PathSeparators = @('/','\') } $IncorectCharFundInPath = $false $IncorectCharFundInFileName = $false $NothingToCheck = $true } END { [String]$DirectoryPath = "" [String]$FileName = "" $PathType = ($Path.GetType()).Name If (@('DirectoryInfo', 'FileInfo') -contains $PathType) { If (($SkipCheckCharsInFolderPart.IsPresent -and $PathType -eq 'DirectoryInfo') -or ($SkipCheckCharsInFileNamePart.IsPresent -and $PathType -eq 'FileInfo')) { Return 1 } ElseIf ($PathType -eq 'DirectoryInfo') { [String]$DirectoryPath = $Path.FullName } elseif ($PathType -eq 'FileInfo') { [String]$DirectoryPath = $Path.DirectoryName [String]$FileName = $Path.Name } } ElseIf ($PathType -eq 'String') { If ( $SkipDividingForParts.IsPresent -and $SkipCheckCharsInFolderPart.IsPresent ) { $FileName = $Path } ElseIf ( $SkipDividingForParts.IsPresent -and $SkipCheckCharsInFileNamePart.IsPresent ) { $DirectoryPath = $Path } Else { #Convert String to Array of chars $PathArray = $Path.ToCharArray() $PathLength = $PathArray.Length For ($i = ($PathLength-1); $i -ge 0; $i--) { If ($PathSeparators -contains $PathArray[$i]) { [String]$DirectoryPath = [String]$Path.Substring(0, $i +1) break } } If ([String]::IsNullOrEmpty($DirectoryPath)) { [String]$FileName = [String]$Path } Else { [String]$FileName = $Path.Replace($DirectoryPath, "") } } } Else { [String]$MessageText = "Input object {0} can't be tested" -f ($Path.GetType()).Name Throw $MessageText } [String]$MessageText = "The path provided as a string was divided to: directory part: {0} ; file name part: {1} ." -f $DirectoryPath, $FileName Write-Verbose -Message $MessageText If ($SkipCheckCharsInFolderPart.IsPresent -and $SkipCheckCharsInFileNamePart.IsPresent) { Return 1 } If (-not ($SkipCheckCharsInFolderPart.IsPresent) -and -not [String]::IsNullOrEmpty($DirectoryPath)) { $NothingToCheck = $false foreach ($Char in $PathInvalidChars) { If ($DirectoryPath.ToCharArray() -contains $Char) { $IncorectCharFundInPath = $true [String]$MessageText = "The incorrect char {0} with the UTF code [{1}] found in the Path part." -f $Char, $([int][char]$Char) Write-Verbose -Message $MessageText } } } If (-not ($SkipCheckCharsInFileNamePart.IsPresent) -and -not [String]::IsNullOrEmpty($FileName)) { $NothingToCheck = $false foreach ($Char in $FileNameInvalidChars) { If ($FileName.ToCharArray() -contains $Char) { $IncorectCharFundInFileName = $true [String]$MessageText = "The incorrect char {0} with the UTF code [{1}] found in FileName part." -f $Char, $([int][char]$Char) Write-Verbose -Message $MessageText } } } If ($IncorectCharFundInPath -and $IncorectCharFundInFileName) { Return 4 } elseif ($NothingToCheck) { Return 1 } elseif ($IncorectCharFundInPath) { Return 2 } elseif ($IncorectCharFundInFileName) { Return 3 } Else { Return 0 } } } function Test-PScriboStyle { <# .SYNOPSIS Tests whether a style has been defined. #> [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory, ValueFromPipeline, Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Name ) process { return $PScriboDocument.Styles.ContainsKey($Name) } } function Test-PScriboStyleColor { <# .SYNOPSIS Tests whether a color string is a valid HTML color. #> [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory, ValueFromPipeline, Position = 0)] [ValidateNotNullOrEmpty()] [System.String] $Color ) process { if (Resolve-PScriboStyleColor -Color $Color) { return $true } else { return $false } } } function Write-PScriboProcessSectionId { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.String] $SectionId, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $SectionType, [Parameter(ValueFromPipelineByPropertyName)] [System.Int32] $Length = 40, [Parameter(ValueFromPipelineByPropertyName)] [System.Int32] $Indent = 0 ) process { if ($SectionId.Length -gt $Length) { $sectionDisplayName = '{0}[..]' -f $SectionId.Substring(0, ($Length -4)) } else { $sectionDisplayName = $SectionId } $writePScriboMessageParams = @{ Message = $localized.PluginProcessingSection -f $SectionType, $sectionDisplayName Indent = $Indent } Write-PScriboMessage @writePScriboMessageParams } } # SIG # Begin signature block # MIIuugYJKoZIhvcNAQcCoIIuqzCCLqcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBpcTbRyY7sRlDv # 3owFWshs/9CYxLiCUMTjPWzjqG09NaCCE6QwggWQMIIDeKADAgECAhAFmxtXno4h # 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 # DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQghMsd7p6u267ysAQqUymLkqEs # xz6p1474xox/kLOP/F0wDQYJKoZIhvcNAQEBBQAEggIAGYRFata3StebA5V7+C7q # vojFnDRXE8+CHIkr9ONLjcEwZiNrg9WF/j7NkJAmB1uqbk05kV+zcZeQampdNI3k # 2JqPQ7Jq+emI/UU50Z+PY9e87400iKD4gHoFqBaHARt6wy6gM4PgeWZ35FHKo4J7 # PDGQHVXvMyJC4dCwTXCUT3hcQ+RM8o4joc//HJYMgoHmnPnPS38MYlpIwRaenbvN # PsMfHBHzAKN/BheYIFVweuqPP5aHTU+9n4gSQC68+MhlZ37OoJXE3Z1vW8+E/Nkf # /MzYs2Y4pjkFdlzyzkBFRLvfnSMMFwLmsAdHbYl18D0jwkK2D6bTTCjXvNcYcJRx # PJUx5pIKTiT6ORAf96+VxPIgdSNPTfhZt8VJJ1gCpwClphm+O9iQqVWqETgCzBpp # XFBx3TEP3YUqnb5888wknq80BdVnsMFGShDpOdFN1RQ21jTpyGxPRIESU/iT/Q4V # tESqPIADhWIrZutHDBgkJvhtkAz3+VSFrO6dTNZ2Lzs54iy45XQ5wm74exCGM2ZA # Tk1oU9G4OYsMsuoWes0yPY5wHF8M58+Rl5/f7/cdqJRsK/5gFIk62ismOIK4j0im # 9im81MHE+f/RXQIAG3cTTvyCK3VPApka3+QMhpQ4Ai4d0B4AYi9Xb6JltNF/ahwI # D+IDrGleEUOEWy907yWzBCGhghc5MIIXNQYKKwYBBAGCNwMDATGCFyUwghchBgkq # hkiG9w0BBwKgghcSMIIXDgIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3DQEJ # EAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgR/3xQv+O # yw4cDByeGlDKO2IBiMXNU5v5WXSR1xxqzeICEHkQilH3WR/Ws9elYVp4+7EYDzIw # MjUwMzA3MDg1NTEzWqCCEwMwgga8MIIEpKADAgECAhALrma8Wrp/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 # SIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjUwMzA3MDg1 # NTEzWjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBTb04XuYtvSPnvk9nFIUIck1YZb # RTAvBgkqhkiG9w0BCQQxIgQgu6M7eCoXwRx8iw9O2qR2mBjFMb0fXN2k+BXO1tO5 # H8UwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgdnafqPJjLx9DCzojMK7WVnX+13Pb # BdZluQWTmEOPmtswDQYJKoZIhvcNAQEBBQAEggIAI1iZPtlheZQDkPMfsuDzjoPu # 12PplVIMYS//21V7JJqGy/0VAmFb36sWFwZUc9gv8O85w4ujmsKS4svXOi+GFrFt # cQ6jaoPqj9nPvD6XjknAU8ip+ShhBrFOt0ANt1nYcw9K8pQV4n5c6vlfMDmmxfF/ # rSSdlNss5ieu+yTwJvzu01N+OUoyuwM/jihlN/aX4F0XYt6NI8laKPE7F0qZDjqO # kAvQcnsKe+VXYLwzWlMRsoXkoWlvoT3ZlOTZKiGdF+uT3U+/Zoc2WnnMV35/z4M/ # MBkS7QOwgolc7i9lVWh2tRNT/7d4W0HavJYVbRpdx/0FSgPwx+tmr0TJEH+oZxSx # EibqTcP0IVXnfmSlq1lN6wtKW1nbpn2DBtdT7A7Zp3puJJ/+B7pYGnLoV53rj4dL # ZPOVH3e5U7m6dhxcHg1dXgQlDH7Sx8hATAJSl4mkEeYJuBE32gCb2xUQd94qorq1 # LdHphk05Hto8MtNfG3D7WSNFud9a9UuQtho588PX8QbAEDMWYJwZTRi2L7LVUfBi # iasZUNPVTDd4WYf7vF5KegJ8NbnhOD97FDU0FxpT+xl7Y8vr07yHACD+ClVct0Hs # FLhDPISDnXG8i5vGZFwhUTpWG/Iijf6sjdAaK9ki3Zns1hNi/R8wWF3ksPnVaqAA # dYBtMpXMv5qtLYIarWI= # SIG # End signature block |