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,CCITTFaxDecodeorJBIG2Decodestreams — 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,cmandDo, 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pdfsqueeze-1.0.2-py3-none-any.whl.
File metadata
- Download URL: pdfsqueeze-1.0.2-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c79b1b02adc38da17111a52bf1ccf29254fc1267a936e62c702eb72020bb3a82
|
|
| MD5 |
2fc3303211ad00ad513e7618d6efd385
|
|
| BLAKE2b-256 |
7b1f54d1a17c534907700195da603bd50f3e80df62115eba755eee688ebecb1b
|