Src/Plugins/Private/OutHtml.Internal.ps1
#region OutHtml Private Functions function New-PScriboHtmlOption { <# .SYNOPSIS Sets the text plugin specific formatting/output options. .NOTES All plugin options should be prefixed with the plugin name. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions','')] [OutputType([System.Collections.Hashtable])] param ( [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [System.Boolean] $NoPageLayoutStyle = $false ) process { return @{ NoPageLayoutStyle = $NoPageLayoutStyle; } } #end process } #end function New-PScriboHtmlOption function GetHtmlPaddedTableStyle { <# .SYNOPSIS Generates padded html stylesheet style attributes from a PScribo table style. #> [CmdletBinding()] [OutputType([System.String])] param ( ## PScribo document table style [Parameter(Mandatory, ValueFromPipeline)] [System.Object] $TableStyle ) process { $styleBuilder = New-Object -TypeName System.Text.StringBuilder; [ref] $null = $styleBuilder.AppendFormat(' padding: {0}rem {1}rem {2}rem {3}rem;', (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingTop)), (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingRight)), (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingBottom)), (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingLeft))) return $styleBuilder.ToString(); } } #end function function GetHtmlStyle { <# .SYNOPSIS Generates html stylesheet style attributes from a PScribo document style. #> [CmdletBinding()] [OutputType([System.String])] param ( ## PScribo document style [Parameter(Mandatory, ValueFromPipeline)] [System.Object] $Style ) process { $styleBuilder = New-Object -TypeName System.Text.StringBuilder; [ref] $null = $styleBuilder.AppendFormat(" font-family: '{0}';", $Style.Font -join "','"); ## Create culture invariant decimal https://github.com/iainbrighton/PScribo/issues/6 $invariantFontSize = ConvertToInvariantCultureString -Object ($Style.Size / 12) -Format 'f2'; [ref] $null = $styleBuilder.AppendFormat(' font-size: {0}rem;', $invariantFontSize); [ref] $null = $styleBuilder.AppendFormat(' text-align: {0};', $Style.Align.ToLower()); if ($Style.Bold) { [ref] $null = $styleBuilder.Append(' font-weight: bold;'); } else { [ref] $null = $styleBuilder.Append(' font-weight: normal;'); } if ($Style.Italic) { [ref] $null = $styleBuilder.Append(' font-style: italic;'); } if ($Style.Underline) { [ref] $null = $styleBuilder.Append(' text-decoration: underline;'); } if ($Style.Color.StartsWith('#')) { [ref] $null = $styleBuilder.AppendFormat(' color: {0};', $Style.Color.ToLower()); } else { [ref] $null = $styleBuilder.AppendFormat(' color: #{0};', $Style.Color); } if ($Style.BackgroundColor) { if ($Style.BackgroundColor.StartsWith('#')) { [ref] $null = $styleBuilder.AppendFormat(' background-color: {0};', $Style.BackgroundColor.ToLower()); } else { [ref] $null = $styleBuilder.AppendFormat(' background-color: #{0};', $Style.BackgroundColor.ToLower()); } } return $styleBuilder.ToString(); } } #end function GetHtmlStyle function GetHtmlTableStyle { <# .SYNOPSIS Generates html stylesheet style attributes from a PScribo document table style. #> [CmdletBinding()] [OutputType([System.String])] param ( ## PScribo document table style [Parameter(Mandatory, ValueFromPipeline)] [System.Object] $TableStyle ) process { $tableStyleBuilder = New-Object -TypeName 'System.Text.StringBuilder'; [ref] $null = $tableStyleBuilder.AppendFormat(' padding: {0}rem {1}rem {2}rem {3}rem;', (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingTop)), (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingRight)), (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingBottom)), (ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.PaddingLeft))) [ref] $null = $tableStyleBuilder.AppendFormat(' border-style: {0};', $TableStyle.BorderStyle.ToLower()); if ($TableStyle.BorderWidth -gt 0) { $invariantBorderWidth = ConvertToInvariantCultureString -Object (ConvertMmToEm $TableStyle.BorderWidth); [ref] $null = $tableStyleBuilder.AppendFormat(' border-width: {0}rem;', $invariantBorderWidth); if ($TableStyle.BorderColor.Contains('#')) { [ref] $null = $tableStyleBuilder.AppendFormat(' border-color: {0};', $TableStyle.BorderColor); } else { [ref] $null = $tableStyleBuilder.AppendFormat(' border-color: #{0};', $TableStyle.BorderColor); } } [ref] $null = $tableStyleBuilder.Append(' border-collapse: collapse;'); ## <table align="center"> is deprecated in Html5 if ($TableStyle.Align -eq 'Center') { [ref] $null = $tableStyleBuilder.Append(' margin-left: auto; margin-right: auto;'); } elseif ($TableStyle.Align -eq 'Right') { [ref] $null = $tableStyleBuilder.Append(' margin-left: auto; margin-right: 0;'); } return $tableStyleBuilder.ToString(); } } #end function Outhtmltablestyle function GetHtmlTableDiv { <# .SYNOPSIS Generates Html <div style=..><table style=..> tags based upon table width, columns and indentation .NOTES A <div> is required to ensure that the table stays within the "page" boundaries/margins. #> param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Table ) process { $divBuilder = New-Object -TypeName 'System.Text.StringBuilder'; if ($Table.Tabs -gt 0) { $invariantMarginLeft = ConvertToInvariantCultureString -Object (ConvertMmToEm -Millimeter (12.7 * $Table.Tabs)); [ref] $null = $divBuilder.AppendFormat('<div style="margin-left: {0}rem;">' -f $invariantMarginLeft); } else { [ref] $null = $divBuilder.Append('<div>'); } [ref] $null = $divBuilder.AppendFormat('<table class="{0}"', $Table.Style.ToLower()); $styleElements = @(); if ($Table.Width -gt 0) { $styleElements += 'width:{0}%;' -f $Table.Width; } if ($Table.ColumnWidths) { $styleElements += 'table-layout: fixed;'; $styleElements += 'word-break: break-word;' } if ($styleElements.Count -gt 0) { [ref] $null = $divBuilder.AppendFormat(' style="{0}">', [System.String]::Join(' ', $styleElements)); } else { [ref] $null = $divBuilder.Append('>'); } return $divBuilder.ToString(); } } #end function GetHtmlTableDiv function GetHtmlTableColGroup { <# .SYNOPSIS Generates Html <colgroup> tags based on table column widths #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Table ) process { $colGroupBuilder = New-Object -TypeName 'System.Text.StringBuilder'; if ($Table.ColumnWidths) { [ref] $null = $colGroupBuilder.Append('<colgroup>'); foreach ($columnWidth in $Table.ColumnWidths) { if ($null -eq $columnWidth) { [ref] $null = $colGroupBuilder.Append('<col />'); } else { [ref] $null = $colGroupBuilder.AppendFormat('<col style="max-width:{0}%; min-width:{0}%; width:{0}%" />', $columnWidth); } } [ref] $null = $colGroupBuilder.AppendLine('</colgroup>'); } return $colGroupBuilder.ToString(); } } #end function GetHtmlTableDiv function OutHtmlTOC { <# .SYNOPSIS Generates Html table of contents. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Object] $TOC ) process { $tocBuilder = New-Object -TypeName 'System.Text.StringBuilder'; [ref] $null = $tocBuilder.AppendFormat('<h1 class="{0}">{1}</h1>', $TOC.ClassId, $TOC.Name); #[ref] $null = $tocBuilder.AppendLine('<table style="width: 100%;">'); [ref] $null = $tocBuilder.AppendLine('<table>'); foreach ($tocEntry in $Document.TOC) { $sectionNumberIndent = ' ' * $tocEntry.Level; if ($Document.Options['EnableSectionNumbering']) { [ref] $null = $tocBuilder.AppendFormat('<tr><td>{0}</td><td>{1}<a href="#{2}" style="text-decoration: none;">{3}</a></td></tr>', $tocEntry.Number, $sectionNumberIndent, $tocEntry.Id, $tocEntry.Name).AppendLine(); } else { [ref] $null = $tocBuilder.AppendFormat('<tr><td>{0}<a href="#{1}" style="text-decoration: none;">{2}</a></td></tr>', $sectionNumberIndent, $tocEntry.Id, $tocEntry.Name).AppendLine(); } } [ref] $null = $tocBuilder.AppendLine('</table>'); return $tocBuilder.ToString(); } #end process } #end function OutHtmlTOC function OutHtmlBlankLine { <# .SYNOPSIS Outputs html PScribo.Blankline. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Object] $BlankLine ) process { $blankLineBuilder = New-Object -TypeName System.Text.StringBuilder; for ($i = 0; $i -lt $BlankLine.LineCount; $i++) { [ref] $null = $blankLineBuilder.Append('<br />'); } return $blankLineBuilder.ToString(); } #end process } #end function OutHtmlBlankLine function OutHtmlStyle { <# .SYNOPSIS Generates an in-line HTML CSS stylesheet from a PScribo document styles and table styles. #> [CmdletBinding()] [OutputType([System.String])] param ( ## PScribo document styles [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Collections.Hashtable] $Styles, ## PScribo document tables styles [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.Collections.Hashtable] $TableStyles, ## Suppress page layout styling [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NoPageLayoutStyle ) process { $stylesBuilder = New-Object -TypeName 'System.Text.StringBuilder'; [ref] $null = $stylesBuilder.AppendLine('<style type="text/css">'); if (-not $NoPageLayoutStyle) { ## Add HTML page layout styling options, e.g. when emailing HTML documents [ref] $null = $stylesBuilder.AppendLine('html { height: 100%; -webkit-background-size: cover; -moz-background-size: cover; -o-background-size: cover; background-size: cover; background: #f8f8f8; }'); [ref] $null = $stylesBuilder.Append("page { background: white; width: $($Document.Options['PageWidth'])mm; display: block; margin-top: 1rem; margin-left: auto; margin-right: auto; margin-bottom: 1rem; "); [ref] $null = $stylesBuilder.AppendLine('border-style: solid; border-width: 1px; border-color: #c6c6c6; }'); [ref] $null = $stylesBuilder.AppendLine('@media print { body, page { margin: 0; box-shadow: 0; } }'); [ref] $null = $stylesBuilder.AppendLine('hr { margin-top: 1.0rem; }'); [ref] $null = $stylesBuilder.Append(" .portrait { background: white; width: $($Document.Options['PageWidth'])mm; display: block; margin-top: 1rem; margin-left: auto; margin-right: auto; margin-bottom: 1rem; "); [ref] $null = $stylesBuilder.AppendLine('border-style: solid; border-width: 1px; border-color: #c6c6c6; }'); [ref] $null = $stylesBuilder.Append(" .landscape { background: white; width: $($Document.Options['PageHeight'])mm; display: block; margin-top: 1rem; margin-left: auto; margin-right: auto; margin-bottom: 1rem; "); [ref] $null = $stylesBuilder.AppendLine('border-style: solid; border-width: 1px; border-color: #c6c6c6; }'); } foreach ($style in $Styles.Keys) { ## Build style $htmlStyle = GetHtmlStyle -Style $Styles[$style]; [ref] $null = $stylesBuilder.AppendFormat(' .{0} {{{1} }}', $Styles[$style].Id, $htmlStyle).AppendLine(); } foreach ($tableStyle in $TableStyles.Keys) { $tStyle = $TableStyles[$tableStyle]; $tableStyleId = $tStyle.Id.ToLower(); $htmlTableStyle = GetHtmlTableStyle -TableStyle $tStyle; $htmlHeaderStyle = GetHtmlStyle -Style $Styles[$tStyle.HeaderStyle]; $htmlRowStyle = GetHtmlStyle -Style $Styles[$tStyle.RowStyle]; $htmlAlternateRowStyle = GetHtmlStyle -Style $Styles[$tStyle.AlternateRowStyle]; ## Generate table style [ref] $null = $stylesBuilder.AppendFormat(' table.{0} {{{1} }}', $tableStyleId, $htmlTableStyle).AppendLine(); [ref] $null = $stylesBuilder.AppendFormat(' table.{0} th {{{1}{2} }}', $tableStyleId, $htmlHeaderStyle, $htmlTableStyle).AppendLine(); [ref] $null = $stylesBuilder.AppendFormat(' table.{0} tr:nth-child(odd) td {{{1}{2} }}', $tableStyleId, $htmlRowStyle, $htmlTableStyle).AppendLine(); [ref] $null = $stylesBuilder.AppendFormat(' table.{0} tr:nth-child(even) td {{{1}{2} }}', $tableStyleId, $htmlAlternateRowStyle, $htmlTableStyle).AppendLine(); } #end foreach style [ref] $null = $stylesBuilder.AppendLine('</style>'); return $stylesBuilder.ToString().TrimEnd(); } #end process } #end function OutHtmlStyle function OutHtmlSection { <# .SYNOPSIS Output formatted Html section. #> [CmdletBinding()] [OutputType([System.String])] param ( ## Section to output [Parameter(Mandatory, ValueFromPipeline)] [System.Object] $Section ) process { [System.Text.StringBuilder] $sectionBuilder = New-Object System.Text.StringBuilder; if ($Section.IsSectionBreak) { [ref] $null = $sectionBuilder.Append((OutHtmlPageBreak -Orientation $Section.Orientation)); } $encodedSectionName = [System.Net.WebUtility]::HtmlEncode($Section.Name); if ($Document.Options['EnableSectionNumbering']) { [System.String] $sectionName = '{0} {1}' -f $Section.Number, $encodedSectionName; } else { [System.String] $sectionName = '{0}' -f $encodedSectionName; } [int] $headerLevel = $Section.Number.Split('.').Count; ## Html <h5> is the maximum supported level if ($headerLevel -gt 6) { WriteLog -Message $localized.MaxHeadingLevelWarning -IsWarning; $headerLevel = 6; } if ([System.String]::IsNullOrEmpty($Section.Style)) { $className = $Document.DefaultStyle; } else { $className = $Section.Style; } if ($Section.Tabs -gt 0) { $tabEm = ConvertToInvariantCultureString -Object (ConvertMmToEm -Millimeter (12.7 * $Section.Tabs)) -Format 'f2'; [ref] $null = $sectionBuilder.AppendFormat('<div style="margin-left: {0}rem;">' -f $tabEm); } [ref] $null = $sectionBuilder.AppendFormat('<a name="{0}"><h{1} class="{2}">{3}</h{1}></a>', $Section.Id, $headerLevel, $className, $sectionName.TrimStart()); if ($Section.Tabs -gt 0) { [ref] $null = $sectionBuilder.Append('</div>'); } foreach ($s in $Section.Sections.GetEnumerator()) { if ($s.Id.Length -gt 40) { $sectionId = '{0}[..]' -f $s.Id.Substring(0,36); } else { $sectionId = $s.Id; } $currentIndentationLevel = 1; if ($null -ne $s.PSObject.Properties['Level']) { $currentIndentationLevel = $s.Level +1; } WriteLog -Message ($localized.PluginProcessingSection -f $s.Type, $sectionId) -Indent $currentIndentationLevel; switch ($s.Type) { 'PScribo.Section' { [ref] $null = $sectionBuilder.Append((OutHtmlSection -Section $s)); } 'PScribo.Paragraph' { [ref] $null = $sectionBuilder.Append((OutHtmlParagraph -Paragraph $s)); } 'PScribo.LineBreak' { [ref] $null = $sectionBuilder.Append((OutHtmlLineBreak)); } 'PScribo.PageBreak' { [ref] $null = $sectionBuilder.Append((OutHtmlPageBreak -Orientation $Section.Orientation)); } 'PScribo.Table' { [ref] $null = $sectionBuilder.Append((OutHtmlTable -Table $s)); } 'PScribo.BlankLine' { [ref] $null = $sectionBuilder.Append((OutHtmlBlankLine -BlankLine $s)); } 'PScribo.Image' { [ref] $null = $sectionBuilder.Append((OutHtmlImage -Image $s)); } Default { WriteLog -Message ($localized.PluginUnsupportedSection -f $s.Type) -IsWarning; } } #end switch } #end foreach return $sectionBuilder.ToString(); } #end process } # end function OutHtmlSection function GetHtmlParagraphStyle { <# .SYNOPSIS Generates html style attribute from PScribo paragraph style overrides. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Paragraph ) process { $paragraphStyleBuilder = New-Object -TypeName System.Text.StringBuilder; if ($Paragraph.Tabs -gt 0) { ## Default to 1/2in tab spacing $tabEm = ConvertToInvariantCultureString -Object (ConvertMmToEm -Millimeter (12.7 * $Paragraph.Tabs)) -Format 'f2'; [ref] $null = $paragraphStyleBuilder.AppendFormat(' margin-left: {0}rem;', $tabEm); } if ($Paragraph.Font) { [ref] $null = $paragraphStyleBuilder.AppendFormat(" font-family: '{0}';", $Paragraph.Font -Join "','"); } if ($Paragraph.Size -gt 0) { ## Create culture invariant decimal https://github.com/iainbrighton/PScribo/issues/6 $invariantParagraphSize = ConvertToInvariantCultureString -Object ($Paragraph.Size / 12) -Format 'f2'; [ref] $null = $paragraphStyleBuilder.AppendFormat(' font-size: {0}rem;', $invariantParagraphSize); } if ($Paragraph.Bold -eq $true) { [ref] $null = $paragraphStyleBuilder.Append(' font-weight: bold;'); } if ($Paragraph.Italic -eq $true) { [ref] $null = $paragraphStyleBuilder.Append(' font-style: italic;'); } if ($Paragraph.Underline -eq $true) { [ref] $null = $paragraphStyleBuilder.Append(' text-decoration: underline;'); } if (-not [System.String]::IsNullOrEmpty($Paragraph.Color) -and $Paragraph.Color.StartsWith('#')) { [ref] $null = $paragraphStyleBuilder.AppendFormat(' color: {0};', $Paragraph.Color.ToLower()); } elseif (-not [System.String]::IsNullOrEmpty($Paragraph.Color)) { [ref] $null = $paragraphStyleBuilder.AppendFormat(' color: #{0};', $Paragraph.Color.ToLower()); } return $paragraphStyleBuilder.ToString().TrimStart(); } #end process } #end function GetHtmlParagraphStyle function OutHtmlParagraph { <# .SYNOPSIS Output formatted Html paragraph. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Paragraph ) process { [System.Text.StringBuilder] $paragraphBuilder = New-Object -TypeName 'System.Text.StringBuilder'; $encodedText = [System.Net.WebUtility]::HtmlEncode($Paragraph.Text); if ([System.String]::IsNullOrEmpty($encodedText)) { $encodedText = [System.Net.WebUtility]::HtmlEncode($Paragraph.Id); } # $encodedText = $encodedText -replace [System.Environment]::NewLine, '<br />'; $encodedText = $encodedText.Replace([System.Environment]::NewLine, '<br />'); $customStyle = GetHtmlParagraphStyle -Paragraph $Paragraph; if ([System.String]::IsNullOrEmpty($Paragraph.Style) -and [System.String]::IsNullOrEmpty($customStyle)) { [ref] $null = $paragraphBuilder.AppendFormat('<div>{0}</div>', $encodedText); } elseif ([System.String]::IsNullOrEmpty($customStyle)) { [ref] $null = $paragraphBuilder.AppendFormat('<div class="{0}">{1}</div>', $Paragraph.Style, $encodedText); } else { [ref] $null = $paragraphBuilder.AppendFormat('<div style="{0}">{1}</div>', $customStyle, $encodedText); } return $paragraphBuilder.ToString(); } #end process } #end OutHtmlParagraph function GetHtmlTableList { <# .SYNOPSIS Generates list html <table> from a PScribo.Table row object. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Table, [Parameter(Mandatory)] [System.Object] $Row ) process { $listTableBuilder = New-Object -TypeName System.Text.StringBuilder; [ref] $null = $listTableBuilder.Append((GetHtmlTableDiv -Table $Table)); [ref] $null = $listTableBuilder.Append((GetHtmlTableColGroup -Table $Table)); [ref] $null = $listTableBuilder.Append('<tbody>'); for ($i = 0; $i -lt $Table.Columns.Count; $i++) { $propertyName = $Table.Columns[$i]; $rowPropertyName = $Row.$propertyName; ## Core [ref] $null = $listTableBuilder.AppendFormat('<tr><th>{0}</th>', $propertyName); $propertyStyle = '{0}__Style' -f $propertyName; if ($row.PSObject.Properties[$propertyStyle]) { $propertyStyleHtml = (GetHtmlStyle -Style $Document.Styles[$Row.$propertyStyle]); if ([System.String]::IsNullOrEmpty($rowPropertyName)) { [ref] $null = $listTableBuilder.AppendFormat('<td style="{0}"> </td></tr>', $propertyStyleHtml); } else { $encodedHtmlContent = [System.Net.WebUtility]::HtmlEncode($row.$propertyName.ToString()); $encodedHtmlContent = $encodedHtmlContent.Replace([System.Environment]::NewLine, '<br />'); [ref] $null = $listTableBuilder.AppendFormat('<td style="{0}">{1}</td></tr>', $propertyStyleHtml, $encodedHtmlContent); } } else { if ([System.String]::IsNullOrEmpty($rowPropertyName)) { [ref] $null = $listTableBuilder.Append('<td> </td></tr>'); } else { $encodedHtmlContent = [System.Net.WebUtility]::HtmlEncode($row.$propertyName.ToString()); $encodedHtmlContent = $encodedHtmlContent.Replace([System.Environment]::NewLine, '<br />') [ref] $null = $listTableBuilder.AppendFormat('<td>{0}</td></tr>', $encodedHtmlContent); } } } #end for each property [ref] $null = $listTableBuilder.AppendLine('</tbody></table></div>'); return $listTableBuilder.ToString(); } #end process } #end function GetHtmlTableList function GetHtmlTable { <# .SYNOPSIS Generates html <table> from a PScribo.Table object. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Table ) process { $standardTableBuilder = New-Object -TypeName System.Text.StringBuilder; [ref] $null = $standardTableBuilder.Append((GetHtmlTableDiv -Table $Table)); [ref] $null = $standardTableBuilder.Append((GetHtmlTableColGroup -Table $Table)); ## Table headers [ref] $null = $standardTableBuilder.Append('<thead><tr>'); for ($i = 0; $i -lt $Table.Columns.Count; $i++) { [ref] $null = $standardTableBuilder.AppendFormat('<th>{0}</th>', $Table.Columns[$i]); } [ref] $null = $standardTableBuilder.Append('</tr></thead>'); ## Table body [ref] $null = $standardTableBuilder.AppendLine('<tbody>'); foreach ($row in $Table.Rows) { [ref] $null = $standardTableBuilder.Append('<tr>'); foreach ($propertyName in $Table.Columns) { $propertyStyle = '{0}__Style' -f $propertyName; $rowPropertyName = $row.$propertyName; ## Core if ([System.String]::IsNullOrEmpty($rowPropertyName)) { $encodedHtmlContent = ' '; # is already encoded (#72) } else { $encodedHtmlContent = [System.Net.WebUtility]::HtmlEncode($rowPropertyName.ToString()); } $encodedHtmlContent = $encodedHtmlContent.Replace([System.Environment]::NewLine, '<br />'); if ($row.PSObject.Properties[$propertyStyle]) { ## Cell styles override row styles $propertyStyleHtml = (GetHtmlStyle -Style $Document.Styles[$row.$propertyStyle]).Trim(); [ref] $null = $standardTableBuilder.AppendFormat('<td style="{0}">{1}</td>', $propertyStyleHtml, $encodedHtmlContent); } elseif (($row.PSObject.Properties['__Style']) -and (-not [System.String]::IsNullOrEmpty($row.__Style))) { ## We have a row style $rowStyleHtml = (GetHtmlStyle -Style $Document.Styles[$row.__Style]).Trim(); [ref] $null = $standardTableBuilder.AppendFormat('<td style="{0}">{1}</td>', $rowStyleHtml, $encodedHtmlContent); } else { if ($null -ne $row.$propertyName) { ## Check that the property has a value [ref] $null = $standardTableBuilder.AppendFormat('<td>{0}</td>', $encodedHtmlContent); } else { [ref] $null = $standardTableBuilder.Append('<td> </td>'); } } #end if $row.PropertyStyle } #end foreach property [ref] $null = $standardTableBuilder.AppendLine('</tr>'); } #end foreach row [ref] $null = $standardTableBuilder.AppendLine('</tbody></table></div>'); return $standardTableBuilder.ToString(); } #end process } #end function GetHtmlTableList function OutHtmlTable { <# .SYNOPSIS Output formatted Html <table> from PScribo.Table object. .NOTES One table is output per table row with the -List parameter. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Table ) process { [System.Text.StringBuilder] $tableBuilder = New-Object -TypeName 'System.Text.StringBuilder'; if ($Table.List) { ## Create a table for each row for ($r = 0; $r -lt $Table.Rows.Count; $r++) { $row = $Table.Rows[$r]; if ($r -gt 0) { ## Add a space between each table to mirror Word output rendering [ref] $null = $tableBuilder.AppendLine('<p />'); } [ref] $null = $tableBuilder.Append((GetHtmlTableList -Table $Table -Row $row)); } #end foreach row } else { [ref] $null = $tableBuilder.Append((GetHtmlTable -Table $Table)); } #end if return $tableBuilder.ToString(); #Write-Output ($tableBuilder.ToString()) -NoEnumerate; } #end process } #end function OutHtmlTable function OutHtmlLineBreak { <# .SYNOPSIS Output formatted Html line break. #> [CmdletBinding()] [OutputType([System.String])] param ( ) process { return '<hr />'; } } #end function OutHtmlLineBreak function OutHtmlPageBreak { <# .SYNOPSIS Output formatted Html page break. #> [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.String] $Orientation ) process { [System.Text.StringBuilder] $pageBreakBuilder = New-Object 'System.Text.StringBuilder'; [ref] $null = $pageBreakBuilder.Append('</div></div>'); $topMargin = ConvertMmToEm $Document.Options['MarginTop']; $leftMargin = ConvertMmToEm $Document.Options['MarginLeft']; $bottomMargin = ConvertMmToEm $Document.Options['MarginBottom']; $rightMargin = ConvertMmToEm $Document.Options['MarginRight']; [ref] $null = $pageBreakBuilder.AppendFormat('<div class="{0}">', $Orientation.ToLower()); [ref] $null = $pageBreakBuilder.AppendFormat('<div class="{0}" style="padding-top: {1}rem; padding-left: {2}rem; padding-bottom: {3}rem; padding-right: {4}rem;">', $Document.DefaultStyle, $topMargin, $leftMargin, $bottomMargin, $rightMargin).AppendLine(); return $pageBreakBuilder.ToString(); } } #end function OutHtmlPageBreak function OutHtmlImage { <# .SYNOPSIS Output embedded Html image. #> [CmdletBinding()] param ( ## PScribo Image object [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [System.Object] $Image ) process { [System.Text.StringBuilder] $imageBuilder = New-Object -TypeName 'System.Text.StringBuilder' [ref] $null = $imageBuilder.AppendFormat('<div align="{0}">', $Image.Align).AppendLine() $imageBase64 = [System.Convert]::ToBase64String($Image.Bytes) [ref] $null = $imageBuilder.AppendFormat('<img src="data:{0};base64, {1}" alt="{2}" height="{3}" width="{4}" />', $Image.MimeType, $imageBase64, $Image.Text, $Image.Height, $Image.Width).AppendLine() [ref] $null = $imageBuilder.AppendLine('</div>') return $imageBuilder.ToString() } } #end function OutHtmlImage #endregion OutHtml Private Functions # SIG # Begin signature block # MIIcawYJKoZIhvcNAQcCoIIcXDCCHFgCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUNkRYiVIRO/t1CNgFB9WGPJpl # vieggheaMIIFIzCCBAugAwIBAgIQAsbTxa4q6RSRmx0hkVyicTANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE5MDcwMzAwMDAwMFoXDTIxMTEw # MzEyMDAwMFowYDELMAkGA1UEBhMCR0IxDzANBgNVBAcTBkxvbmRvbjEfMB0GA1UE # ChMWVmlydHVhbCBFbmdpbmUgTGltaXRlZDEfMB0GA1UEAxMWVmlydHVhbCBFbmdp # bmUgTGltaXRlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ/s4NME # JLA1Aji4EHJ85uwyEEAepndYn1X8pRnGkOlTzVHITAeH7BQnehjbNwCj7MHUPTSM # zSucXhyfaMZthCNYtugyZ2uU4uVjB1f3xdmXXFX+aukMYgCk1ZQFbQMBqbzRY4Cl # DwlLNGVEjDJeLBUL6ciIETqDc27YLg772WLpuvIXne13EYXN422Y83XRqEMf4v9S # 398S8MRk5qdasRtxYZY6GciZZQnAL/XObpXDM3tDFgcQuyGcZttRuXVZXEj+mlY8 # gUIzkSJ0aJn1pVVTsa+tCvAZuJMJwdPhyM7NUa7Ysm7n9qdF7BvcrWBmaYRfDyya # lLwRoOcI2HVodX8CAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nED # wGD5LfZldQ5YMB0GA1UdDgQWBBSDFWXl70FjVfl8IBwATpE46qvGeDAOBgNVHQ8B # Af8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYv # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmww # NaAzoDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3Mt # ZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0 # dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcB # AQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggr # BgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hB # MkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZI # hvcNAQELBQADggEBAJ1VZZNMAy8KyQLBqwRWAWDqcOxjVly6FTIeqO2/ul9rEYm8 # B9mNx60/AL+TbTbUwBzia2pwBuIin70eClZHFstvQcASBbB0k14R/rs+jestfFRm # rsEz272POc6vsKce3TOlqBc2rtvVyuUPRvI2yQm1WYTpOgQnnp3ix2LBd+fgRANs # P9yurvnGdEFFzToFDXFVkFHBQ9Pr5tAb4i7ZkSFC52BtB7NVuoiH83lx07SyjIxU # 11ELEDZBpO3HiTsTzbhPAEw4CP++ONK8fieWZevDK9DFEiNIC0gWL/DH1+c7eihO # oJdJqRAT9wkAMIjcskZ5LObGvMst/hqwBewpLzYwggUwMIIEGKADAgECAhAECRgb # X9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYD # VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAi # BgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAw # MDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp # Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERp # Z2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE # 9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvsp # J8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWu # HEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel0 # 5iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4P # waLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHN # MIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUE # DDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6 # Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMu # ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0f # BHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNz # dXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG # /WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ # UzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYD # VR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEB # AD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh # 9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6R # Ffu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEM # j7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutm # Q9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUu # kpHqaGxEMrJmoecYpJpkUe8wggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/Bm # MA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IEFzc3VyZWQgSUQgQ0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAw # MDBaMEcxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMc # RGlnaUNlcnQgVGltZXN0YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQAD # ggEPADCCAQoCggEBAKNkXfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V # +JLjntVaY1sCSVDZg85vZu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnM # Xij0ZLZQt/USs3OWCmejvmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oO # jafeTl/iqLYtWQJhiGFyGGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZ # h6h+z+yMDDZbesF6uHjHyQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZ # FQlXmVYwk/PJYczQCMxr7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1Ud # DwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMI # MIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIB # FhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFW # HoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBm # AGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0 # AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAv # AEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0 # AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAg # AGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBw # AG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBu # AGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcN # K2cCzTAdBgNVHQ4EFgQUYVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4 # oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE # Q0EtMS5jcmwwOKA2oDSGMmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dEFzc3VyZWRJRENBLTEuY3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkq # hkiG9w0BAQUFAAOCAQEAnSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEX # zakbvFoWOQCd42yE5FpA+94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gw # g5QZBWs1kBCge5fH9j/n4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x # 2C/jbRcTBu7kA7YUq/OPQ6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYo # FenYk+VVFvC7Cqsc21xIJ2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VU # QAOSK2vCUcIKSK+w1G7g9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9 # +QOWA63qAArrPye7uhswDQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTAT # BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEk # MCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAw # MDAwMFoXDTIxMTExMDAwMDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp # Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMY # RGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A # MIIBCgKCAQEA6IItmfnKwkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgW # Wd+etSQVwpi5tHdJ3InECtqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+ # x8cxSIB8HqIPkg5QycaH6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32j # x0PdAug7Pe2xQaPtP77blUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSN # vtLOBq6thG9IhJtPQLnxTPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0 # XEK4aH631XcKJ1Z8D2KkPzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/ # BAQDAgGGMDsGA1UdJQQ0MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMG # CCsGAQUFBwMEBggrBgEFBQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9 # bAABBDCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3Nz # bC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5 # ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABl # ACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAg # AG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABh # AG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwBy # AGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBp # AGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABl # AGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJ # YIZIAYb9bAMVMBIGA1UdEwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQG # CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG # N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv # b3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9j # cmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYD # VR0OBBYEFBUAEisTmLKZB+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuC # MS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LP # UukxR6tWXHvVDQtBs+/sdR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJH # hZRW0eu7NoR3zCSl8wQZVann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFi # qKgz5Js5p8T1zh14dpQlc+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwC # Xaj++wc4Tw3GXZG5D2dFzdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7Pkdcntxbu # D8O9fAqg7iwIVYUiuOsYGk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAm # MYIEOzCCBDcCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0 # IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNl # cnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQAsbTxa4q6RSRmx0h # kVyicTAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkq # hkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGC # NwIBFTAjBgkqhkiG9w0BCQQxFgQUWnQIgqT1ISID9xWprclw2gbSwUwwDQYJKoZI # hvcNAQEBBQAEggEAXBVwU9/wdJfO1AeuUfVIRnFFNr43HhwKQriilb9wdjN1mVSw # z9GgieR6Nvjit8MGzwks3eZy4iM/np68VCnUaCQ4NdN1Mz0wHjnpk3zDQReU4rcS # oUsRJDgvnbNNyyJ5kJNx8wfiW6V2ANDZsLCk7AI2U4OZj95IElvWKkJ2FrYlqb2q # WSHpoWH1TOsisn0PW8gybzTOLrARYP4vpdPVr1L3uduRyhEVQfwppng1o6QEVD+o # EIJFRGSAXbMC4jg4yEyMTJCjgJJcxyBEWVjdhbD8B+rs8vh1BIzK4QRSOl19+ENU # KIU+Sapf4txGC3WSrgMkz7Cd8gVCS65MkRRepaGCAg8wggILBgkqhkiG9w0BCQYx # ggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0w # GAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjAwMjI0 # MDk0MzIyWjAjBgkqhkiG9w0BCQQxFgQUkioCqUlf7y5oAZ3TwFcwV3fJOkEwDQYJ # KoZIhvcNAQEBBQAEggEAhb6ZjzJIbUMoDl0HwAJSsdVmECXHx/2/Lfr2XzRR8Apx # zqubFWWA4pm4xVq223QHKqcmtyPNt25qvBGd8kdWBJy49jZtDgYcez0MhtRsY+O/ # mTuP1O1mrACA9hm+8d7OxYsAKRmRlsrBNd3IRonGj+bkAXr6SMvrs65k7tku0v+Q # KFzXjbQyXJNor3SRTUSZz+yYhg0vMaH4RRoP2vu4Wb09F19EwXBHxy35RkUNqi+3 # uQ1bveBpy2IK/z3zXTilkzpzU9kypWMsAr0rFuFAbeGaBCvsJptnHBtUt5F6YUGG # mM3u7ZVqySrfzPhqJfg49uvmDJ2B/JaNXxDQ/4vXng== # SIG # End signature block |