Pykno is a local-first, object-preserving command-line PDF compressor for macOS. It contains no networking code, performs compression entirely on your Mac, and never overwrites an original file.
The recommended engine uses local Python packages:
- pikepdf, which embeds the QPDF PDF parser/writer;
- Pillow, which decodes, resizes, and JPEG-encodes raster images.
These packages do not upload or remotely process documents. Installation tools may contact their package indexes to download dependencies; PDF processing itself makes no network requests.
Install uv, clone the repository, and install an isolated command:
brew install uv
uv tool install .
pykno --versionFor development, create the repository-local environment:
./setup-pyknoThis creates an isolated .venv and installs an editable pykno command.
./pykno confidential.pdfThe default balanced mode caps full-page images at 144 dpi and uses 72 percent JPEG quality. It writes confidential-compressed.pdf beside the original.
For stronger compression with explicit image settings:
./pykno --preset small scan.pdf
./pykno --preset print report.pdfBatch processing and a separate output folder:
./pykno --preset balanced --output ./compressed one.pdf two.pdf three.pdfCustom image settings:
./pykno --dpi 110 --quality 62 confidential.pdfChoose the processing engine through the same command:
./pykno --engine objects document.pdf
./pykno --engine quartz document.pdf
./pykno --engine auto document.pdfRun ./pykno --help for every option.
After installing Pykno somewhere outside ~/Documents, add the configurable Finder action with:
pykno --install-quick-actionRight-click one or more PDFs and choose Quick Actions → Compress with Pykno. The action prompts once for Small, Balanced, Print, or custom DPI and JPEG quality settings.
Remove it with:
pykno --uninstall-quick-actionFinder restricts workflows from reading development tools inside ~/Documents. A package-manager installation avoids that macOS privacy boundary.
The Python engine finds raster image XObjects, decodes eligible unmasked 8-bit RGB or grayscale images, and calculates effective resolution from each image's page-space transformation matrix. It accounts for shared images and images nested in Form XObjects, downsamples images above the requested resolution, and replaces them with locally encoded JPEG streams only when the replacement is smaller. Malformed or untraceable content falls back to a conservative page-size cap.
Pages are not redrawn. Text, vectors, links, forms, annotations, outlines, metadata, page boxes, and encryption are preserved.
Images with transparency, masks, unusual bit depths, or unsupported color modes are deliberately left unchanged.
The Quartz redraw implementation is built into the main command:
./pykno --engine quartz --preset small document.pdfQuartz handles some image types the object-preserving engine skips, but may discard links, forms, outlines, attachments, tags, and other document-level structures.
The auto engine starts with object-preserving compression. If the document contains images but none can be recompressed, or if the result is not smaller, it also tries the original PDF with Quartz. It compares both local results and keeps the smaller one. The output reports which engine won.
If Quartz is unavailable or cannot open the input, auto mode keeps the valid object-preserving result and reports a warning instead of failing the whole operation.
Neither engine performs OCR. For a locked PDF, add --password-prompt. The password is read securely instead of being placed in shell history.
- There are no HTTP, analytics, telemetry, account, or cloud APIs in the source.
- The recommended engine uses only pikepdf/QPDF, Pillow, and local files.
- Output is first written to a temporary file, reopened, and checked for the same nonzero page count.
- The temporary file is moved into place only after validation.
- Existing output is preserved atomically unless --force is supplied, and output symlinks are always rejected.
- The input path can never be the output path.
- The object engine bounds decoded image pixels and nested Form traversal when processing malformed or hostile PDFs.
For highly sensitive documents, remember that Spotlight, Time Machine, cloud-synced folders, and other system services are separate from this program. Use a nonsynced local directory if those matter to your threat model.
Dependencies are isolated under .venv and reproduced from uv.lock. To rebuild the environment:
uv sync --lockedThe object-preserving engine is Python-based and designed to remain portable. The Quartz fallback and Finder Quick Action require macOS. On other platforms, --engine auto safely remains on the object engine and --engine quartz reports that Quartz is unavailable.
The Quartz helper compiles on first use and requires the Xcode Command Line Tools.
- Images with masks, transparency, unusual bit depths, or unsupported color modes are skipped.
- The object engine does not perform OCR, bitonal conversion, or vector-stream simplification.
- The Quartz engine may discard links, forms, outlines, attachments, tags, and other document-level structures.
- Effective-DPI analysis handles transformation matrices and nested Form XObjects, but malformed or untraceable content uses a conservative fallback.
- Compression is lossy for images that are replaced with JPEG. Keep the original PDF.
This is an independent clean-room implementation based on documented PDF and macOS APIs plus observable application behavior. It contains no copied proprietary source code or bundled third-party application assets.
Run the test suite with:
uv run --locked python -m unittest discover -s tests -vSee CONTRIBUTING.md for development guidance and SECURITY.md for responsible vulnerability reporting.
Pykno is available under the Apache License 2.0.