Src/Plugins/Html/Html.ps1

function Get-HtmlCanvasHeight
{
<#
    .SYNOPSIS
        Calculates the usable document canvas height.
#>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [System.String] $Orientation
    )
    process
    {
        if ($Orientation -eq 'Portrait')
        {
            $pageHeight = $Document.Options['PageHeight']
            return ($pageHeight - $Document.Options['MarginTop'] - $Document.Options['MarginBottom']) -as [System.Int32]
        }
        else
        {
            $pageHeight = $Document.Options['PageWidth']
            return ($pageHeight - $Document.Options['MarginTop'] - (($Document.Options['MarginBottom'] * 1.5) -as [System.Int32]))
        }
    }
}

function Get-HtmlListItemInlineStyle
{
<#
    .SYNOPSIS
        Generates inline Html style attribute from PScribo list Item style overrides.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.Management.Automation.PSObject] $Item
    )
    process
    {
        $itemStyleBuilder = New-Object -TypeName System.Text.StringBuilder

        if ($Item.Font)
        {
            $fontList = $List.Font -Join "','"
            [ref] $null = $itemStyleBuilder.AppendFormat(" font-family: '{0}';", $fontList)
        }

        if ($Item.Size -gt 0)
        {
            ## Create culture invariant decimal https://github.com/iainbrighton/PScribo/issues/6
            $invariantItemSize = ConvertTo-InvariantCultureString -Object ($Item.Size / 12) -Format 'f2'
            [ref] $null = $itemStyleBuilder.AppendFormat(' font-size: {0}rem;', $invariantItemSize)
        }

        if ($Item.Bold -eq $true)
        {
            [ref] $null = $itemStyleBuilder.Append(' font-weight: bold;')
        }

        if ($Item.Italic -eq $true)
        {
            [ref] $null = $itemStyleBuilder.Append(' font-style: italic;')
        }

        if ($Item.Underline -eq $true)
        {
            [ref] $null = $itemStyleBuilder.Append(' text-decoration: underline;')
        }

        if (-not [System.String]::IsNullOrEmpty($Item.Color))
        {
            $color = Resolve-PScriboStyleColor -Color $Item.Color
            [ref] $null = $itemStyleBuilder.AppendFormat(' color: #{0};', $color)
        }

        return $itemStyleBuilder.ToString().TrimStart()
    }
}

function Get-HtmlParagraphInlineStyle
{
<#
    .SYNOPSIS
        Generates inline Html style attribute from PScribo paragraph style overrides.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.Management.Automation.PSObject] $Paragraph,

        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Management.Automation.SwitchParameter] $NoIndent
    )
    process
    {
        $paragraphStyleBuilder = New-Object -TypeName System.Text.StringBuilder

        if (-not $NoIndent)
        {
            if ($Paragraph.Tabs -gt 0)
            {
                ## Default to 1/2in tab spacing
                $tabEm = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -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 = ConvertTo-InvariantCultureString -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))
        {
            $color = Resolve-PScriboStyleColor -Color $Paragraph.Color
            [ref] $null = $paragraphStyleBuilder.AppendFormat(' color: #{0};', $color)
        }

        return $paragraphStyleBuilder.ToString().TrimStart()
    }
}

function Get-HtmlStyle
{
<#
    .SYNOPSIS
        Generates html stylesheet style attributes from a PScribo document style.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        ## PScribo document style
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.PSObject] $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 = ConvertTo-InvariantCultureString -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)
        {
            $color = Resolve-PScriboStyleColor -Color $Style.Color
            [ref] $null = $styleBuilder.AppendFormat(' color: #{0};', $color)
        }

        if ($Style.BackgroundColor)
        {
            $backgroundColor = Resolve-PScriboStyleColor -Color $Style.BackgroundColor
            [ref] $null = $styleBuilder.AppendFormat(' background-color: #{0};', $backgroundColor)
        }

        return $styleBuilder.ToString()
    }
}

