fix: stamp SnapOtter as Producer on generated PDFs (#416)

Conversion engines wrote their own names into PDF metadata: LibreOffice,
Ghostscript, pdfcpu, WeasyPrint, and PDFKit all stamped Producer/Creator
on generated files. A new doc_scrub_meta docs-profile script (PyMuPDF)
rewrites both fields to SnapOtter and drops the stale XMP copy; the
worker applies it to the 25 PDF-generating tools before outputs reach
object storage. Best effort by design: any failure keeps the original
bytes and only logs a warning.

Deliberately untouched: tools that edit the user's own PDF and preserve
its metadata (qpdf edits, sign, flatten), encrypted outputs (copied
through), and pdfa-convert, where a metadata rewrite risks PDF/A
conformance.

Claude-Session: https://claude.ai/code/session_01XGB4pGvTvb7sUX4JN745U7
This commit is contained in:
SnapOtter
2026-07-04 03:05:32 +00:00
committed by GitHub
parent 4d37092bbe
commit 8b3f1e6884
6 changed files with 152 additions and 1 deletions
+1
View File
@@ -45,6 +45,7 @@ export {
pdfMetadataSetPy,
pdfPageCountPy,
pdfRedactPy,
pdfScrubProducerPy,
pdfSignPy,
pdfTextPy,
pdfToWordPy,
+13
View File
@@ -20,6 +20,19 @@ export async function pdfFlattenPy(inPath: string, outPath: string): Promise<voi
}
}
/**
* Stamp SnapOtter as Producer/Creator on a GENERATED PDF (PyMuPDF), replacing
* the conversion engine's self-promotion (LibreOffice, Ghostscript, pdfcpu,
* WeasyPrint, PDFKit). Encrypted files are copied through untouched.
*/
export async function pdfScrubProducerPy(inPath: string, outPath: string): Promise<void> {
const stdout = await runDocsScript("doc_scrub_meta", { path: inPath, out: outPath });
const parsed = JSON.parse(stdout.trim()) as { ok?: boolean; error?: string };
if (parsed.error) {
throw new Error(`doc_scrub_meta failed: ${parsed.error}`);
}
}
/** True redaction with verification pass (PyMuPDF search + apply_redactions). */
export async function pdfRedactPy(
inPath: string,