Add-Type -AssemblyName System.Drawing $auditDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path function New-ContactSheet { param( [Parameter(Mandatory)] [string[]]$Files, [Parameter(Mandatory)] [string]$OutputName, [int]$Columns = 3 ) $thumbnailWidth = 360 $thumbnailHeight = 256 $labelHeight = 38 $cellPadding = 14 $cellWidth = $thumbnailWidth + ($cellPadding * 2) $cellHeight = $thumbnailHeight + $labelHeight + ($cellPadding * 2) $rows = [Math]::Ceiling($Files.Count / $Columns) $sheet = New-Object System.Drawing.Bitmap ($cellWidth * $Columns), ($cellHeight * $rows) $graphics = [System.Drawing.Graphics]::FromImage($sheet) $graphics.Clear([System.Drawing.Color]::FromArgb(8, 12, 28)) $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic $graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality $font = New-Object System.Drawing.Font 'Segoe UI', 13, ([System.Drawing.FontStyle]::Regular) $brush = New-Object System.Drawing.SolidBrush ([System.Drawing.Color]::FromArgb(229, 235, 255)) try { for ($index = 0; $index -lt $Files.Count; $index++) { $column = $index % $Columns $row = [Math]::Floor($index / $Columns) $x = ($column * $cellWidth) + $cellPadding $y = ($row * $cellHeight) + $cellPadding $sourcePath = Join-Path $auditDirectory $Files[$index] $source = [System.Drawing.Image]::FromFile($sourcePath) try { $ratio = [Math]::Min($thumbnailWidth / $source.Width, $thumbnailHeight / $source.Height) $drawWidth = [int]($source.Width * $ratio) $drawHeight = [int]($source.Height * $ratio) $drawX = $x + [int](($thumbnailWidth - $drawWidth) / 2) $drawY = $y + [int](($thumbnailHeight - $drawHeight) / 2) $graphics.DrawImage($source, $drawX, $drawY, $drawWidth, $drawHeight) $label = [IO.Path]::GetFileNameWithoutExtension($Files[$index]) -replace '^\d+-', '' $graphics.DrawString($label, $font, $brush, $x, ($y + $thumbnailHeight + 8)) } finally { $source.Dispose() } } $outputPath = Join-Path $auditDirectory $OutputName $sheet.Save($outputPath, [System.Drawing.Imaging.ImageFormat]::Png) } finally { $brush.Dispose() $font.Dispose() $graphics.Dispose() $sheet.Dispose() } } New-ContactSheet -OutputName 'route-contact-sheet.png' -Files @( '01-dashboard.png', '02-run-control.png', '03-login.png', '04-agents.png', '05-agent-detail.png', '06-projects.png', '07-project-detail.png', '08-task-board.png', '09-task-detail.png', '10-memory.png', '11-docs.png', '12-models.png', '13-activity.png', '14-calendar.png', '15-security.png', '16-incidents.png', '17-notifications.png', '18-settings.png' ) New-ContactSheet -OutputName 'evidence-contact-sheet.png' -Files @( '00-baseline-login.png', '19-run-control-1920.png', '20-run-control-1024.png', '21-memory-detail.png', '22-doc-detail.png', '23-incident-detail.png' )