function Get-HtmlTable
{
<#
    .SYNOPSIS
        Generates html <table> from a PScribo.Table object.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.Management.Automation.PSObject] $Table
    )
    begin
    {
        $formattedTables = ConvertTo-PScriboPreformattedTable -Table $Table
    }
    process
    {
        $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style
        $tableBuilder = New-Object -TypeName System.Text.StringBuilder
        foreach ($formattedTable in $formattedTables)
        {
            if ($Table.HasCaption -and ($tableStyle.CaptionLocation -eq 'Above'))
            {
                [ref] $null = $tableBuilder.Append((Get-HtmlTableCaption -Table $Table))
            }

            [ref] $null = $tableBuilder.Append((Get-HtmlTableDiv -Table $Table))
            [ref] $null = $tableBuilder.Append((Get-HtmlTableColGroup -Table $Table))

            ## Table headers
            $startRow = 0
            if ($formattedTable.HasHeaderRow)
            {
                [ref] $null = $tableBuilder.Append('<thead><tr>')
                foreach ($cell in $formattedTable.Rows[0].Cells)
                {
                    [ref] $null = $tableBuilder.AppendFormat('<th>{0}</th>', $cell.Content)
                }
                [ref] $null = $tableBuilder.Append('</tr></thead>')
                $startRow += 1
            }

            ## Table rows
            [ref] $null = $tableBuilder.AppendLine('<tbody>')
            for ($r = $startRow; $r -lt $formattedTable.Rows.Count; $r++)
            {
                $row = $formattedTable.Rows[$r]
                if ($row.IsStyleInherited)
                {
                    [ref] $null = $tableBuilder.Append('<tr>')
                }
                else
                {
                    [ref] $null = $tableBuilder.AppendFormat('<tr style="{0}">', (Get-HtmlStyle -Style $Document.Styles[$row.Style]).Trim())
                }

                for ($c = 0; $c -lt $row.Cells.Count; $c++)
                {
                    $cell = $row.Cells[$c]

                    if ([System.String]::IsNullOrEmpty($cell.Content))
                    {
                        $encodedHtmlContent = '&nbsp;' # &nbsp; is already encoded (#72)
                    }
                    else
                    {
                        $cellContent = Resolve-PScriboToken -InputObject $cell.Content
                        $encodedHtmlContent = [System.Net.WebUtility]::HtmlEncode($cellContent)
                        $encodedHtmlContent = $encodedHtmlContent.Replace([System.Environment]::NewLine, '<br />')
                    }

                    if ($formattedTable.HasHeaderColumn -and ($c -eq 0))
                    {
                        ## Display first column with header styling
                        [ref] $null = $tableBuilder.AppendFormat('<th>{0}</th>', $encodedHtmlContent)
                    }
                    else
                    {
                        if ($cell.IsStyleInherited)
                        {
                            [ref] $null = $tableBuilder.AppendFormat('<td>{0}</td>', $encodedHtmlContent)
                        }
                        else
                        {
                            $propertyStyleHtml = (Get-HtmlStyle -Style $Document.Styles[$cell.Style]).Trim()
                            [ref] $null = $tableBuilder.AppendFormat('<td style="{0}">{1}</td>', $propertyStyleHtml, $encodedHtmlContent)
                        }
                    }
                }
                [ref] $null = $tableBuilder.AppendLine('</tr>')
            }

            [ref] $null = $tableBuilder.AppendLine('</tbody></table>')

            if ($Table.HasCaption -and ($tableStyle.CaptionLocation -eq 'Below'))
            {
                [ref] $null = $tableBuilder.Append((Get-HtmlTableCaption -Table $Table))
            }

            ## Add a space between each table to mirror Word output rendering
            [ref] $null = $tableBuilder.AppendLine('<br /></div>')
        }
        return $tableBuilder.ToString()
    }
}

function Get-HtmlTableCaption
{
<#
    .SYNOPSIS
        Generates html <p> caption from a PScribo.Table object.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.Management.Automation.PSObject] $Table
    )
    process
    {
        $tableStyle = Get-PScriboDocumentStyle -TableStyle $Table.Style

        ## Scaffold paragraph and paragraph run for table caption
        $paragraphId = '{0}{1}' -f $tableStyle.CaptionPrefix, $Table.Number
        $paragraph = New-PScriboParagraph -Id $paragraphId -Style $tableStyle.CaptionStyle -NoIncrementCounter
        $paragraphRunText = '{0} {1} {2}' -f $tableStyle.CaptionPrefix, $Table.CaptionNumber, $Table.Caption
        $paragraphRun = New-PScriboParagraphRun -Text $paragraphRunText
        $paragraphRun.IsParagraphRunEnd = $true
        [ref] $null = $paragraph.Sections.Add($paragraphRun)

        return (Out-HtmlParagraph -Paragraph $paragraph)
    }
}

function Get-HtmlTableColGroup
{
<#
    .SYNOPSIS
        Generates Html <colgroup> tags based on table column widths
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.Management.Automation.PSObject] $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()
    }
}

function Get-HtmlTableDiv
{
<#
    .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.Management.Automation.PSObject] $Table
    )
    process
    {
        $divBuilder = New-Object -TypeName 'System.Text.StringBuilder'
        [ref] $null = $divBuilder.Append('<div style="word-break: break-word; overflow-wrap: anywhere; ')

        if ($Table.Tabs -gt 0)
        {
            $invariantMarginLeft = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter (12.7 * $Table.Tabs))
            [ref] $null = $divBuilder.AppendFormat('margin-left: {0}rem; ' -f $invariantMarginLeft)
        }
        ## Ensure we close the <div style=" "> tag
        [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;' # 'word-wrap: break-word;' or 'overflow-wrap: 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()
    }
}

