Files
StandardSence/StandardScene.Magnetic/docs/export-pdf.ps1
T

210 lines
7.7 KiB
PowerShell

# 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('<table>')
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('<thead><tr>') } elseif ($i -eq 1) { [void]$builder.AppendLine('<tbody><tr>') } else { [void]$builder.AppendLine('<tr>') }
foreach ($cell in $cells) {
$cellHtml = Escape-Html($cell.Trim())
$cellHtml = $cellHtml -replace '`([^`]+)`', '<code>$1</code>'
[void]$builder.AppendLine("<$tag>$cellHtml</$tag>")
}
[void]$builder.AppendLine('</tr>')
if ($i -eq 0) { [void]$builder.AppendLine('</thead>') }
}
if ($rows.Count -gt 1) { [void]$builder.AppendLine('</tbody>') }
[void]$builder.AppendLine('</table>')
$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('</ol>') } else { [void]$builder.AppendLine('</ul>') }
$inListRef.Value = $false
$orderedRef.Value = $false
}
foreach ($line in $lines) {
if ($line -match '^\s*```') {
if ($inCode) {
[void]$sb.AppendLine('</code></pre>')
$inCode = $false
} else {
Flush-Table $sb ([ref]$inTable) $tableRows
Close-List $sb ([ref]$inList) ([ref]$listOrdered)
[void]$sb.AppendLine('<pre><code>')
$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("<h$level>$(Escape-Html $text)</h$level>")
continue
}
if ($line -match '^>\s*(.+)$') {
Close-List $sb ([ref]$inList) ([ref]$listOrdered)
$text = $Matches[1]
[void]$sb.AppendLine("<blockquote><p>$(Escape-Html $text)</p></blockquote>")
continue
}
if ($line -match '^---\s*$') {
Close-List $sb ([ref]$inList) ([ref]$listOrdered)
[void]$sb.AppendLine('<hr/>')
continue
}
if ($line -match '^\d+\.\s+(.+)$') {
if (-not $inList) { [void]$sb.AppendLine('<ol>'); $inList = $true; $listOrdered = $true }
$text = Escape-Html $Matches[1]
$text = $text -replace '`([^`]+)`', '<code>$1</code>'
[void]$sb.AppendLine("<li>$text</li>")
continue
}
if ($line -match '^-\s+(.+)$') {
if (-not $inList) { [void]$sb.AppendLine('<ul>'); $inList = $true; $listOrdered = $false }
$text = Escape-Html $Matches[1]
$text = $text -replace '`([^`]+)`', '<code>$1</code>'
[void]$sb.AppendLine("<li>$text</li>")
continue
}
if ([string]::IsNullOrWhiteSpace($line)) {
Close-List $sb ([ref]$inList) ([ref]$listOrdered)
continue
}
Close-List $sb ([ref]$inList) ([ref]$listOrdered)
$p = Escape-Html $line
$p = $p -replace '`([^`]+)`', '<code>$1</code>'
[void]$sb.AppendLine("<p>$p</p>")
}
Flush-Table $sb ([ref]$inTable) $tableRows
Close-List $sb ([ref]$inList) ([ref]$listOrdered)
if ($inCode) { [void]$sb.AppendLine('</code></pre>') }
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 = @"
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>Mag2Car Guide</title>
<style>
@page { size: A4; margin: 18mm 16mm; }
body { font-family: "Microsoft YaHei", "Segoe UI", sans-serif; color: #222; line-height: 1.55; font-size: 11pt; }
h1 { font-size: 22pt; border-bottom: 2px solid #2563eb; padding-bottom: 8px; margin-top: 0; }
h2 { font-size: 16pt; color: #1e40af; margin-top: 22px; page-break-after: avoid; }
h3 { font-size: 13pt; color: #1d4ed8; margin-top: 16px; page-break-after: avoid; }
h4, h5 { font-size: 11.5pt; margin-top: 12px; page-break-after: avoid; }
p, li { orphans: 3; widows: 3; }
code, pre { font-family: Consolas, "Courier New", monospace; }
code { background: #f3f4f6; padding: 1px 4px; border-radius: 3px; font-size: 10pt; }
pre { background: #f8fafc; border: 1px solid #e5e7eb; padding: 10px; overflow-x: auto; font-size: 9.5pt; }
pre code { background: transparent; padding: 0; }
table { border-collapse: collapse; width: 100%; margin: 10px 0 14px; font-size: 10pt; page-break-inside: avoid; }
th, td { border: 1px solid #d1d5db; padding: 6px 8px; vertical-align: top; }
th { background: #eff6ff; text-align: left; }
tr:nth-child(even) td { background: #fafafa; }
blockquote { border-left: 4px solid #93c5fd; margin: 10px 0; padding: 6px 12px; color: #374151; background: #f8fafc; }
hr { border: none; border-top: 1px solid #e5e7eb; margin: 18px 0; }
ul, ol { margin: 6px 0 10px 20px; }
</style>
</head>
<body>
$body
</body>
</html>
"@
[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"