PDF-Compression: Tool Research + Rollout
Section titled “PDF-Compression: Tool Research + Rollout”Date: 2026-08-17
What was asked
Section titled “What was asked”Talbot noticed Cursor Stripe receipts run ~10x the size of near-identical Anthropic ones, and a BMO Investorline statement corpus had months running ~6x bigger than others. Research + validate a lossless PDF compression tool (borrow, don’t build), confirm no readability loss on real receipts + the 12 BMO 2025 statements.
What was found
Section titled “What was found”The size gaps were never a compression-settings problem. qpdf/zopfli lossless recompression tops out at ~7–15% — content streams are already zlib-deflated, near their own entropy limit. The real cause: each affected PDF embeds a print-resolution raster image (e.g. a 2550×300px header banner) wildly oversized for screen/archival use. Confirmed by comparing embedded-image byte totals between a small and a large file of the same document type.
Fix: Ghostscript’s /screen preset (72dpi image downsampling) — leaves text as vector (100% lossless, verified via PyMuPDF text-extraction string-equality across every page of every test file) while discarding the oversized image resolution. On the 12 BMO 2025 statements: 14.47MB → 2.09MB (6.9x). Compared head-to-head against /ebook (150dpi) — not a close call, /ebook runs ~45% larger overall (3.04MB, 4.8x). /printer (300dpi) is a third, milder fallback if image fidelity ever needs to be higher.
Ghostscript needed sudo apt install ghostscript — Talbot ran it directly (no password available in-agent-session). qpdf was installed without sudo via apt-get download qpdf libqpdf29t64 + dpkg-deb -x into ~/.local/{bin,lib} — useful pattern for any Ubuntu package when sudo isn’t available: bypasses the package manager, no root needed.
Tool shipped
Section titled “Tool shipped”pdfcompress (~/utils/pdfcompress/, on PATH via ~/.local/bin/pdfcompress):
pdfcompress in.pdf out.pdf # default: Ghostscript /screen, lossy images/lossless textpdfcompress --lossless in.pdf out.pdf # qpdf recompress only, nothing touches imagespdfcompress --batch src_dir/ dst_dir/ # recursive, mirrors subfolder structure, prints per-file + totalFull findings/benchmarks: Core/IT/Utils/External/pdfcompress.md.
Doc/folder cleanup done in the same pass
Section titled “Doc/folder cleanup done in the same pass”pdfcompress.mdmovedCustom/→External/(thin wrapper over external binaries, not custom-built logic) —pdftext.mdstays inCustom/(real installeduv-managed package, different shape).- Trimmed the duplicated benchmark tables out of
Utilities.md’s PDF-compression section to a 2-line pointer atpdfcompress.md(SSOT).
Gotcha found + fixed: stale doc pointer
Section titled “Gotcha found + fixed: stale doc pointer”Core/IT/Utils/Custom/rename-receipts.md pointed at “Core/Processes/Tasks/File-Naming-Upgrade.md for the full decision trail” — that task had actually completed 2026-07-29 and its task file was correctly deleted per the normal lifecycle; the pointer just never got updated to the log. Repointed it at Core/Processes/Logs/2026-07-29_File-Naming-Reference-Artifacts.md. New open item found in the same pass: Statements\Investments\ was never in rename_receipts’ scope and uses a different naming convention (AccountNumber_MMYYYY.pdf) than the Naming System.md §4 standard used elsewhere — flagged, not resolved.
Follow-on tasks spun out (not part of this task’s scope)
Section titled “Follow-on tasks spun out (not part of this task’s scope)”Core/Accounting/Tasks/PDF-Compression-Rollout.md— batch-applypdfcompressto the broader accounting corpus (Rogers bills, remaining statement types), compress-then-rename order.Core/Processes/Tasks/File-Naming-Vault-Wide-Rollout.md— resolve theStatements\Investments\convention question + the still-deferred vault-wide retroactive rename.
Result
Section titled “Result”12 BMO 2025 statements + nov.PDF/dec.PDF renamed in place to match the existing 21414578_MMYYYY.pdf pattern (uncompressed originals; compression only applied to the c:\tmp\FSS-BMO\ showcase output, not the source folder).