function Get-HtmlTablePaddingStyle
{
<#
    .SYNOPSIS
        Generates html stylesheet style padding attributes from a PScribo document table style.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        ## PScribo document table style
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.PSObject] $TableStyle
    )
    process
    {
        $tableStyleBuilder = New-Object -TypeName 'System.Text.StringBuilder'
        [ref] $null = $tableStyleBuilder.AppendFormat(' padding: {0}rem {1}rem {2}rem {3}rem;',
            (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingTop)),
                (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingRight)),
                    (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingBottom)),
                        (ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.PaddingLeft)))
        return $tableStyleBuilder.ToString()
    }
}

function Get-HtmlTableStyle
{
<#
    .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.Management.Automation.PSObject] $TableStyle
    )
    process
    {
        $tableStyleBuilder = New-Object -TypeName 'System.Text.StringBuilder'
        [ref] $null = $tableStyleBuilder.AppendFormat((Get-HtmlTablePaddingStyle -TableStyle $TableStyle))
        [ref] $null = $tableStyleBuilder.AppendFormat(' border-style: {0};', $TableStyle.BorderStyle.ToLower())

        if ($TableStyle.BorderWidth -gt 0)
        {
            $invariantBorderWidth = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $TableStyle.BorderWidth)
            [ref] $null = $tableStyleBuilder.AppendFormat(' border-width: {0}rem;', $invariantBorderWidth)

            $borderColor = Resolve-PScriboStyleColor -Color $TableStyle.BorderColor
            [ref] $null = $tableStyleBuilder.AppendFormat(' border-color: #{0};', $borderColor.ToLower())
        }

        [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()
    }
}

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
        }
    }
}

function Out-HtmlBlankLine
{
<#
    .SYNOPSIS
        Outputs html PScribo.Blankline.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.PSObject] $BlankLine
    )
    process
    {
        $blankLineBuilder = New-Object -TypeName System.Text.StringBuilder

        for ($i = 0; $i -lt $BlankLine.LineCount; $i++)
        {
            [ref] $null = $blankLineBuilder.Append('<br />')
        }

        return $blankLineBuilder.ToString()
    }
}

