🗜️ Your document is 40MB because of screenshots, not sentences. Fix that for free, on your own machine.
Search that phrase and you find two kinds of tools. Web tools that cap you at 10–25MB for free and charge for anything bigger. And tools, web or desktop, that "compress" every image with the same fixed setting, usually near a 90% quality cut, whether the image needs it or not. Diagrams turn soft. Screenshots pick up rough edges around the text. And you still do not know why the file was big to begin with.
Here is what most tools never tell you: a Word file is almost never big because of the text. Text is tiny. The file is big because Word keeps every picture at its original size, say a 4000x3000 screenshot, even when the page shows it at six inches wide. Compress that without checking first, and you either save little or ruin images that never needed it.
docx-compressor does neither. It reads the size each picture is actually shown at, straight from the document's own XML. It resizes each picture to match that size, then saves it back in its original format. Nothing else in the file changes. It runs on your machine only: no upload, no account, no size limit, no subscription.
| Typical "compress docx" web tool | docx-compressor | |
|---|---|---|
| File size limit | Often capped at 10–25MB on free tier | None. Limited only by your disk. |
| Compression logic | Fixed, blind (same setting for every image) | Dynamic, measured per image from its actual on-page size |
| Where your file goes | Uploaded to a server you don't control | Never leaves your computer |
| Image quality | Often visibly worse: soft diagrams, rough edges on text | Resized to the DPI you choose; small, already-tight images are left alone |
| Text, styles, layout | Sometimes altered by re-export | Byte-identical. Only the image data changes. |
| Cost | Free tier plus a paywall for bigger files | Free, forever. MIT licensed. |
| Source | Closed | 100% Python, fully open |
Word stores two numbers for every picture: the size of the image file itself, and the size it is drawn at on the page (in EMUs, 914,400 per inch, on the wp:extent tag around it). Most tools read only the first number. docx-compressor reads the second, straight from word/document.xml and every header, footer, and footnote, then:
- Measures the largest size each picture is shown at, anywhere in the document.
- Resizes the picture to that size at your chosen DPI. The default, 150, keeps about 900px across on a six-inch figure. That is well above a normal screen, so diagrams, screenshots, and mockups stay sharp on screen and print cleanly.
- Re-encodes the picture in its original format: PNG stays PNG, JPEG stays JPEG, both saved with Pillow's optimize setting on.
- Leaves everything else alone. Every other part of the
.docxfile (text, styles, headers, footers, comments, tracked changes, relationships) is copied over byte for byte.
Nothing is forced to a fixed percentage. A small image that is already tight stays exactly as it is.
- Vector and legacy images (EMF, WMF, SVG): skipped entirely. Resizing or re-encoding them risks breaking the drawing.
- Text, styles, headers, footers, comments, tracked changes: untouched at the XML level.
- Small images under a size limit (20KB by default): icons and bullets are not worth re-encoding.
- Embedded fonts: rare, but if images alone do not get you under target, check these by hand next.
❯ uv run compress-docx thesis.docx --dpi 200 --jpeg-quality 100
image action before after saved
--------------------------------------------------------------------------
image37.png resized 2.3 MB 1016.1 KB 1.3 MB
image51.png resized 1.9 MB 775.9 KB 1.2 MB
image38.png resized 1.8 MB 742.1 KB 1.1 MB
image35.png resized 1.1 MB 286.6 KB 877.6 KB
image33.png resized 1.1 MB 430.4 KB 711.0 KB
image54.png resized 1.1 MB 498.1 KB 590.0 KB
image53.png resized 1.0 MB 489.1 KB 560.7 KB
image34.png resized 645.1 KB 210.8 KB 434.3 KB
image49.png resized 1.3 MB 932.6 KB 362.7 KB
image31.png resized 598.4 KB 288.9 KB 309.5 KB
image50.png resized 1.1 MB 815.8 KB 305.4 KB
image57.png resized 898.2 KB 649.2 KB 249.0 KB
image55.png resized 898.8 KB 676.4 KB 222.4 KB
image36.png resized 1.1 MB 927.5 KB 215.0 KB
image32.png resized 318.0 KB 170.8 KB 147.1 KB
image42.png resized 297.5 KB 156.2 KB 141.3 KB
image3.jpg recompressed 940.6 KB 819.2 KB 121.4 KB
image30.png resized 388.9 KB 279.1 KB 109.8 KB
image2.jpg recompressed 809.3 KB 714.3 KB 95.1 KB
image45.png resized 169.1 KB 94.0 KB 75.1 KB
image6.png resized 113.7 KB 47.8 KB 65.8 KB
image8.png resized 101.6 KB 48.4 KB 53.2 KB
image40.png resized 357.2 KB 307.0 KB 50.2 KB
image46.png resized 210.2 KB 171.5 KB 38.7 KB
image56.png resized 258.8 KB 224.1 KB 34.7 KB
image48.png resized 122.9 KB 95.7 KB 27.1 KB
image43.png resized 231.9 KB 205.0 KB 26.9 KB
image47.png resized 150.4 KB 126.2 KB 24.2 KB
image39.png resized 179.8 KB 164.3 KB 15.5 KB
image52.png recompressed 62.4 KB 47.6 KB 14.8 KB
image7.png recompressed 109.9 KB 103.3 KB 6.6 KB
image44.png resized 196.1 KB 192.2 KB 3.9 KB
image4.jpeg kept 109.5 KB 109.5 KB 0 B
image5.png skipped 14.1 KB 14.1 KB 0 B
image1.png kept 106.5 KB 106.5 KB 0 B
image9.jpeg skipped 1.3 KB 1.3 KB 0 B
image10.jpeg skipped 1.1 KB 1.1 KB 0 B
image11.png skipped 510 B 510 B 0 B
image12.jpeg skipped 1.0 KB 1.0 KB 0 B
image13.png skipped 558 B 558 B 0 B
image14.png skipped 174 B 174 B 0 B
image15.png skipped 195 B 195 B 0 B
image16.png skipped 706 B 706 B 0 B
image17.png skipped 828 B 828 B 0 B
image18.jpeg skipped 1.5 KB 1.5 KB 0 B
image19.jpeg skipped 2.1 KB 2.1 KB 0 B
image20.jpeg skipped 4.1 KB 4.1 KB 0 B
image21.png skipped 661 B 661 B 0 B
image22.png skipped 136 B 136 B 0 B
image23.png skipped 417 B 417 B 0 B
image24.png skipped 289 B 289 B 0 B
image25.jpeg skipped 1.6 KB 1.6 KB 0 B
image26.png skipped 394 B 394 B 0 B
image27.png skipped 253 B 253 B 0 B
image28.png skipped 627 B 627 B 0 B
image41.png kept 215.4 KB 215.4 KB 0 B
image29.png skipped 1019 B 1019 B 0 B
image3.png skipped 256 B 256 B 0 B
image4.png skipped 818 B 818 B 0 B
--------------------------------------------------------------------------
Images: 22.2 MB -> 12.9 MB
Document: 22.8 MB -> 13.2 MB
Reduction: 41.8%
Written: thesis-compressed.docxNotice what is missing from that table: one fixed percentage. Each image was measured and treated on its own terms. Some were cut by over a megabyte. Some were left alone because they were already small or already tight. That is the whole point.
Requires Python 3.10+.
With uv (recommended):
git clone https://github.com/Laoode/docx-compressor.git
cd docx-compressor
uv venv
uv pip install -e ".[dev]"With plain pip:
git clone https://github.com/Laoode/docx-compressor.git
cd docx-compressor
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"uv run compress-docx document.docx document-compressed.docxIf you omit the output path, it writes <name>-compressed.docx next to the input:
uv run compress-docx document.docxSee a report without writing a file:
uv run compress-docx document.docx --dry-run| Flag | Default | Effect |
|---|---|---|
--dpi |
150 | Effective resolution kept for resized images |
--jpeg-quality |
90 | JPEG re-encode quality (1–100) |
--max-dimension |
2000 | Cap (px, longest edge) for images whose displayed size can't be found in the XML |
--convert-png-to-jpeg |
off | Convert opaque PNGs to JPEG for extra savings |
--dry-run |
off | Print the report only, write nothing |
- Run with the defaults first (
--dpi 150 --jpeg-quality 90). - Check the resulting size. If it's already in your target range, open it and spot-check your densest diagram/screenshot at 100% zoom.
- Still too big? Do not jump straight to aggressive settings. Change one setting at a time:
or
uv run compress-docx document.docx --dpi 130
uv run compress-docx document.docx --jpeg-quality 82
- Only use
--convert-png-to-jpegif you are still over target after that. It turns opaque PNGs (screenshots, diagrams with no transparency) into JPEGs for extra savings, at a small risk of faint marks around sharp text and lines. That is why it stays off by default.
uv pip install -e ".[dev]"
uv run pytestThe test suite builds a synthetic .docx (an oversized diagram PNG, an oversized photo JPEG, a transparent-background PNG, and a tiny icon), runs the compressor on it, and checks that: the output is a valid zip with well-formed XML, every relationship still points at a file that exists, every remaining image still decodes, the transparent PNG keeps its alpha channel and format, and the tiny icon is left untouched.
- Legacy VML-embedded images (rare outside old Word exports) aren't measured for display size; they fall back to the
--max-dimensioncap. - Multi-frame GIFs are re-saved as a single frame if they ever get resized.
Issues and pull requests are welcome. See CONTRIBUTING.md.
MIT. Use it, fork it, ship it in your own tools. No attribution required.
No cloud. No account. No upload. Just Python doing exactly what it's told.
