Shrink a whole archive of scanned PDFs to tiny, searchable, 1-bit black & white — without touching a single original.
Drop a folder of scanned PDFs. PaperPress analyses every file — recursively — and shows you exactly what it would do before it does anything. Tick what you want, hit Convert, and a mirrored copy of the folder appears with fat scans re-compressed to crisp 1-bit CCITT G4 (~20 KB per page) and everything else passed through byte-identical. A 5.7 MB scan becomes ~300 KB; your originals are never modified.
PaperPress is the batch companion to PaperDrop, which makes these compact PDFs at scan time.
-
Analyse before convert — every PDF gets a verdict up front: Re-compress (fat raster scans), Born digital (real text/vector PDFs that rasterising would only ruin), Already converted, Already compact (1-bit or 4-bit), or Already small. Nothing is written until you confirm.
-
Idempotent — every output is stamped (PDF Producer metadata), so re-analysing a converted folder marks everything Already converted and nothing is ever re-encoded generation over generation.
-
Tiny archival pages — scan pages are re-rendered at 300 dpi (higher-res sources are downsampled; lower-res sources are upsampled so the antialiasing they carry becomes smooth 1-bit edges rather than jagged text), binarised with a locally adaptive threshold (Sauvola — faded print survives alongside bold print), despeckled, and stored with CCITT G4 fax compression: ~20 KB per A4 page.
-
Photos survive — pages that are actually photographic (histogram and local-contrast analysis, not guesswork) are kept as grayscale JPEG instead of being dithered to mush. One document can mix both.
-
Legibility beats compression — sources scanned below 150 dpi never get binarised (small print can't survive it), and above that a worst-region damage check compares the 1-bit result against the grayscale it came from: if any region of the page degrades, the whole page stays grayscale — 4-bit Flate by default (crisper and smaller than JPEG on document content), or JPEG via Settings.
-
Searchable — a fresh invisible text layer via Apple's Vision OCR on every converted page. No cloud, no external tools.
-
See it before and after — analyse is instant and read-only; convert shows a live count and finishes with per-file results. Press space to Quick Look any file — sources on the review list, converted outputs on the results — and arrow through the batch:
-
Scan edges cleaned — the black bands a scanner lid or skewed feed leaves along page edges are whitened (only when they hug the border — content that reaches the edge is kept; toggleable).
-
A proper Finder citizen — right-click → Open With → PaperPress on PDFs or folders (Preview stays your default), drop them on the Dock icon, or use "Analyse with PaperPress" in the Services menu; files arriving while a review is open join the current batch.
-
Never destructive — output goes to a separate folder mirroring the source tree; pass-through files are copied byte-identical and file dates are preserved. If a conversion wouldn't save at least 20% (configurable), the original is copied instead.
-
Native and honest — SwiftUI, signed and notarized, zero dependencies, no telemetry, entirely offline.
- Download the latest DMG
- Drag PaperPress into Applications
- Launch — no security warnings; the app is notarized by Apple
git clone https://github.com/bensquire/PaperPress.git
cd PaperPress
./bundle.sh # builds + signs PaperPress.app (needs a Developer ID)Or for development: swift build && swift run PaperPress.
make test # unit tests (XCTest, AAA style)
make lint # Apple swift-format, strict
make format # auto-format in placeEnable the pre-commit hook (lint + tests) with:
git config core.hooksPath .githooksSources/PressKit/ # engine: inspection, classification, compression
PDFInspector.swift # verdicts: convert / born-digital / already-*
PDFRender.swift # page rasteriser (native-dpi, rotation-aware)
PageClassifier.swift # text vs photo (paper-peak + smooth-midtone)
Binarize.swift # Sauvola adaptive threshold + worst-region
# damage metric (the G4-or-grayscale decision)
EdgeClean.swift # scan-edge band removal (content-safe)
Gray4.swift # 4-bit grayscale Flate encoder (PNG predictor)
Converter.swift # per-page G4/4-bit/JPEG ladder, min-saving guard
Pipeline.swift # Otsu, cleanup, 1-bit packing (from PaperDrop)
PDFWriter.swift # hand-rolled PDF: G4 + DCT + Flate + OCR layer
G4.swift, OCR.swift # G4 stream extraction, Vision OCR
Sources/PressApp/ # SwiftUI app: analyse → review → convert
Sources/PaperPress/ # @main entry point
Tests/PaperPressTests/ # unit tests with programmatic PDF fixtures
icon/ # programmatic icon generator
scripts/ # DMG packaging
- The page's embedded scan image is found and its true resolution measured; black scan-edge bands are whitened (content that reaches the edge is kept).
- A histogram + spatial pass decides: document or photo?
- Documents scanned at 150 dpi or better are re-rendered at 300 dpi (low-res antialiasing becomes smooth 1-bit edges), binarised with a locally adaptive threshold (Sauvola — faint print survives because each pixel is judged against its neighbourhood, not one global cut), despeckled, and G4-compressed — the encoding fax machines used, unbeatable for black-and-white text.
- The 1-bit result is then compared region-by-region against the grayscale it came from; if the worst region measurably degraded (merged strokes, filled counters), or the source was below 150 dpi to begin with, the page ships as 4-bit grayscale Flate instead — legibility beats compression.
- Photos: grayscale JPEG at 150 dpi — continuous tone carries no useful detail beyond that.
- Vision OCR reads a 150 dpi grayscale of the page (measured as accurate as higher resolutions, much faster) and an invisible text layer is embedded, so Spotlight and Preview can search the result.
- If the rebuilt PDF isn't meaningfully smaller, the original is copied through unchanged — the tool never makes a file worse.
Releases are cut by tag. Pushing a v* tag makes CI build, sign,
notarize, and staple the DMG, then publish a GitHub Release with
generated notes:
git tag v0.1.0
git push --tagsThe release workflow needs six repository secrets (Settings → Secrets
and variables → Actions) for signing and notarization:
SIGNING_CERTIFICATE_P12_BASE64, SIGNING_CERTIFICATE_PASSWORD,
APPLE_TEAM_ID, APPLE_API_KEY_BASE64, APPLE_API_KEY_ID,
APPLE_API_ISSUER_ID. A vX.Y.Z-something tag (e.g. v0.2.0-rc1)
publishes as a prerelease. Plain pushes only run lint/tests/smoke —
tags are the only way a release ships.

