# Export Mag2Car guide Markdown to PDF (requires Chrome or Edge) param( [string]$InputMd = "$PSScriptRoot\Mag2Car使用指南.md", [string]$OutputPdf = "$PSScriptRoot\Mag2Car使用指南.pdf" ) $ErrorActionPreference = 'Stop' [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 function Escape-Html([string]$text) { return [System.Net.WebUtility]::HtmlEncode($text) } function Convert-MarkdownToHtml([string]$markdown) { $lines = $markdown -split "`r?`n" $sb = New-Object System.Text.StringBuilder $inCode = $false $inTable = $false $tableRows = New-Object System.Collections.Generic.List[string[]] $inList = $false $listOrdered = $false function Flush-Table { param($builder, [ref]$inTableRef, $rows) if (-not $inTableRef.Value) { return } [void]$builder.AppendLine('') for ($i = 0; $i -lt $rows.Count; $i++) { $cells = $rows[$i] $tag = if ($i -eq 0) { 'th' } else { 'td' } if ($i -eq 0) { [void]$builder.AppendLine('') } elseif ($i -eq 1) { [void]$builder.AppendLine('') } else { [void]$builder.AppendLine('') } foreach ($cell in $cells) { $cellHtml = Escape-Html($cell.Trim()) $cellHtml = $cellHtml -replace '`([^`]+)`', '$1' [void]$builder.AppendLine("<$tag>$cellHtml") } [void]$builder.AppendLine('') if ($i -eq 0) { [void]$builder.AppendLine('') } } if ($rows.Count -gt 1) { [void]$builder.AppendLine('') } [void]$builder.AppendLine('
') $rows.Clear() $inTableRef.Value = $false } function Close-List { param($builder, [ref]$inListRef, [ref]$orderedRef) if (-not $inListRef.Value) { return } if ($orderedRef.Value) { [void]$builder.AppendLine('') } else { [void]$builder.AppendLine('') } $inListRef.Value = $false $orderedRef.Value = $false } foreach ($line in $lines) { if ($line -match '^\s*```') { if ($inCode) { [void]$sb.AppendLine('') $inCode = $false } else { Flush-Table $sb ([ref]$inTable) $tableRows Close-List $sb ([ref]$inList) ([ref]$listOrdered) [void]$sb.AppendLine('
')
                $inCode = $true
            }
            continue
        }

        if ($inCode) {
            [void]$sb.AppendLine((Escape-Html $line))
            continue
        }

        if ($line -match '^\s*\|(.+)\|\s*$') {
            Close-List $sb ([ref]$inList) ([ref]$listOrdered)
            if ($line -match '^\s*\|[-:\s|]+\|\s*$') { continue }
            $cells = ($line.Trim('|') -split '\|') | ForEach-Object { $_.Trim() }
            $tableRows.Add($cells)
            $inTable = $true
            continue
        } else {
            Flush-Table $sb ([ref]$inTable) $tableRows
        }

        if ($line -match '^(#{1,6})\s+(.+)$') {
            Close-List $sb ([ref]$inList) ([ref]$listOrdered)
            $level = $Matches[1].Length
            $text = $Matches[2]
            [void]$sb.AppendLine("$(Escape-Html $text)")
            continue
        }

        if ($line -match '^>\s*(.+)$') {
            Close-List $sb ([ref]$inList) ([ref]$listOrdered)
            $text = $Matches[1]
            [void]$sb.AppendLine("

$(Escape-Html $text)

") continue } if ($line -match '^---\s*$') { Close-List $sb ([ref]$inList) ([ref]$listOrdered) [void]$sb.AppendLine('
') continue } if ($line -match '^\d+\.\s+(.+)$') { if (-not $inList) { [void]$sb.AppendLine('
    '); $inList = $true; $listOrdered = $true } $text = Escape-Html $Matches[1] $text = $text -replace '`([^`]+)`', '$1' [void]$sb.AppendLine("
  1. $text
  2. ") continue } if ($line -match '^-\s+(.+)$') { if (-not $inList) { [void]$sb.AppendLine('
') } return $sb.ToString() } if (-not (Test-Path $InputMd)) { throw "Markdown file not found: $InputMd" } $md = Get-Content -Path $InputMd -Raw -Encoding UTF8 $body = Convert-MarkdownToHtml $md $htmlPath = [System.IO.Path]::ChangeExtension($OutputPdf, '.html') $html = @" Mag2Car Guide $body "@ [System.IO.File]::WriteAllText($htmlPath, $html, [System.Text.UTF8Encoding]::new($false)) $browserCandidates = @( "$env:ProgramFiles\Google\Chrome\Application\chrome.exe", "${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe", "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe", "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe" ) $browser = $browserCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 if (-not $browser) { throw "Chrome/Edge not found. HTML generated at: $htmlPath" } if (Test-Path $OutputPdf) { Remove-Item $OutputPdf -Force } $resolvedHtml = (Resolve-Path -LiteralPath $htmlPath).Path $fileUri = [System.Uri]::new($resolvedHtml).AbsoluteUri $args = @( "--headless=new", "--disable-gpu", "--no-pdf-header-footer", "--print-to-pdf=$OutputPdf", $fileUri ) $proc = Start-Process -FilePath $browser -ArgumentList $args -Wait -PassThru if ($proc.ExitCode -ne 0 -or -not (Test-Path $OutputPdf)) { throw "PDF export failed with exit code $($proc.ExitCode). HTML: $htmlPath" } Write-Output "PDF: $OutputPdf" Write-Output "HTML: $htmlPath"