Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

docx-compressor

docx-compressor cover

🗜️ Your document is 40MB because of screenshots, not sentences. Fix that for free, on your own machine.


The problem with "compress my Word file"

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

How it works

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:

  1. Measures the largest size each picture is shown at, anywhere in the document.
  2. 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.
  3. Re-encodes the picture in its original format: PNG stays PNG, JPEG stays JPEG, both saved with Pillow's optimize setting on.
  4. Leaves everything else alone. Every other part of the .docx file (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.

What it never touches

  • 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.

See it work

❯ 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.docx

Notice 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.

Install

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]"

Usage

uv run compress-docx document.docx document-compressed.docx

If you omit the output path, it writes <name>-compressed.docx next to the input:

uv run compress-docx document.docx

See a report without writing a file:

uv run compress-docx document.docx --dry-run

Flags

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

Recommended workflow

  1. Run with the defaults first (--dpi 150 --jpeg-quality 90).
  2. Check the resulting size. If it's already in your target range, open it and spot-check your densest diagram/screenshot at 100% zoom.
  3. Still too big? Do not jump straight to aggressive settings. Change one setting at a time:
    uv run compress-docx document.docx --dpi 130
    or
    uv run compress-docx document.docx --jpeg-quality 82
  4. Only use --convert-png-to-jpeg if 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.

Development

uv pip install -e ".[dev]"
uv run pytest

The 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.

Known limitations

  • Legacy VML-embedded images (rare outside old Word exports) aren't measured for display size; they fall back to the --max-dimension cap.
  • Multi-frame GIFs are re-saved as a single frame if they ever get resized.

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md.

License

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.

About

Your private DOCX compression tool

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages