Private/Write-VellumPdfPageBand.ps1

function Write-VellumPdfPageBand {
    <#
    .SYNOPSIS
        Stamps the repeated headers and footers (Set-VellumPdfPageBand) on every page.
    .DESCRIPTION
        Runs once while a page document is being finished, when the page count is final.
        Each band is drawn as an artifact. The stored bands are cleared afterwards so a
        second pass cannot stamp them twice.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory)] [VellumPdf.Document.PdfDocument]$Document)

    $prop = $Document.PSObject.Properties['PSVellumPageBands']
    if (-not $prop -or $prop.Value.Count -eq 0) { return }
    $bands = @($prop.Value.Values)
    $prop.Value.Clear()
    $count = $Document.Pages.Count
    $rotatedWarned = $false
    $overflowWarned = [System.Collections.Generic.HashSet[object]]::new()
    for ($i = 1; $i -le $count; $i++) {
        $pdfPage = $Document.Pages[$i - 1]
        if ($pdfPage.Rotate -ne 0 -and -not $rotatedWarned) {
            Write-Warning 'Set-VellumPdfPageBand: a rotated page gets its bands relative to the unrotated page, not the displayed top and bottom.'
            $rotatedWarned = $true
        }
        $canvas = Get-VellumPdfPageCanvas -Document $Document -Page $i -CommandName 'Set-VellumPdfPageBand'
        foreach ($band in $bands) {
            if ($band.SkipFirstPage -and $i -eq 1) { continue }
            $text = $band.Template.Replace('{pages}', [string]($count + $band.StartNumber - 1)).Replace('{page}', [string]($i + $band.StartNumber - 1))
            $width = $pdfPage.MediaBox.Width
            $height = $pdfPage.MediaBox.Height
            $y = if ($band.Position -eq 'Header') { $pdfPage.MediaBox.LlY + $height - $band.Offset } else { $pdfPage.MediaBox.LlY + $band.Offset }
            $left = $pdfPage.MediaBox.LlX
            switch ($band.Align) {
                'Left' { $x = $left + $band.Inset; $alignment = [VellumPdf.Canvas.TextAlignment]::Left }
                'Right' { $x = $left + $width - $band.Inset; $alignment = [VellumPdf.Canvas.TextAlignment]::Right }
                default { $x = $left + $width / 2; $alignment = [VellumPdf.Canvas.TextAlignment]::Center }
            }
            $resolved = $band.Resolved
            $available = $width - 2 * $band.Inset
            if ($overflowWarned.Add($band)) {
                $bandWidth = Measure-VellumPdfTextWidth -Resolved $resolved -Text $text -FontSize $band.FontSize
                if ($bandWidth -gt $available) {
                    Write-Warning ("Set-VellumPdfPageBand: the $($band.Position.ToLower()) text '$text' is $([math]::Round($bandWidth, 1)) pt wide, " +
                        "wider than the $([math]::Round($available, 1)) pt between the page insets, so it runs off the page. " +
                        'Shorten it, use a smaller -FontSize or reduce -Inset.')
                }
            }
            [void]$canvas.SaveState()
            try {
                if ($band.Rgb) { [void]$canvas.SetFillColorRgb($band.Rgb[0], $band.Rgb[1], $band.Rgb[2]) }
                Invoke-VellumPdfTagged -Document $Document -Canvas $canvas -PdfPage $pdfPage -Kind Artifact -Draw {
                    [void]$canvas.BeginText()
                    if ($resolved.Handle) {
                        $Document.RegisterEmbeddedFontUsage($pdfPage, $resolved.Handle)
                        [void]$canvas.SetFontByName($resolved.Handle.ResourceName, $band.FontSize)
                        [void][PSVellumPDF.Interop.CanvasHelpers]::ShowEmbeddedText($canvas, $resolved.Handle, $text, $band.FontSize, $x, $y, $alignment)
                    }
                    else {
                        [void]$canvas.SetFont($resolved.Resource, $band.FontSize)
                        [void]$canvas.ShowTextAligned($text, $x, $y, $alignment)
                    }
                    [void]$canvas.EndText()
                }
            }
            finally { [void]$canvas.RestoreState() }
        }
    }
}