I have a PDF document which I want to reduce in size. I've already tried PyPDF2 File Size reductions:

from PyPDF2 import PdfReader, PdfWriter

reader = PdfReader("example.pdf")
writer = PdfWriter()

for page in reader.pages:
    page.compress_content_streams()  # This is CPU intensive!

with open("out.pdf", "wb") as f:

and I used

mutool clean -czggggfDi input.pdf output.pdf

but that didn't really change the size.

Using mutool extract input.pdf I can see that the 7.3 MB of the PDF are likely dominated by 4 images (img-0317.png: 6.4 MB and img-0201.jpg: 3.5 MB and img-0095.png: 1.3 MB and img-0133.png: 1.1 MB). Just storing those PNG images as JPG with gimp reduced the size from 6.4 MB to 1.4 MB.

How can I exchange the embedded images without changing anything else?

1 Answer 1


You can try Ghostscript (see here and, for optimizations, this German article) – or, for even more compression, pdfsizeopt (see here). The latter takes a lot longer, but gets better results on scanned text pages.


Should be provided by your package management. On Debian/Ubuntu/Mint etc, you can install it using sudo apt install ghostscript. You then can use a shell script wrapper like the one I compiled going by the above sources:

# Compress/Optimize PDF files
# see: https://askubuntu.com/a/256449/68291
# and: https://www.linux-community.de/ausgaben/linuxuser/2019/04/optimal-kombiniert/

[[ -z "$1" ]] && {
  echo "Compress/Optimize PDF files"
  echo "Syntax:"
  echo "  ${0} <InFile> [OutFile] [level]"
  echo ""
  echo "Levels:"
  echo "- screen:    72dpi (low res)"
  echo "- ebook:    150dpi (OK; default if not specified otherwise)"
  echo "- prepress: 300dpi (Acrobat Distiller 'Prepress Optimized')"
  echo "- printer:  300dpi (Acrobat Distiller 'Print Optimized')"
  echo "- default:  ???dpi (auto-select, usually results in bigger file)"
  exit 1

[[ ! -f "$1" ]] && {
  echo "Input File '${1}' not found, nothing to do."
  exit 5

[[ -z "$2" ]] && outfile=${1%\.pdf}_comp.pdf

[[ -f "$outfile" ]] && {
  echo "The specified output file '$outfile' does already exist, aborting."
  exit 3

case "$3" in
  screen)   level="-dPDFSETTINGS=/screen" ;;      #  72dpi
  prepress) level="-dPDFSETTINGS=/prepress" ;;    # 300dpi
  printer)  level="-dPDFSETTINGS=/printer" ;;     # 300dpi
  default)  level="-dPDFSETTINGS=/default" ;;     # auto-estimate
  *)        level="-dPDFSETTINGS=/ebook -dColorImageResolution=150 -dGrayImageResolution=150 -dMonoImageResolution=150 -dMonoImageDownsampleType=/Subsample" ;;       # 150dpi

gs -sDEVICE=pdfwrite \
-dColorImageDownsampleType=/Bicubic \
-dGrayImageDownsampleType=/Bicubic \
-dMonoImageDownsampleType=/Bicubic \
-dCompatibilityLevel=1.4 $level -dNOPAUSE -dQUIET -dBATCH -sOutputFile=$outfile $1

Called without parameters it tells you its syntax. With just the input PDF as parameter, it compresses images to 150dpi (ebook), writing output to a file having _comp (aka "compressed") appended to its basename.


This requires quite some binaries downloaded from different places (mostly Github). To make it easier, here's a script performing the "installation" steps – just run it in an empty directory:

wget https://github.com/pts/tif22pnm/releases/download/2014-01-09/png22pnm.xstatic
wget https://github.com/pts/sam2p/releases/download/v0.49.4/sam2p.xstatic
wget http://www.jonof.id.au/files/kenutils/pngout-20200115-linux.tar.gz
wget https://github.com/pts/pdfsizeopt-jbig2/releases/download/2017-01-24/jbig2.xstatic
wget https://raw.githubusercontent.com/pts/pdfsizeopt/master/pdfsizeopt.single

mv png22pnm.xstatic png22pnm
mv sam2p.xstatic sam2p
mv jbig2.xstatic jbig2
tar xf pngout-20200115-linux.tar.gz --strip-components=2 pngout-20200115-linux/amd64/pngout
chmod u+x pdfsizeopt.single png22pnm sam2p

Then you can use the following wrapper script to compress your PDF files (works very good on scanned text pages; not sure if it's really suited for graphical stuff):

# see: https://askubuntu.com/a/1011292
# takes quite long but gets good results on scanned text pages, even leaving OCR intact

[[ -z "$1" ]] && {
  echo "Compress PDF consisting of scanned text pages."
  echo "Usage: $0 <infile>"
  echo "Run 'pdfsizeopt.single --help' for more details."

./pdfsizeopt.single --v=30 --do-fast-bilevel-images=yes $1 ${1%\.pdf}_opt.pdf

(if you want to call it from anywhere, adjust the path of ./pdfsizeopt.single to point to where the tools have been placed, and make an alias to the script)

I decided to include the --do-fast-bilevel-images=yes parameter as it speeds up processing a lot (on a test PDF: about 2min without the parameter, less than a second with the parameter), plus --v=30 to reduce output to a usable level. There are many more parameters you can play with to optimize results to your liking.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.