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.
This commit is contained in:
2026-08-27 07:37:09 +02:00
parent 532485a830
commit 6610acfa44
230 changed files with 9362 additions and 11508 deletions
+6 -6
View File
@@ -5,15 +5,15 @@ using System.Runtime.InteropServices;
using System.Text;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
namespace KillerPDF.Features
namespace MmdPdf.Features
{
// ============================================================
// Headless CLI batch mode
// ============================================================
//
// KillerPDF.exe --batch-resave <input.pdf|inputDir> <output.pdf|outputDir> [--log <file.csv>] [--quiet]
// MmdPdf.exe --batch-resave <input.pdf|inputDir> <output.pdf|outputDir> [--log <file.csv>] [--quiet]
//
// Resaves one PDF (or every *.pdf under a folder tree, mirroring the
// relative structure into the output folder) through the same pipeline a
@@ -25,7 +25,7 @@ namespace KillerPDF.Features
//
// Built for the veraPDF validation harness (validation/): baseline the
// corpus, --batch-resave it, validate the output tree, then diff with
// validation/Compare-VeraPDF.ps1. The claim being tested is "a KillerPDF
// validation/Compare-VeraPDF.ps1. The claim being tested is "a MmdPdf
// save does not degrade standards conformance".
//
// Exit codes: 0 = every file OK or SKIP, 1 = at least one FAIL
@@ -34,7 +34,7 @@ namespace KillerPDF.Features
// Invoked from App.OnStartup BEFORE the single-instance mutex, so a batch
// run works even while a GUI instance is open and never forwards to it.
//
// KillerPDF builds as a GUI-subsystem exe, so it has no console of its
// MmdPdf builds as a GUI-subsystem exe, so it has no console of its
// own; AttachConsole(-1) latches onto the parent terminal when launched
// from one. Output interleaves with the prompt (standard GUI-app quirk) -
// the authoritative record is the --log CSV and the exit code.
@@ -82,7 +82,7 @@ namespace KillerPDF.Features
if (badUsage || string.IsNullOrWhiteSpace(input) || string.IsNullOrWhiteSpace(output))
{
con.WriteLine("Usage: KillerPDF.exe --batch-resave <input.pdf|inputDir> <output.pdf|outputDir> [--log <file.csv>] [--quiet]");
con.WriteLine("Usage: MmdPdf.exe --batch-resave <input.pdf|inputDir> <output.pdf|outputDir> [--log <file.csv>] [--quiet]");
exitCode = 2;
return true;
}
+21 -21
View File
@@ -18,14 +18,14 @@ using Docnet.Core.Models;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using MmdPdf.Services;
// The scrubs, bitmap helpers, import helpers and PDFium interop all live in Services
// (PdfScrub.cs, BitmapHelpers.cs, PdfImport.cs, PdfiumInterop.cs; KillerUI refactor,
// (PdfScrub.cs, BitmapHelpers.cs, PdfImport.cs, PdfiumInterop.cs; MmdPdfUI refactor,
// 2026-07-31), called qualified below. No Features-to-Shell reaches remain in this file.
// OpenBatchConsole and FlattenBatchDetail are shared with the batch runner.
using static KillerPDF.Features.BatchRunner;
using static MmdPdf.Features.BatchRunner;
namespace KillerPDF.Features
namespace MmdPdf.Features
{
// ============================================================
// Command-line interface
@@ -35,7 +35,7 @@ namespace KillerPDF.Features
// BEFORE the single-instance mutex, so CLI runs work while a GUI instance
// is open, never forward to it, and never show a window. A launch with no
// recognized command flag falls through to the normal GUI (including the
// classic "KillerPDF.exe file.pdf" file-association open).
// classic "MmdPdf.exe file.pdf" file-association open).
//
// Each command reuses the same pipeline its GUI equivalent runs - the
// merge named-destination rewrite, the pre-save scrubs, the PDFium
@@ -140,11 +140,11 @@ namespace KillerPDF.Features
private static string CliHelpText() => string.Join(Environment.NewLine,
[
"KillerPDF " + (Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "") + " - command line usage",
"MmdPdf " + (Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "") + " - command line usage",
"",
" KillerPDF.exe <file.pdf> open in the app",
" KillerPDF.exe --version | -v print version",
" KillerPDF.exe --help | -h | /? this text",
" MmdPdf.exe <file.pdf> open in the app",
" MmdPdf.exe --version | -v print version",
" MmdPdf.exe --help | -h | /? this text",
"",
" --merge <out.pdf> <in1> <in2> ... merge PDFs (and images) into one PDF",
" --extract-pages <in.pdf> <pages> <out.pdf>",
@@ -165,7 +165,7 @@ namespace KillerPDF.Features
" open/save pipeline (validation harness)",
"",
"Exit codes: 0 success, 1 operation failed, 2 bad usage.",
"Runs headless and works while the KillerPDF window is open.",
"Runs headless and works while the MmdPdf window is open.",
]);
/// <summary>
@@ -246,7 +246,7 @@ namespace KillerPDF.Features
{
if (pos.Count < 3)
{
con.WriteLine("Usage: KillerPDF.exe --merge <out.pdf> <in1.pdf> <in2.pdf> ...");
con.WriteLine("Usage: MmdPdf.exe --merge <out.pdf> <in1.pdf> <in2.pdf> ...");
return 2;
}
string outPath = Path.GetFullPath(pos[0]);
@@ -297,7 +297,7 @@ namespace KillerPDF.Features
{
if (pos.Count != 3)
{
con.WriteLine("Usage: KillerPDF.exe --extract-pages <in.pdf> <pages> <out.pdf> (pages like 1-3,5,9-12)");
con.WriteLine("Usage: MmdPdf.exe --extract-pages <in.pdf> <pages> <out.pdf> (pages like 1-3,5,9-12)");
return 2;
}
string inPath = Path.GetFullPath(pos[0]), spec = pos[1], outPath = Path.GetFullPath(pos[2]);
@@ -325,7 +325,7 @@ namespace KillerPDF.Features
{
if (pos.Count != 2)
{
con.WriteLine("Usage: KillerPDF.exe --split <in.pdf> <outputFolder>");
con.WriteLine("Usage: MmdPdf.exe --split <in.pdf> <outputFolder>");
return 2;
}
string inPath = Path.GetFullPath(pos[0]), outDir = Path.GetFullPath(pos[1]);
@@ -358,7 +358,7 @@ namespace KillerPDF.Features
{
if (pos.Count != 2)
{
con.WriteLine("Usage: KillerPDF.exe --decrypt <in.pdf> <out.pdf> [--password <password>]");
con.WriteLine("Usage: MmdPdf.exe --decrypt <in.pdf> <out.pdf> [--password <password>]");
return 2;
}
string inPath = Path.GetFullPath(pos[0]), outPath = Path.GetFullPath(pos[1]);
@@ -479,7 +479,7 @@ namespace KillerPDF.Features
{
if (pos.Count != 2)
{
con.WriteLine("Usage: KillerPDF.exe --to-image <in.pdf> <outputFolder> [--dpi <n>] [--format png|jpg] [--pages <range>] [--transparent]");
con.WriteLine("Usage: MmdPdf.exe --to-image <in.pdf> <outputFolder> [--dpi <n>] [--format png|jpg] [--pages <range>] [--transparent]");
return 2;
}
string inPath = Path.GetFullPath(pos[0]), outDir = Path.GetFullPath(pos[1]);
@@ -520,7 +520,7 @@ namespace KillerPDF.Features
{
w = pr.GetPageWidth();
h = pr.GetPageHeight();
raw = KillerPDF.Services.PdfiumInterop.RenderPageWithAnnotations(
raw = MmdPdf.Services.PdfiumInterop.RenderPageWithAnnotations(
renderPath, idx, w, h, transparent)
?? (transparent
? pr.GetImage()
@@ -546,7 +546,7 @@ namespace KillerPDF.Features
{
if (pos.Count != 2)
{
con.WriteLine("Usage: KillerPDF.exe --flatten <in.pdf> <out.pdf> [--dpi <n>]");
con.WriteLine("Usage: MmdPdf.exe --flatten <in.pdf> <out.pdf> [--dpi <n>]");
return 2;
}
string inPath = Path.GetFullPath(pos[0]), outPath = Path.GetFullPath(pos[1]);
@@ -570,7 +570,7 @@ namespace KillerPDF.Features
// #141: WithAnnotations, or the rebuild drops the file's own markup.
w = pr.GetPageWidth();
h = pr.GetPageHeight();
raw = KillerPDF.Services.PdfiumInterop.RenderPageWithAnnotations(renderPath, i, w, h)
raw = MmdPdf.Services.PdfiumInterop.RenderPageWithAnnotations(renderPath, i, w, h)
?? pr.GetImage(new Docnet.Core.Converters.NaiveTransparencyRemover());
}
int rot = rotations != null && i < rotations.Length ? rotations[i] : 0;
@@ -616,7 +616,7 @@ namespace KillerPDF.Features
{
if (pos.Count != 1)
{
con.WriteLine("Usage: KillerPDF.exe --print <in.pdf> [--printer <name>] [--pages <range>] [--copies <n>]");
con.WriteLine("Usage: MmdPdf.exe --print <in.pdf> [--printer <name>] [--pages <range>] [--copies <n>]");
return 2;
}
string inPath = Path.GetFullPath(pos[0]);
@@ -677,7 +677,7 @@ namespace KillerPDF.Features
{
w = pr.GetPageWidth();
h = pr.GetPageHeight();
raw = KillerPDF.Services.PdfiumInterop.RenderPageWithAnnotations(renderPath, idx, w, h)
raw = MmdPdf.Services.PdfiumInterop.RenderPageWithAnnotations(renderPath, idx, w, h)
?? pr.GetImage(); // #141
}
int rot = rotations != null && idx < rotations.Length ? rotations[idx] : 0;
@@ -740,7 +740,7 @@ namespace KillerPDF.Features
{
if (pos.Count != 2)
{
con.WriteLine("Usage: KillerPDF.exe --ocr <in.pdf> <out.pdf> [--lang <code>] (default eng)");
con.WriteLine("Usage: MmdPdf.exe --ocr <in.pdf> <out.pdf> [--lang <code>] (default eng)");
return 2;
}
string inPath = Path.GetFullPath(pos[0]), outPath = Path.GetFullPath(pos[1]);