73 lines
3.0 KiB
PowerShell
73 lines
3.0 KiB
PowerShell
# 将数据表 JPEG 包装成极简单页 A4 PDF (DCTDecode 直嵌, 字节级 xref 偏移)
|
|
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\gen-pdf.ps1
|
|
# 说明: 中文内容以图片形式嵌入, 规避 PDF 中文字体嵌入问题; 结构最小化, 主流阅读器可打开。
|
|
param(
|
|
[string]$Jpg,
|
|
[string]$Pdf
|
|
)
|
|
|
|
Add-Type -AssemblyName System.Drawing
|
|
$img = [System.Drawing.Image]::FromFile($Jpg)
|
|
$w = $img.Width
|
|
$h = $img.Height
|
|
$img.Dispose()
|
|
$jpgBytes = [System.IO.File]::ReadAllBytes($Jpg)
|
|
|
|
$chunks = New-Object System.Collections.Generic.List[byte[]]
|
|
$offsets = New-Object System.Collections.Generic.List[int]
|
|
$pos = 0
|
|
|
|
function Add-Ascii([string]$t) {
|
|
$b = [System.Text.Encoding]::ASCII.GetBytes($t)
|
|
$chunks.Add($b)
|
|
$script:pos += $b.Length
|
|
}
|
|
function Mark-Obj() { $offsets.Add($script:pos) }
|
|
|
|
$imgLen = $jpgBytes.Length + 1
|
|
$content = 'q 595.28 0 0 841.89 0 0 cm /Im0 Do Q'
|
|
$contentLen = $content.Length + 1
|
|
|
|
Add-Ascii "%PDF-1.4`n"
|
|
Mark-Obj; Add-Ascii "1 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj`n"
|
|
Mark-Obj; Add-Ascii "2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj`n"
|
|
Mark-Obj; Add-Ascii "3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595.28 841.89] /Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>endobj`n"
|
|
Mark-Obj; Add-Ascii "4 0 obj<< /Type /XObject /Subtype /Image /Width $w /Height $h /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length $imgLen >>stream`n"
|
|
$chunks.Add($jpgBytes); $pos += $jpgBytes.Length
|
|
Add-Ascii "`nendstream`nendobj`n"
|
|
Mark-Obj; Add-Ascii "5 0 obj<< /Length $contentLen >>stream`n"
|
|
Add-Ascii "$content`nendstream`nendobj`n"
|
|
|
|
$xrefOffset = $pos
|
|
$sb = New-Object System.Text.StringBuilder
|
|
[void]$sb.Append("xref`n0 6`n0000000000 65535 f `n")
|
|
foreach ($off in $offsets) {
|
|
[void]$sb.Append(("{0:0000000000} {1:00000} n `n" -f $off, 0))
|
|
}
|
|
Add-Ascii $sb.ToString()
|
|
Add-Ascii ("trailer`n<< /Size 6 /Root 1 0 R >>`nstartxref`n" + $xrefOffset + "`n%%EOF`n")
|
|
|
|
$fs = [System.IO.File]::Create($Pdf)
|
|
foreach ($b in $chunks) { $fs.Write($b, 0, $b.Length) }
|
|
$fs.Close()
|
|
|
|
# ---- 自检: startxref 指向 xref, 且每个对象偏移处是 "N 0 obj" ----
|
|
$check = [System.IO.File]::ReadAllBytes($Pdf)
|
|
$text = [System.Text.Encoding]::ASCII.GetString($check)
|
|
$m = [regex]::Match($text, 'startxref\s+(\d+)')
|
|
if (-not $m.Success) { Write-Output ("FAIL: 无 startxref - " + $Pdf); return }
|
|
$xref = [int64]$m.Groups[1].Value
|
|
$ok = ($text.Substring($xref, 4) -eq 'xref')
|
|
$lines = $text.Substring($xref + 4).Split("`n")
|
|
# xref 段: 行0='xref', 行1='0 6', 行2=第0号自由条目, 行3..7=对象1..5
|
|
for ($i = 3; $i -lt 8; $i++) {
|
|
$entry = $lines[$i]
|
|
$objNum = $i - 2
|
|
if ($entry -match '^(\d{10}) 00000 n') {
|
|
$off2 = [int64]$entry.Substring(0, 10)
|
|
if ($text.Substring($off2, 10) -notmatch ('^' + $objNum + ' 0 obj')) { $ok = $false }
|
|
} else { $ok = $false }
|
|
}
|
|
if ($ok) { Write-Output ("OK: " + $Pdf + " (" + (($check.Length / 1KB).ToString('0') + " KB)")) }
|
|
else { Write-Output ("FAIL: xref 校验失败 - " + $Pdf) }
|