function Out-HtmlDocument
{
<#
    .SYNOPSIS
        Html output plugin for PScribo.

    .DESCRIPTION
        Outputs a Html file representation of a PScribo document object.
#>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','pluginName')]
    [OutputType([System.IO.FileInfo])]
    param
    (
        ## PScribo document object to convert to a text document
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.PSObject] $Document,

        ## Output directory path for the .html file
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [System.String] $Path,

        ### Hashtable of all plugin supported options
        [Parameter(ValueFromPipelineByPropertyName)]
        [AllowNull()]
        [System.Collections.Hashtable] $Options
    )
    process
    {
        $pluginName = 'Html'
        $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
        Write-PScriboMessage -Message ($localized.DocumentProcessingStarted -f $Document.Name)

        ## Merge the document, plugin default and specified/specific plugin options
        $mergePScriboPluginOptionParams = @{
            DefaultPluginOptions = New-PScriboHtmlOption
            DocumentOptions = $Document.Options
            PluginOptions = $Options
        }
        $options = Merge-PScriboPluginOption @mergePScriboPluginOptionParams
        $noPageLayoutStyle = $Options['NoPageLayoutStyle']
        $topMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $options['MarginTop']) -Format 'f2'
        $leftMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $options['MarginLeft']) -Format 'f2'
        $bottomMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $options['MarginBottom']) -Format 'f2'
        $rightMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $options['MarginRight']) -Format 'f2'
        $script:currentPageNumber = 1

        [System.Text.StringBuilder] $htmlBuilder = New-Object System.Text.StringBuilder
        [ref] $null = $htmlBuilder.AppendLine('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">')
        [ref] $null = $htmlBuilder.AppendLine('<html xmlns="http://www.w3.org/1999/xhtml">')
        [ref] $null = $htmlBuilder.AppendLine('<head><title>{0}</title>' -f $Document.Name)
        [ref] $null = $htmlBuilder.AppendLine('{0}</head><body>' -f (Out-HtmlStyle -Styles $Document.Styles -TableStyles $Document.TableStyles -NoPageLayoutStyle:$noPageLayoutStyle))
        [ref] $null = $htmlBuilder.AppendFormat('<div class="{0}">', $options['PageOrientation'].ToLower())
        [ref] $null = $htmlBuilder.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()

        [ref] $null = $htmlBuilder.AppendLine((Out-HtmlHeaderFooter -Header -FirstPage))

        $canvasHeight = Get-HtmlCanvasHeight -Orientation $options['PageOrientation']
        [ref] $null = $htmlBuilder.AppendFormat('<div style="min-height: {0}mm" >', $canvasHeight)

        foreach ($subSection in $Document.Sections.GetEnumerator())
        {
            $currentIndentationLevel = 1
            if ($null -ne $subSection.PSObject.Properties['Level'])
            {
                $currentIndentationLevel = $subSection.Level +1
            }
            Write-PScriboProcessSectionId -SectionId $subSection.Id -SectionType $subSection.Type -Indent $currentIndentationLevel

            switch ($subSection.Type)
            {
                'PScribo.Section'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlSection -Section $subSection))
                }
                'PScribo.Paragraph'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlParagraph -Paragraph $subSection))
                }
                'PScribo.Table'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlTable -Table $subSection))
                }
                'PScribo.LineBreak'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlLineBreak))
                }
                'PScribo.PageBreak'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlPageBreak -Orientation $options['PageOrientation']))
                }
                'PScribo.TOC'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlTOC -TOC $subSection))
                }
                'PScribo.BlankLine'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlBlankLine -BlankLine $subSection))
                }
                'PScribo.Image'
                {
                    [ref] $null = $htmlBuilder.Append((Out-HtmlImage -Image $subSection))
                }
                'PScribo.ListReference'
                {
                    $htmlList = Out-HtmlList -List $Document.Lists[$subSection.Number -1]
                    [ref] $null = $htmlBuilder.Append($htmlList).AppendLine()
                }
                Default
                {
                    Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
                }
            }
        } #end foreach section

        [ref] $null = $htmlBuilder.AppendLine('</div>') ## Canvas
        [ref] $null = $htmlBuilder.Append((Out-HtmlHeaderFooter -Footer))

        [ref] $null = $htmlBuilder.AppendLine('</div></div></body>')
        $stopwatch.Stop()
        Write-PScriboMessage -Message ($localized.DocumentProcessingCompleted -f $Document.Name)
        $destinationPath = Join-Path $Path ('{0}.html' -f $Document.Name)
        Write-PScriboMessage -Message ($localized.SavingFile -f $destinationPath)
        $htmlBuilder.ToString().TrimEnd() | Out-File -FilePath $destinationPath -Force -Encoding utf8
        [ref] $null = $htmlBuilder

        if ($stopwatch.Elapsed.TotalSeconds -gt 90)
        {
            Write-PScriboMessage -Message ($localized.TotalProcessingTimeMinutes -f $stopwatch.Elapsed.TotalMinutes)
        }
        else
        {
            Write-PScriboMessage -Message ($localized.TotalProcessingTimeSeconds -f $stopwatch.Elapsed.TotalSeconds)
        }

        Write-Output (Get-Item -Path $destinationPath)
    }
}

function Out-HtmlHeaderFooter
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DefaultHeader')]
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageHeader')]
        [System.Management.Automation.SwitchParameter] $Header,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DefaultFooter')]
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageFooter')]
        [System.Management.Automation.SwitchParameter] $Footer,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageHeader')]
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'FirstPageFooter')]
        [System.Management.Automation.SwitchParameter] $FirstPage
    )
    process
    {
        $headerFooter = Get-PScriboHeaderFooter @PSBoundParameters
        if ($null -ne $headerFooter)
        {
            [System.Text.StringBuilder] $hfBuilder = New-Object System.Text.StringBuilder
            [ref] $null = $hfBuilder.Append('<div>')

            foreach ($subSection in $headerFooter.Sections.GetEnumerator())
            {
                switch ($subSection.Type)
                {
                    'PScribo.Paragraph'
                    {
                        [ref] $null = $hfBuilder.Append((Out-HtmlParagraph -Paragraph $subSection))
                    }
                    'PScribo.Table'
                    {
                        [ref] $null = $hfBuilder.Append((Out-HtmlTable -Table $subSection))
                    }
                    'PScribo.BlankLine'
                    {
                        [ref] $null = $hfBuilder.Append((Out-HtmlBlankLine -BlankLine $subSection))
                    }
                    'PScribo.LineBreak'
                    {
                        [ref] $null = $hfBuilder.Append((Out-HtmlLineBreak))
                    }
                }
            }

            [ref] $null = $hfBuilder.Append('</div>')
            return $hfBuilder.ToString()
        }
    }
}

function Out-HtmlImage
{
<#
    .SYNOPSIS
        Output embedded Html image.
#>

    [CmdletBinding()]
    param
    (
        ## PScribo Image object
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.Management.Automation.PSObject] $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()
    }
}

function Out-HtmlLineBreak
{
<#
    .SYNOPSIS
        Output formatted Html line break.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param ( )
    process
    {
        return '<hr />'
    }
}

