Files
mmd-pdf/build/build-portable.ps1
T
kua-agent 6610acfa44 feat: rebrand to MMD PDF, single corporate theme, and remove original text under an edit
- Services/PdfRedactText.cs: strip text whose origin falls inside a CoverAnnotation
  from the page content stream at save time, hooked into PdfBurn.DrawAnnotationsIntoDoc.
  Fixes edited values staying recoverable by text extraction.
- Themes/MMD.xaml replaces all thirteen themes; picker and accent strip removed; no dark mode.
- Rename KillerPDF -> MMD PDF across code, resources, packaging and locale strings; new icon.
- Remove the upstream author credit and the in-app install button.
2026-08-27 07:37:09 +02:00

134 lines
6.2 KiB
PowerShell

#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 'MmdPdf.csproj'
$launcherProject = Join-Path $projectDir 'Packaging\MmdPdfLauncher\MmdPdfLauncher.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 'MmdPdf.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 'MmdPdf.csproj has no Version.' }
if (-not $RepackOnly) {
Write-Host "==> Building loose MmdPdf 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 'MmdPdf.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 'MmdPdf.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=MmdPdf `
-p:LauncherVersion=$version `
-p:LauncherIcon="$(Join-Path $projectDir 'Resources\mmd-icon.ico')" `
-p:PayloadZip="$payloadZip" `
-p:AllowUnsignedInstall="$(!$RequireSignature)" `
-p:PublishDir="$launcherOutput\"
if ($LASTEXITCODE -ne 0) { throw 'Launcher build failed.' }
$builtLauncher = Join-Path $launcherOutput 'MmdPdf.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
}