Private/Write-AACResourceMapPdf.ps1
|
function Write-AACResourceMapPdf { <# .SYNOPSIS Writes a map (ConvertTo-AACResourceMap and friends) as a one-page PDF: the title and what was read, the whole diagram at full size and its legend, on a page sized to fit - vector, so it stays sharp at any zoom. .DESCRIPTION The map page (Private\ResourceMap.html) has a print mode; Microsoft Edge or Google Chrome prints it headless. PDFsharp can't draw SVG, and the browser is what lays the map out anyway. A browser of its own (a fresh profile in a temporary folder) so one you have open doesn't get in the way. #> [CmdletBinding()] param( [Parameter(Mandatory)] [hashtable] $Map, [Parameter(Mandatory)] [string] $Path, [Parameter(Mandatory)] [string] $Title, [string[]] $Fact = @(), [ValidateSet('RIGHT', 'DOWN')] [string] $Direction = 'RIGHT', [ValidateSet('dark', 'light', 'blueprint')] [string] $Theme = 'light', [ValidateSet('layered', 'hubspoke', 'packed')] [string] $Layout = 'layered', [ValidateSet('layered', 'packed')] [string] $Hierarchy = 'layered', # How long the browser gets to lay out and print the map. [int] $TimeoutSeconds = 120 ) $browser = Get-AACBrowserPath if (-not $browser) { throw 'Azure Admin Console needs Microsoft Edge or Google Chrome to write the diagram as a PDF, and found neither. Install one, or use -HtmlPath and print the page to PDF from your browser.' } $work = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ('aac-pdf-' + [guid]::NewGuid().ToString('N')) $null = New-Item -ItemType Directory -Path $work -Force try { $page = Join-Path -Path $work -ChildPath 'map.html' $pdf = Join-Path -Path $work -ChildPath 'map.pdf' Write-AACResourceMapHtml -Map $Map -Path $page -Title $Title -Fact $Fact -Direction $Direction -Theme $Theme -Layout $Layout -Hierarchy $Hierarchy -Print # Each argument quoted whole: the temporary folder may have spaces in it. # The virtual time budget lets the page lay the map out before it's printed. $arguments = @( '--headless=new', '--disable-gpu', '--no-first-run', '--no-default-browser-check', '--disable-extensions', '--hide-scrollbars' "`"--user-data-dir=$(Join-Path -Path $work -ChildPath 'profile')`"" '--no-pdf-header-footer', '--run-all-compositor-stages-before-draw', '--virtual-time-budget=60000' "`"--print-to-pdf=$pdf`"" "`"$(([System.Uri]::new($page)).AbsoluteUri)`"" ) -join ' ' # Started directly rather than with Start-Process: no window, and nothing # that wraps Start-Process (a demo, a test) gets in the way. $start = [System.Diagnostics.ProcessStartInfo]::new($browser, $arguments) $start.UseShellExecute = $false $start.CreateNoWindow = $true $process = [System.Diagnostics.Process]::Start($start) $deadline = (Get-Date).AddSeconds($TimeoutSeconds) $written = $false # The browser may hand off to a child process: wait for the PDF itself, # whole (it can be opened on its own) rather than for the process. while (-not $written -and (Get-Date) -lt $deadline) { Start-Sleep -Milliseconds 400 if ((Test-Path -LiteralPath $pdf) -and (Get-Item -LiteralPath $pdf).Length -gt 0 -and $process.HasExited) { try { [System.IO.File]::Open($pdf, 'Open', 'Read', 'None').Dispose() $written = $true } catch { $written = $false } } } if (-not $process.HasExited) { $process | Stop-Process -Force -ErrorAction SilentlyContinue } if (-not $written) { throw "Azure Admin Console: the browser didn't write the PDF within $TimeoutSeconds seconds. Try again, or use -HtmlPath and print the page to PDF from your browser." } $folder = Split-Path -Path $Path -Parent if ($folder -and -not (Test-Path -LiteralPath $folder)) { $null = New-Item -ItemType Directory -Path $folder -Force } Move-Item -LiteralPath $pdf -Destination $Path -Force } finally { # The browser can hold its profile a moment after it exits. foreach ($attempt in 1..5) { Remove-Item -LiteralPath $work -Recurse -Force -ErrorAction SilentlyContinue if (-not (Test-Path -LiteralPath $work)) { break } Start-Sleep -Milliseconds 300 } } } |