function Out-HtmlList
{
<#
    .SYNOPSIS
        Output formatted Html list.
#>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','Options')]
    param
    (
        ## List to output
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.PSObject] $List,

        ## List indent
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.Int32] $Indent,

        ## Number style
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $NumberStyle,

        ## Bullet style
        [Parameter(ValueFromPipelineByPropertyName)]
        [System.String] $BulletStyle
    )
    begin
    {
        ## Fix Set-StrictMode
        if (-not (Test-Path -Path Variable:\Options))
        {
            $options = New-PScriboHtmlOption
        }
    }
    process
    {
        $leader = ''.PadRight($Indent, ' ')
        $listBuilder = New-Object -TypeName System.Text.StringBuilder

        if ($List.IsNumbered)
        {
            if ($List.HasNumberStyle)
            {
                $NumberStyle = $List.NumberStyle
            }
            elseif (-not $PSBoundParameters.ContainsKey('NumberStyle'))
            {
                $NumberStyle = $Document.DefaultNumberStyle
            }
            $style = $Document.NumberStyles[$NumberStyle]

            $outHtmlListParams = @{
                NumberStyle = $NumberStyle
            }

            switch ($style.Format)
            {
                'Number'
                {
                    $inlineStyle = 'decimal'
                }
                'Letter'
                {
                    if ($style.Uppercase)
                    {
                        $inlineStyle = 'upper-alpha'
                    }
                    else
                    {
                        $inlineStyle = 'lower-alpha'
                    }
                }
                'Roman'
                {
                    if ($style.Uppercase)
                    {
                        $inlineStyle = 'upper-roman'
                    }
                    else
                    {
                        $inlineStyle = 'lower-roman'
                    }
                }
                'Custom'
                {
                    $inlineStyle = 'decimal'
                }
            }

            if ($List.HasStyle)
            {
                [ref] $null = $listBuilder.AppendFormat('{0}<ol class="{1}" style="list-style-type:{2};">', $leader, $List.Style, $inlineStyle)
            }
            else
            {
                [ref] $null = $listBuilder.AppendFormat('{0}<ol style="list-style-type:{1};">', $leader, $inlineStyle)
            }
            [ref] $null = $listBuilder.AppendLine()
        }
        else
        {
            if ($List.HasBulletStyle)
            {
                $BulletStyle = $List.BulletStyle
            }

            switch ($BulletStyle)
            {
                Circle
                {
                    $inlineStyle = ' style="list-style-type:circle;"'
                }
                Disc
                {
                    $inlineStyle = ' style="list-style-type:disc;"'
                }
                Square
                {
                    $inlineStyle = ' style="list-style-type:square;"'
                }
                Default
                {
                    ## Dash style is not supported in Html so default to the browser's rendering engine
                    $inlineStyle = ''
                }
            }

            $outHtmlListParams = @{
                BulletStyle = $BulletStyle
            }

            if ($List.HasStyle)
            {
                [ref] $null = $listBuilder.AppendFormat('{0}<ul class="{1}"{2}>', $leader, $List.Style, $inlineStyle)
            }
            else
            {
                [ref] $null = $listBuilder.AppendFormat('{0}<ul{1}>', $leader, $inlineStyle)
            }
            [ref] $null = $listBuilder.AppendLine()
        }

        $leader = ''.PadRight($Indent, ' ')
        foreach ($item in $List.Items)
        {
            if ($item.Type -eq 'PScribo.Item')
            {
                if (($item.HasStyle -eq $true) -and ($item.HasInlineStyle -eq $true))
                {
                    $inlineStyle = Get-HtmlListItemInlineStyle -Item $item
                    [ref] $null = $listBuilder.AppendFormat('{0}<li class="{1}" style="{2}">{3}</li>', $leader, $item.Style, $inlineStyle, $item.Text)
                }
                elseif ($item.HasStyle)
                {
                    [ref] $null = $listBuilder.AppendFormat('{0} <li class="{1}"">{2}</li>', $leader, $item.Style, $item.Text)
                }
                elseif ($item.HasInlineStyle)
                {
                    $inlineStyle = Get-HtmlListItemInlineStyle -Item $item
                    [ref] $null = $listBuilder.AppendFormat('{0} <li style="{1}">{2}</li>', $leader, $inlineStyle, $item.Text)
                }
                else
                {
                    [ref] $null = $listBuilder.AppendFormat('{0} <li>{1}</li>', $leader, $item.Text)
                }
                [ref] $null = $listBuilder.AppendLine()
            }
            else
            {
                $nestedList = Out-HtmlList -List $item -Indent ($Indent +2) @outHtmlListParams
                [ref] $null = $listBuilder.AppendLine($nestedList)
            }
        }

        if ($List.IsNumbered)
        {
            [ref] $null = $listBuilder.AppendFormat('{0}</ol>', $leader)
        }
        else
        {
            [ref] $null = $listBuilder.AppendFormat('{0}</ul>', $leader)
        }
        [ref] $null = $listBuilder.AppendLine()

        return $listBuilder.ToString()
    }
}

