vendor: import KillerPDF 1.7.5 source (GPL-3.0) as the base for MMD PDF

This commit is contained in:
2026-08-27 06:58:22 +02:00
commit 532485a830
577 changed files with 149058 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
#Requires -Version 5.1
param(
[string]$Configuration = "Release",
[switch]$KeepSymbols,
[switch]$RepackOnly,
[switch]$RequireSignature
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$projectDir = Split-Path -Parent $PSScriptRoot
$appProject = Join-Path $projectDir 'KillerPDF.csproj'
$launcherProject = Join-Path $projectDir 'Packaging\KillerLauncher\KillerLauncher.csproj'
$artifactRoot = Join-Path $projectDir "bin\$Configuration\net48\portable-package"
$payloadDir = Join-Path $artifactRoot 'payload'
$payloadZip = Join-Path $artifactRoot 'payload.zip'
$launcherOutput = Join-Path $artifactRoot 'launcher'
$publicDir = Join-Path $projectDir "bin\$Configuration\net48\publish"
$publicExe = Join-Path $publicDir 'KillerPDF.exe'
if (-not $RepackOnly) {
if ([IO.Directory]::Exists($artifactRoot)) { [IO.Directory]::Delete($artifactRoot, $true) }
}
if ([IO.Directory]::Exists($launcherOutput)) { [IO.Directory]::Delete($launcherOutput, $true) }
[IO.Directory]::CreateDirectory($artifactRoot) | Out-Null
[IO.Directory]::CreateDirectory($payloadDir) | Out-Null
[IO.Directory]::CreateDirectory($launcherOutput) | Out-Null
[IO.Directory]::CreateDirectory($publicDir) | Out-Null
$versionXml = [xml](Get-Content -Raw -LiteralPath $appProject)
$versionNode = $versionXml.SelectSingleNode('/Project/PropertyGroup/Version')
$version = if ($versionNode) { [string]$versionNode.InnerText } else { '' }
if (-not $version) { throw 'KillerPDF.csproj has no Version.' }
if (-not $RepackOnly) {
Write-Host "==> Building loose KillerPDF payload $version..." -ForegroundColor Cyan
& dotnet publish $appProject -c $Configuration `
-p:KillerPayloadBuild=true `
-p:PublishDir="$payloadDir\"
if ($LASTEXITCODE -ne 0) { throw 'Payload build failed.' }
if (-not $KeepSymbols) {
foreach ($symbol in [IO.Directory]::GetFiles($payloadDir, '*.pdb', [IO.SearchOption]::AllDirectories)) {
[IO.File]::Delete($symbol)
}
}
# The PDF file-type icon is a loose installed asset even though the application also embeds it.
[IO.File]::Copy((Join-Path $projectDir 'Resources\pdf-file.ico'),
(Join-Path $payloadDir 'pdf-file.ico'), $true)
} elseif (-not [IO.File]::Exists((Join-Path $payloadDir 'KillerPDF.App.exe'))) {
throw 'RepackOnly requested but the prepared payload is missing.'
}
$manifestPath = Join-Path $payloadDir 'payload.manifest'
$payloadFiles = @([IO.Directory]::GetFiles($payloadDir, '*', [IO.SearchOption]::AllDirectories) |
Where-Object { -not [string]::Equals($_, $manifestPath, [StringComparison]::OrdinalIgnoreCase) } |
Sort-Object { $_.Substring($payloadDir.Length + 1) })
$actualPayloadNames = @($payloadFiles | ForEach-Object {
$_.Substring($payloadDir.Length + 1).Replace('\', '/')
})
$expectedPayloadNames = @(Get-Content -LiteralPath (Join-Path $PSScriptRoot 'payload-files.txt') |
Where-Object { $_ -and -not $_.StartsWith('#') } | Sort-Object)
$payloadDifference = @(Compare-Object $expectedPayloadNames $actualPayloadNames)
if ($payloadDifference.Count -gt 0) {
$details = ($payloadDifference | ForEach-Object { "$($_.SideIndicator) $($_.InputObject)" }) -join [Environment]::NewLine
throw "Payload file set changed. Review dependencies and update build\payload-files.txt deliberately:`n$details"
}
foreach ($required in 'KillerPDF.App.exe', 'pdfium.dll', 'PdfSharpCore.dll', 'System.Text.Json.dll') {
if ($actualPayloadNames -notcontains $required) {
throw "Required loose payload file is missing ($required). Costura/Fody may have run accidentally."
}
}
$manifestLines = foreach ($file in $payloadFiles) {
$relative = $file.Substring($payloadDir.Length + 1).Replace('\', '/')
$hash = (Get-FileHash -LiteralPath $file -Algorithm SHA256).Hash
$size = ([IO.FileInfo]$file).Length
"$hash`t$size`t$relative"
}
[IO.File]::WriteAllLines($manifestPath, $manifestLines, [Text.UTF8Encoding]::new($false))
Write-Host "==> Compressing one verified payload..." -ForegroundColor Cyan
Add-Type -AssemblyName System.IO.Compression
if ([IO.File]::Exists($payloadZip)) { [IO.File]::Delete($payloadZip) }
$zipStream = [IO.File]::Open($payloadZip, [IO.FileMode]::CreateNew)
try {
$archive = [IO.Compression.ZipArchive]::new($zipStream, [IO.Compression.ZipArchiveMode]::Create, $false)
try {
$allFiles = @([IO.Directory]::GetFiles($payloadDir, '*', [IO.SearchOption]::AllDirectories) |
Sort-Object { $_.Substring($payloadDir.Length + 1) })
foreach ($file in $allFiles) {
$relative = $file.Substring($payloadDir.Length + 1).Replace('\', '/')
$entry = $archive.CreateEntry($relative, [IO.Compression.CompressionLevel]::Optimal)
$entry.LastWriteTime = [DateTimeOffset]::new(2020, 1, 1, 0, 0, 0, [TimeSpan]::Zero)
$input = [IO.File]::OpenRead($file)
$output = $entry.Open()
try { $input.CopyTo($output) }
finally { $output.Dispose(); $input.Dispose() }
}
}
finally { $archive.Dispose() }
}
finally { $zipStream.Dispose() }
Write-Host "==> Building the public portable launcher..." -ForegroundColor Cyan
& dotnet publish $launcherProject -c $Configuration `
-p:LauncherAssemblyName=KillerPDF `
-p:LauncherVersion=$version `
-p:LauncherIcon="$(Join-Path $projectDir 'Resources\kp-icon.ico')" `
-p:PayloadZip="$payloadZip" `
-p:AllowUnsignedInstall="$(!$RequireSignature)" `
-p:PublishDir="$launcherOutput\"
if ($LASTEXITCODE -ne 0) { throw 'Launcher build failed.' }
$builtLauncher = Join-Path $launcherOutput 'KillerPDF.exe'
if (-not [IO.File]::Exists($builtLauncher)) { throw "Launcher output is missing: $builtLauncher" }
[IO.File]::Copy($builtLauncher, $publicExe, $true)
$payloadBytes = ([IO.FileInfo]$payloadZip).Length
$publicBytes = ([IO.FileInfo]$publicExe).Length
Write-Host " Payload files : $($payloadFiles.Count)" -ForegroundColor Green
Write-Host " Payload zip : $payloadBytes bytes" -ForegroundColor Green
Write-Host " Public EXE : $publicBytes bytes" -ForegroundColor Green
Write-Host " Output : $publicExe" -ForegroundColor Green
[pscustomobject]@{
Version = $version
PayloadFiles = $payloadFiles.Count
PayloadZipBytes = $payloadBytes
PublicExeBytes = $publicBytes
PublicExe = $publicExe
}
+58
View File
@@ -0,0 +1,58 @@
# Called automatically by the csproj after Publish.
# Produces <AppName>-<Version>-src.zip inside the publish folder.
# PS 5.1 / PS 7 compatible. Uses git to list tracked files so bin/obj/.vs never ship.
param(
[Parameter(Mandatory)][string]$ProjectDir,
[Parameter(Mandatory)][string]$Version,
[Parameter(Mandatory)][string]$AppName,
[Parameter(Mandatory)][string]$PublishDir
)
$ErrorActionPreference = 'Stop'
$projectDirFull = (Resolve-Path $ProjectDir).Path
$publishDirFull = if ([System.IO.Path]::IsPathRooted($PublishDir)) {
$PublishDir
} else {
Join-Path $projectDirFull $PublishDir
}
if (-not (Test-Path $publishDirFull)) {
New-Item -ItemType Directory -Force -Path $publishDirFull | Out-Null
}
$zip = Join-Path $publishDirFull "$AppName-$Version-src.zip"
if (Test-Path $zip) { Remove-Item $zip -Force }
$staging = Join-Path $env:TEMP "$AppName-src-$([guid]::NewGuid())"
try {
New-Item -ItemType Directory -Force -Path $staging | Out-Null
Push-Location $projectDirFull
try {
# Exclude the landing site: it is a separate deployable, not app source, and a
# release's own exe hash can never live correctly inside the source it is built
# from (circular). Keeps the bundle buildable-app-only and free of stale site info.
$files = @(& git ls-files 2>$null | Where-Object { $_ -notlike 'pdf-landing/*' })
if ($LASTEXITCODE -ne 0 -or $files.Count -eq 0) {
Write-Warning "Source bundle skipped: git ls-files returned no tracked files (is git installed and is this a repo?)."
return
}
foreach ($f in $files) {
# A file can be tracked in git but deleted on disk (removed without `git rm`).
# Skip it instead of aborting the whole bundle.
if (-not (Test-Path $f)) { Write-Warning "Skipping tracked file missing on disk: $f"; continue }
$dst = Join-Path $staging $f
$parent = Split-Path $dst -Parent
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Force -Path $parent | Out-Null }
Copy-Item $f $dst -Force
}
$rootLicense = Join-Path $projectDirFull 'LICENSE'
if (Test-Path $rootLicense) { Copy-Item $rootLicense (Join-Path $staging 'LICENSE') -Force }
} finally {
Pop-Location
}
Compress-Archive -Path (Join-Path $staging '*') -DestinationPath $zip -Force
Write-Host "Source bundle: $zip" -ForegroundColor Green
} finally {
Remove-Item $staging -Recurse -Force -ErrorAction SilentlyContinue
}
+127
View File
@@ -0,0 +1,127 @@
#Requires -Version 5.1
param(
[Parameter(Mandatory = $true)]
[string]$ExePath,
[int]$Iterations = 5,
[string]$OutputCsv = "",
[int]$TimeoutSeconds = 45,
[switch]$Launcher
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$ExePath = (Resolve-Path -LiteralPath $ExePath).Path
if (-not $OutputCsv) {
$OutputCsv = Join-Path $PSScriptRoot "startup-results.csv"
}
$outputParent = Split-Path -Parent $OutputCsv
if ($outputParent) { [System.IO.Directory]::CreateDirectory($outputParent) | Out-Null }
function Read-Trace([string]$Path) {
$lines = @(Get-Content -LiteralPath $Path)
if ($lines.Count -lt 2) { throw "Startup trace is incomplete: $Path" }
$header = $lines[0]
$processStartText = [regex]::Match($header, 'processStartUtc=([^|]+)').Groups[1].Value.Trim()
$traceStartText = [regex]::Match($header, 'traceStartUtc=([^|]+)').Groups[1].Value.Trim()
$processStart = [datetimeoffset]::Parse($processStartText, [Globalization.CultureInfo]::InvariantCulture)
$traceStart = [datetimeoffset]::Parse($traceStartText, [Globalization.CultureInfo]::InvariantCulture)
$loaderMs = ($traceStart - $processStart).TotalMilliseconds
$marks = @{}
foreach ($line in $lines | Select-Object -Skip 1) {
$parts = $line -split "`t", 2
if ($parts.Count -eq 2) {
$marks[$parts[1]] = [double]::Parse($parts[0], [Globalization.CultureInfo]::InvariantCulture)
}
}
if (-not $marks.ContainsKey('MainWindow ready')) { throw "Trace has no ready marker: $Path" }
$pdfiumMs = if ($marks.ContainsKey('pdfium integrity check starting') -and
$marks.ContainsKey('pdfium integrity check complete')) {
$marks['pdfium integrity check complete'] - $marks['pdfium integrity check starting']
} else { 0 }
[pscustomobject]@{
LoaderToOnStartupMs = [math]::Round($loaderMs, 1)
OnStartupToReadyMs = [math]::Round($marks['MainWindow ready'], 1)
ProcessToReadyMs = [math]::Round($loaderMs + $marks['MainWindow ready'], 1)
PdfiumIntegrityMs = [math]::Round($pdfiumMs, 1)
MainWindowConstructMs = if ($marks.ContainsKey('Locale initialized') -and
$marks.ContainsKey('MainWindow constructed')) {
[math]::Round($marks['MainWindow constructed'] - $marks['Locale initialized'], 1)
} else { 0 }
ReadyUtc = $traceStart.AddMilliseconds($marks['MainWindow ready'])
}
}
$results = @()
$oldTrace = [Environment]::GetEnvironmentVariable('KILLERPDF_STARTUP_TRACE', 'Process')
try {
for ($i = 1; $i -le $Iterations; $i++) {
$trace = Join-Path ([IO.Path]::GetTempPath()) ("killerpdf-startup-{0}.trace" -f [guid]::NewGuid().ToString('N'))
[Environment]::SetEnvironmentVariable('KILLERPDF_STARTUP_TRACE', $trace, 'Process')
$process = Start-Process -FilePath $ExePath -PassThru
$launchedUtc = $process.StartTime.ToUniversalTime()
$deadline = [datetime]::UtcNow.AddSeconds($TimeoutSeconds)
try {
while ([datetime]::UtcNow -lt $deadline) {
if ($process.HasExited) { throw "KillerPDF exited before reaching the ready marker (exit $($process.ExitCode))." }
if ((Test-Path -LiteralPath $trace) -and
(Select-String -LiteralPath $trace -SimpleMatch 'MainWindow ready' -Quiet)) { break }
Start-Sleep -Milliseconds 25
$process.Refresh()
}
if (-not (Test-Path -LiteralPath $trace) -or
-not (Select-String -LiteralPath $trace -SimpleMatch 'MainWindow ready' -Quiet)) {
throw "Timed out after $TimeoutSeconds seconds waiting for KillerPDF startup."
}
$timing = Read-Trace $trace
$processToReady = if ($Launcher) {
($timing.ReadyUtc.UtcDateTime - $launchedUtc).TotalMilliseconds
} else { $timing.ProcessToReadyMs }
$results += [pscustomobject]@{
Iteration = $i
CacheState = if ($i -eq 1) { 'first' } else { 'warm' }
Exe = $ExePath
ExeBytes = (Get-Item -LiteralPath $ExePath).Length
LoaderToOnStartupMs = $timing.LoaderToOnStartupMs
OnStartupToReadyMs = $timing.OnStartupToReadyMs
ProcessToReadyMs = [math]::Round($processToReady, 1)
PdfiumIntegrityMs = $timing.PdfiumIntegrityMs
MainWindowConstructMs = $timing.MainWindowConstructMs
}
}
finally {
if ($Launcher -and -not $process.HasExited) {
$children = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object { $_.ParentProcessId -eq $process.Id -and $_.Name -eq 'KillerPDF.App.exe' })
foreach ($child in $children) {
Stop-Process -Id $child.ProcessId -Force -ErrorAction SilentlyContinue
}
$null = $process.WaitForExit(5000)
}
if (-not $process.HasExited) {
$null = $process.CloseMainWindow()
if (-not $process.WaitForExit(3000)) { Stop-Process -Id $process.Id -Force }
}
Remove-Item -LiteralPath $trace -Force -ErrorAction SilentlyContinue
}
}
}
finally {
[Environment]::SetEnvironmentVariable('KILLERPDF_STARTUP_TRACE', $oldTrace, 'Process')
}
$results | Export-Csv -LiteralPath $OutputCsv -NoTypeInformation -Encoding UTF8
$results | Format-Table -AutoSize
$warm = @($results | Where-Object CacheState -eq 'warm')
if ($warm.Count -gt 0) {
Write-Host ("Warm mean process-to-ready: {0:N1} ms" -f (($warm | Measure-Object ProcessToReadyMs -Average).Average))
Write-Host ("Warm mean pdfium integrity: {0:N1} ms" -f (($warm | Measure-Object PdfiumIntegrityMs -Average).Average))
}
Write-Host "Results: $OutputCsv"
+44
View File
@@ -0,0 +1,44 @@
CommunityToolkit.Mvvm.dll
de/PdfSharpCore.resources.dll
Docnet.Core.dll
ICSharpCode.SharpZipLib.dll
KillerPDF.App.exe
KillerPDF.App.exe.config
LICENSE
Microsoft.Bcl.AsyncInterfaces.dll
Microsoft.Bcl.HashCode.dll
Microsoft.Extensions.DependencyInjection.Abstractions.dll
Microsoft.Extensions.Logging.Abstractions.dll
pdf-file.ico
pdfium.dll
PdfSharp.BarCodes.dll
PdfSharp.Charting.dll
PdfSharp.Cryptography.dll
PdfSharp.dll
PdfSharp.Quality.dll
PdfSharp.Shared.dll
PdfSharp.Snippets.dll
PdfSharp.System.dll
PdfSharp.WPFonts.dll
PdfSharpCore.dll
SixLabors.Fonts.dll
SixLabors.ImageSharp.dll
System.Buffers.dll
System.ComponentModel.Annotations.dll
System.IO.Pipelines.dll
System.Memory.dll
System.Numerics.Vectors.dll
System.Runtime.CompilerServices.Unsafe.dll
System.Security.Cryptography.Pkcs.dll
System.Text.Encoding.CodePages.dll
System.Text.Encodings.Web.dll
System.Text.Json.dll
System.Threading.Tasks.Extensions.dll
Tesseract.dll
UglyToad.PdfPig.Core.dll
UglyToad.PdfPig.dll
UglyToad.PdfPig.DocumentLayoutAnalysis.dll
UglyToad.PdfPig.Fonts.dll
UglyToad.PdfPig.Package.dll
UglyToad.PdfPig.Tokenization.dll
UglyToad.PdfPig.Tokens.dll