Skip to main content

pdfsqueeze

Shrink PDF files by targeting raster images — no external engine.

pdfsqueeze relies solely on pikepdf to read and rewrite the document, and on Pillow to decode, transform and encode images. No other runtime dependency.

The guiding rule is document safety over size: anything uncertain means the original is kept. Nothing is published that has not been validated, and the source is never overwritten.

The reduction is an estimate, not a guarantee. It depends on how much of the file is images, on the content itself, on filters already applied, and on the document structure.

Installation

pip install pdfsqueeze

Python 3.11 or later. No system package required.

Usage

# Inspect without touching anything
pdfsqueeze --analyze report.pdf
pdfsqueeze --analyze --json report.pdf        # pure JSON on stdout

# Find out what each mode would produce, before committing to one
pdfsqueeze --analyze --sizes report.pdf

# Dry run: what would happen, without writing
pdfsqueeze --dry-run --mode medium report.pdf

# Compress, output next to the source
pdfsqueeze -m medium -y report.pdf             # -> report_compressed.pdf

# Into a directory, with a custom suffix
pdfsqueeze -m light --suffix _web -o ./published report.pdf invoice.pdf

# A whole folder, in parallel
pdfsqueeze -r -m strong -y --jobs 4 ./scans

# Aim for a size cap
pdfsqueeze --target-size 5MB -y report.pdf

Profiles

Mode Target DPI Threshold JPEG quality Chroma Metadata Measured SSIM
light 225 330 85 4:4:4 kept ≥ 0.99
medium 150 180 70 4:2:0 kept ≥ 0.86
strong 96 110 45 4:2:0 stripped ≥ 0.81

The threshold is the effective resolution above which an image becomes a candidate for downsampling. light deliberately sits above the 300 DPI band, so it leaves ordinary office scans untouched. These values were measured, not assumed.

medium and strong ask for confirmation before running; -y answers once for the whole run.

Choosing a mode

--analyze --sizes reports what every mode would produce for a document:

  Projected output sizes:
  ┌────────┬─────┬─────────┬───────────┬────────┐
  │ Mode   │ DPI │ Quality │ Size      │ Gain   │
  ├────────┼─────┼─────────┼───────────┼────────┤
  │ light  │ 225 │ 85      │ 4.8 MiB   │ +0.1 % │
  │ medium │ 150 │ 70      │ 1.2 MiB   │ 75.5 % │
  │ strong │ 96  │ 45      │ 746.6 KiB │ 84.8 % │
  └────────┴─────┴─────────┴───────────┴────────┘

These figures are not estimates: each mode is processed and serialised in memory, through the same steps a real run uses, so the size shown is the size the command would write. The cost is that of a full compression per mode minus the disk write — roughly 2 to 4 seconds for a heavy document — which is why it sits behind an explicit option and --analyze alone stays immediate.

--dpi and --quality apply to every projected mode, which is why the table spells out the DPI and quality actually used.

Target size

--target-size 5MB is a cap, never a size to hit exactly. Accepted forms are 5MB, 800KB, 1.5M and 500k (decimal units, case-insensitive).

The selected mode runs first; as soon as an output fits under the cap it is published and the search stops. Every iteration re-reads the original source — a profile is never applied to the result of the previous one. The search is capped at 6 iterations and never goes below 72 DPI or quality 30. If the cap stays out of reach, the smallest valid result is published and the command exits with code 6.

What it does not do

  • It does not recompress JPXDecode, CCITTFaxDecode or JBIG2Decode streams — they are passed through untouched.
  • It does not convert CMYK images, and says so.
  • It does not re-encode 1-bit images, where JPEG would wreck a bitonal scan.
  • It never asks for, stores or guesses a password. A PDF that requires one is refused (exit code 4); a PDF that merely restricts permissions is processed, with its restrictions reproduced identically.
  • It never claims to preserve a digital signature: any rewrite invalidates it, and this is reported.
  • DPI estimation (v1): the transformation matrix is tracked through q/Q, cm and Do, nested form XObjects included. Skewed matrices and exotic graphics states yield an approximation.

Exit codes

Code Meaning
0 Success, analysis or dry run completed
1 Unexpected internal error
2 Invalid arguments, missing confirmation, or missing source
3 I/O error, path collision
4 PDF requires a password, or its encryption cannot be reproduced
5 Corrupt PDF, or the produced document failed validation
6 --target-size cap not reached
130 Interrupted with Ctrl-C

With several sources, processing continues and the most severe code wins, following the documented priority 5, 4, 3, 6, 2, 1.

License

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pdfsqueeze-1.0.3-py3-none-any.whl (53.4 kB view details)

Uploaded Python 3

File details

Details for the file pdfsqueeze-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: pdfsqueeze-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 53.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.15

File hashes

Hashes for pdfsqueeze-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 3c547a3b7e5146beae9e983a0d99fcf7a2924d6e679c2bc21b76bf3ac2c13502
MD5 1e92bd84c1bec5f7df55054da6fd1fd1
BLAKE2b-256 090fd3f4fe16a21c6521506c1b875187d003d129aa387d490c009dd8c366071a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.3 This release

1 file

1.0.2

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page