function Out-HtmlPageBreak
{
<#
    .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>') ## Canvas
        $isFirstPage = $currentPageNumber -eq 1
        [ref] $null = $pageBreakBuilder.Append((Out-HtmlHeaderFooter -Footer -FirstPage:$isFirstPage))

        $script:currentPageNumber++
        [ref] $null = $pageBreakBuilder.Append('</div></div>')
        $topMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginTop']) -Format 'f2'
        $leftMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginLeft']) -Format 'f2'
        $bottomMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginBottom']) -Format 'f2'
        $rightMargin = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter $Document.Options['MarginRight']) -Format 'f2'
        [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()

        $canvasHeight = Get-HtmlCanvasHeight -Orientation $Orientation
        [ref] $null = $pageBreakBuilder.AppendFormat('<div style="min-height: {0}mm" >', $canvasHeight)
        [ref] $null = $pageBreakBuilder.Append((Out-HtmlHeaderFooter -Header))

        return $pageBreakBuilder.ToString()
    }
}

function Out-HtmlParagraph
{
<#
    .SYNOPSIS
        Output formatted Html paragraph run.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.Management.Automation.PSObject] $Paragraph
    )
    process
    {
        [System.Text.StringBuilder] $paragraphBuilder = New-Object -TypeName 'System.Text.StringBuilder'

        if ([System.String]::IsNullOrEmpty($Paragraph.Style))
        {
            if ($Paragraph.Tabs -gt 0)
            {
                ## Default to 1/2in tab spacing
                $tabEm = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter (12.7 * $Paragraph.Tabs)) -Format 'f2'
                [ref] $null = $paragraphBuilder.AppendFormat('<div style="margin-left: {0}rem;" >', $tabEm)
            }
            else
            {
                [ref] $null = $paragraphBuilder.Append('<div>')
            }
        }
        else
        {
            if ($Paragraph.Tabs -gt 0)
            {
                ## Default to 1/2in tab spacing
                $tabEm = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -Millimeter (12.7 * $Paragraph.Tabs)) -Format 'f2'
                [ref] $null = $paragraphBuilder.AppendFormat('<div class="{0}" style="margin-left: {1}rem;" >', $Paragraph.Style, $tabEm)
            }
            else
            {
                [ref] $null = $paragraphBuilder.AppendFormat('<div class="{0}">', $Paragraph.Style)
            }
        }

        foreach ($paragraphRun in $Paragraph.Sections)
        {
            if (($paragraphRun.HasStyle -eq $true) -and ($paragraphRun.HasInlineStyle -eq $true))
            {
                $inlineStyle = Get-HtmlParagraphInlineStyle -Paragraph $paragraphRun -NoIndent
                [ref] $null = $paragraphBuilder.AppendFormat('<span class="{0}" style="{1}">', $paragraphRun.Style, $inlineStyle)
            }
            elseif ($paragraphRun.HasStyle)
            {
                [ref] $null = $paragraphBuilder.AppendFormat('<span class="{0}">', $paragraphRun.Style)
            }
            elseif ($paragraphRun.HasInlineStyle)
            {
                $inlineStyle = Get-HtmlParagraphInlineStyle -Paragraph $paragraphRun -NoIndent
                [ref] $null = $paragraphBuilder.AppendFormat('<span style="{0}">', $inlineStyle)
            }

            $text = Resolve-PScriboToken -InputObject $paragraphRun.Text
            $encodedText = [System.Net.WebUtility]::HtmlEncode($text)
            $encodedText = $encodedText.Replace([System.Environment]::NewLine, '<br />')
            [ref] $null = $paragraphBuilder.Append($encodedText)

            if (($paragraphRun.IsParagraphRunEnd -eq $false) -and
                ($paragraphRun.NoSpace -eq $false))
            {
                [ref] $null = $paragraphBuilder.Append(' ')
            }

            if ($paragraphRun.HasStyle -or $paragraphRun.HasInlineStyle)
            {
                [ref] $null = $paragraphBuilder.Append('</span>')
            }
        }

        [ref] $null = $paragraphBuilder.Append('</div>')
        return $paragraphBuilder.ToString()
    }
}

