Private/Set-AACPdfOutline.ps1

function Set-AACPdfOutline {
    <#
    .SYNOPSIS
        Shapes a report's PDF bookmarks: one per heading, no untitled
        entries, every entry with children collapsed.
    .DESCRIPTION
        MigraDoc makes a bookmark of every Heading1-3 paragraph, nested by
        level. Where a report skips a level - a Heading2 with no Heading1
        above it, a Heading3 straight under a Heading1 - it puts an untitled
        bookmark in between, a blank line in the viewer's bookmarks panel.
 
        -Document (before rendering): each heading's outline level is
        clamped to at most one below the heading before it, so no level is
        skipped and no untitled bookmark is made. Only the bookmark level
        changes; the heading looks the same.
 
        -Outline (after rendering, PdfDocument.Outlines): every bookmark
        with children is closed, so the panel opens as a short list of
        sections the reader unfolds.
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Changes a PDF document in memory, before it is saved.')]
    param(
        # A MigraDoc.DocumentObjectModel.Document. Untyped: the PDF libraries
        # load only on the first export, after this module is parsed.
        [Parameter(Mandatory, ParameterSetName = 'Document')]
        $Document,

        [Parameter(Mandatory, ParameterSetName = 'Outline')]
        [AllowEmptyCollection()]
        $Outline
    )

    if ($PSCmdlet.ParameterSetName -eq 'Document') {
        # The headings still open above this one, by their style's level: a
        # heading nests under those with a smaller level, and is a sibling of
        # one with the same level - whatever levels were skipped in between.
        $open = [System.Collections.Generic.Stack[int]]::new()
        foreach ($section in $Document.Sections) {
            foreach ($element in $section.Elements) {
                if ($element -isnot [MigraDoc.DocumentObjectModel.Paragraph] -or [string]$element.Style -notmatch '^Heading([1-9])$') { continue }
                $level = [int]$Matches[1]
                while ($open.Count -and $open.Peek() -ge $level) { $null = $open.Pop() }
                $element.Format.OutlineLevel = [MigraDoc.DocumentObjectModel.OutlineLevel]"Level$($open.Count + 1)"
                $open.Push($level)
            }
        }
        return
    }

    foreach ($item in $Outline) {
        if ($item.Outlines.Count) {
            Set-AACPdfOutline -Outline $item.Outlines
            $item.Opened = $false
        }
    }
}