function Out-HtmlSection
{
<#
    .SYNOPSIS
        Output formatted Html section.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        ## Section to output
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.PSObject] $Section
    )
    process
    {
        [System.Text.StringBuilder] $sectionBuilder = New-Object System.Text.StringBuilder
        if ($Section.IsSectionBreak)
        {
            [ref] $null = $sectionBuilder.Append((Out-HtmlPageBreak -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)
        {
            Write-PScriboMessage -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 = ConvertTo-InvariantCultureString -Object (ConvertTo-Em -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 ($subSection in $Section.Sections.GetEnumerator())
        {
            $currentIndentationLevel = 1
            if ($null -ne $subSection.PSObject.Properties['Level'])
            {
                $currentIndentationLevel = $subSection.Level +1
            }
            Write-PScriboProcessSectionId -SectionId $subSection.Id -SectionType $subSection.Type -Indent $currentIndentationLevel

            switch ($subSection.Type)
            {
                'PScribo.Section'
                {
                    [ref] $null = $sectionBuilder.Append((Out-HtmlSection -Section $subSection))
                }
                'PScribo.Paragraph' {
                    [ref] $null = $sectionBuilder.Append((Out-HtmlParagraph -Paragraph $subSection))
                }
                'PScribo.LineBreak'
                {
                    [ref] $null = $sectionBuilder.Append((Out-HtmlLineBreak))
                }
                'PScribo.PageBreak'
                {
                    [ref] $null = $sectionBuilder.Append((Out-HtmlPageBreak -Orientation $Section.Orientation))
                }
                'PScribo.Table'
                {
                    [ref] $null = $sectionBuilder.Append((Out-HtmlTable -Table $subSection))
                }
                'PScribo.BlankLine'
                {
                    [ref] $null = $sectionBuilder.Append((Out-HtmlBlankLine -BlankLine $subSection))
                }
                'PScribo.Image'
                {
                    [ref] $null = $sectionBuilder.Append((Out-HtmlImage -Image $subSection))
                }
                'PScribo.ListReference'
                {
                    $htmlList = Out-HtmlList -List $Document.Lists[$subSection.Number -1]
                    [ref] $null = $sectionBuilder.Append($htmlList).AppendLine()
                }
                Default
                {
                    Write-PScriboMessage -Message ($localized.PluginUnsupportedSection -f $subSection.Type) -IsWarning
                }
            }
        }

        return $sectionBuilder.ToString()
    }
}

function Out-HtmlStyle
{
<#
    .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">')
        $pageWidth = $Document.Options['PageWidth']
        $pageHeight = $Document.Options['PageHeight']
        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: #e6e6e6; }')
            [ref] $null = $stylesBuilder.Append("page { background: white; 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: $($pageWidth)mm; display: block; margin-top: 1rem; margin-left: auto; margin-right: auto; margin-bottom: 1rem; position: relative; ")
            [ref] $null = $stylesBuilder.AppendLine('border-style: solid; border-width: 1px; border-color: #c6c6c6; }')
            [ref] $null = $stylesBuilder.Append(" .landscape { background: white; width: $($pageHeight)mm; display: block; margin-top: 1rem; margin-left: auto; margin-right: auto; margin-bottom: 1rem; position: relative; ")
            [ref] $null = $stylesBuilder.AppendLine('border-style: solid; border-width: 1px; border-color: #c6c6c6; }')
        }

        foreach ($style in $Styles.Keys)
        {
            ## Build style
            $htmlStyle = Get-HtmlStyle -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 = Get-HtmlTableStyle -TableStyle $tStyle
            $htmlHeaderStyle = Get-HtmlStyle -Style $Styles[$tStyle.HeaderStyle]
            $htmlRowStyle = Get-HtmlStyle -Style $Styles[$tStyle.RowStyle]
            $htmlAlternateRowStyle = Get-HtmlStyle -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} td {{{1} }}', $tableStyleId,  $htmlTableStyle).AppendLine()
            [ref] $null = $stylesBuilder.AppendFormat(' table.{0} tr:nth-child(odd) {{{1}{2} }}', $tableStyleId, $htmlRowStyle, $htmlTableStyle).AppendLine()
            [ref] $null = $stylesBuilder.AppendFormat(' table.{0} tr:nth-child(even) {{{1}{2} }}', $tableStyleId, $htmlAlternateRowStyle, $htmlTableStyle).AppendLine()
        }

        [ref] $null = $stylesBuilder.AppendLine('</style>')
        return $stylesBuilder.ToString().TrimEnd()
    }
}

function Out-HtmlTable
{
<#
    .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.Management.Automation.PSObject] $Table
    )
    process
    {
        [System.Text.StringBuilder] $tableBuilder = New-Object -TypeName 'System.Text.StringBuilder'
        $formattedTable = Get-HtmlTable -Table $Table
        [ref] $null = $tableBuilder.Append($formattedTable)
        return $tableBuilder.ToString()
    }
}

function Out-HtmlTOC
{
<#
    .SYNOPSIS
        Generates Html table of contents.
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Management.Automation.PSObject] $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 = '&nbsp;&nbsp;&nbsp;' * $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()
    }
}


# SIG # Begin signature block
# MIIuugYJKoZIhvcNAQcCoIIuqzCCLqcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC/l7VnqsKqfVs/
# nG/XTx9AGvIF+ZNYtfxU+LN3XH2h+qCCE6QwggWQMIIDeKADAgECAhAFmxtXno4h
# 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
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgOuok+ylV9Gz6Zp3yp9eyX/F8
# 1OLa+I7w5j+eKAYS3pAwDQYJKoZIhvcNAQEBBQAEggIAKwjLYQ2BgU7uTbv9OGZP
# mQqc/SyXNXCKHNwOB4cTPLmMgQTqdSKXfYOHczBFneiE9aBKYIQ2YsyDMrI1nNeq
# 3jwuT2LurKBa4i4To+eiE+MWTHWLgQGhhjdDjvNfsHOmPz1CkpXGxi8GpZznb1k9
# jcFdsceB+wcbcpUw8qi+MDUJaol9cdPC5ycTJGmqQs01AAr45mxl6h0MF3Ecg5Uk
# uPpO7l8Exo7ZzZ3Jt5hL7Lh80vRDNq09a8ZtO40sR2+dgumutQnB9mCxwC22cJlT
# Vuovl89pAHwNoHG+QEZAQ5JyiAoeaRbxXxr6Lt+78h0SJxOuQaJP527/ioDBuD8+
# PJ4J1HeZXnTFztjHMuyidzlESeee938K/LUkkyzDKtXx9EO/x6nakyH8lSBTx0jl
# iPMgkciFztVehDGeFjVxhzwTFfC7IqawzyAcwM6U662mO9ZnaRiOM3Y8+OJg975N
# wRiw14a5X2SBLGAcVpOYcPqoLkX2H5UOWxANIcXfZh/a1/TfSv7qtfCzWGO13JfC
# Mt1Xe+F9UQS+NO7Iyy82I2lYCUbpW0+QBmfgvvcMhyLNmB+eFznG2n3QUWkBDT8a
# UypFAcZkMHREiUvz/6szUoF7FZDINpCbXfH0e53O6DocRYfdCc/fx2YOOi8JQHSz
# xpWjm/cf8SaPY8ZDlLNfS0Chghc5MIIXNQYKKwYBBAGCNwMDATGCFyUwghchBgkq
# hkiG9w0BBwKgghcSMIIXDgIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3DQEJ
# EAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQguxTVcL7F
# 3wcnL33QoSMm2oqyMSCpqyTSfWABdbgRpoACEFO1fzRBuc2j3XJK1MdsHk0YDzIw
# MjUwMzA3MDg1NTAxWqCCEwMwgga8MIIEpKADAgECAhALrma8Wrp/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
# NTAxWjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBTb04XuYtvSPnvk9nFIUIck1YZb
# RTAvBgkqhkiG9w0BCQQxIgQgs0gbbOnDTje/YXVVJTk+7g9FW8rJIyCqIna3ADff
# zbUwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgdnafqPJjLx9DCzojMK7WVnX+13Pb
# BdZluQWTmEOPmtswDQYJKoZIhvcNAQEBBQAEggIAKTU58WZYycvxJi0GxHsVc/kA
# bLCclNVJjifzeUDvZ9pxc8QwbEy81TAA5VJtIKYIzkdB82118uVBV/xTQ5VvHiBF
# UtTeFLSip22zKjj4k2gMIqTtFqAjJJKXcx0q+FZAxC44h+cxOqRMW8FKw2BugrL1
# w258pum4TBDs2QW14Xhsliee6PuqcwA2gwrL4halodkqBgXLlkv/9AbeRovYkbhq
# kxz3whLI0LdUlSGbIVF7UmqgTpDkgnn4R9W+ky20luXk+z1WkjQFjpemjmoOIQTi
# BHYLNw1OEIGm7ptift5zFGsa5kozJ8jTuDfOj9yu5xeWEGYIaPmPBQLqMpL9SZOG
# ydfyNoMrrbIAFnm+gEzup4fUvtwsdRIsXsEFYQGchWeVZttYJB5LawSes46Rmhya
# hh1StX5zZ2mIFD89/x13T0bbRSwNtCAee2UiF59OlQdLq3iZXRyxfB7/EBocep+J
# MopGhbNmA4bBEbO8WaMp2p0hYEfmDzFJwVonCtRdAARBpScoo5yswgq+tlnXqd7m
# TlGdg3n1hhLnolNx7GAjVLbhI6XUD+//1zXu10/OLfLlf8NlVeiWPs6eh3VV/+Dz
# IkcqG1muUjHVK7zVGXxXZGx1w6tdRR9//HU36cml6rQ+X8i1hKE26VnXGdu4IKy+
# rIsa/pWpraw8hMSmyVQ=
# SIG # End signature block