Cómo reducir el tamaño de un PDF con un script en Python 3

Es muy habitual que, para inscribirte en una guardería, pedir una ayuda pública o entregar documentación en una sede electrónica, tengas que subir escaneos en PDF con un límite de tamaño. Los escaneos del DNI, del libro de familia o de un contrato suelen pesar bastante, sobre todo si se han hecho a alta resolución.

En estos casos lo más práctico es comprimirlos antes de subirlos. Puedes usar la herramienta online reducir PDF o, como explico en este artículo, hacerlo en tu propio ordenador con Python 3 y Ghostscript, sin compartir los ficheros con ninguna web. El código está en el repositorio github.com/al118345/pdfconpython3.

Por qué comprimir los PDF en local

Comprimir en tu equipo tiene sentido cuando el documento contiene datos personales, facturas, contratos o documentos de identidad que no conviene subir a servicios de terceros. Python se encarga de organizar el proceso y Ghostscript reescribe el PDF, así que puedes aplicar la misma configuración a una carpeta entera.

La decisión importante es el perfil de calidad: /screen genera ficheros muy pequeños pero puede degradar las imágenes, mientras que /prepress conserva más calidad y produce documentos más pesados. En los escaneos, guarda siempre el original y compara el resultado antes de borrar nada.

Vídeo explicativo: youtube.com/watch?v=Qc6WG2k5tFk

Compresión de ficheros en Python

Este script contiene los métodos necesarios para comprimir PDF con Python 3:

#!/usr/bin/env python3
"""
Script de Python que utiliza ghoscript para comprimir ficheros PDF.
Niveles de compresión:
    0: defecto
    1: prepress
    2: printer
    3: ebook
    4: screen
Dependencias: Ghostscript.
En MacOSX aplica la siguiente línea `brew install ghostscript`.
"""
import argparse
import subprocess
import os.path
import sys
import shutil

def compress(input_file_path, output_file_path, power=0):
    """Función para comprimir PDF via Ghostscript """
    quality = {
        0: '/default',
        1: '/prepress',
        2: '/printer',
        3: '/ebook',
        4: '/screen'
    }

    # Comprovamos si existe el fichero
    if not os.path.isfile(input_file_path):
        print("Error: invalid path for input PDF file")
        sys.exit(1)

    #Comprobamos si es un pdf
    if input_file_path.split('.')[-1].lower() != 'pdf':
        print("Error: input file is not a PDF")
        sys.exit(1)

    gs = get_ghostscript_path()
    print("Compress PDF...")
    initial_size = os.path.getsize(input_file_path)
    subprocess.call([gs, '-sDEVICE=pdfwrite', '-dCompatibilityLevel=1.4',
                    '-dPDFSETTINGS={}'.format(quality[power]),
                    '-dNOPAUSE', '-dQUIET', '-dBATCH',
                    '-sOutputFile={}'.format(output_file_path),
                     input_file_path]
    )
    final_size = os.path.getsize(output_file_path)
    ratio = 1 - (final_size / initial_size)
    print("Compression by {0:.0%}.".format(ratio))
    print("Final file size is {0:.1f}MB".format(final_size / 1000000))
    print("Done.")


def get_ghostscript_path():
    gs_names = ['gs', 'gswin32', 'gswin64']
    for name in gs_names:
        if shutil.which(name):
            return shutil.which(name)
    raise FileNotFoundError(f'No GhostScript executable was found on path ({"/".join(gs_names)})')

Método compress

La función compress recibe la ruta del fichero original, la ruta donde se guardará el comprimido y el nivel de compresión. Un valor de 4 comprime al máximo y un valor de 1 pierde muy poca calidad.

El método solo llama a Ghostscript con estas opciones:

  • -sDEVICE=pdfwrite: genera un PDF nuevo.
  • -dCompatibilityLevel=1.4: versión de PDF compatible con casi cualquier lector.
  • -dPDFSETTINGS=[0-4]: perfil de calidad (default, prepress, printer, ebook o screen).
  • -dNOPAUSE, -dQUIET y -dBATCH: ejecución sin interacción.

Importante: usa una ruta de salida distinta a la de entrada (por ejemplo x-comprimido.pdf u otra carpeta). No sobrescribas el original; bórralo solo cuando hayas comprobado el resultado.

Método get_ghostscript_path

Devuelve la ruta del ejecutable de Ghostscript instalado en el sistema (gs en Linux y macOS, gswin64c en Windows). Si no lo encuentra, instala Ghostscript desde el gestor de paquetes de tu sistema o desde su web oficial.

Método main

Un ejemplo básico de uso por lotes:

if __name__ == '__main__':
    from os import listdir
    from os.path import isfile, join
    path = 'ruta_a_los ficheros_originales'
    path_compress = 'ruta_a_los_ficheros_comprimidos'


    onlyfiles = [f for f in listdir(path) if isfile(join(path, f))]

    for i in onlyfiles:
        compress(path+i, path_compress+i, power=3)

El programa recorre todos los ficheros de una carpeta y guarda cada PDF comprimido en la carpeta de destino.

Qué perfil elegir

  • /screen (72 ppp): el más ligero; válido para documentos de texto que solo se leerán en pantalla.
  • /ebook (150 ppp): buen equilibrio para escaneos de DNI, nóminas o certificados.
  • /printer (300 ppp): cuando el documento se va a imprimir.
  • /prepress: máxima calidad; apenas reduce si el PDF ya estaba optimizado.

Si el resultado sigue siendo demasiado grande, el problema suele ser la resolución del escaneo: vuelve a escanear a 150-200 ppp en escala de grises en lugar de color a 600 ppp.

Lista de comprobación

  • Guarda los ficheros comprimidos en otra carpeta.
  • Compara el tamaño original y el final antes de sustituir nada.
  • Abre un resultado de muestra para comprobar que el texto se lee y las imágenes se ven bien.
  • Usa el modo por lotes solo después de probar un documento con el perfil elegido.

Cuándo es especialmente útil

Este flujo encaja con tareas administrativas repetitivas: reducir una carpeta de formularios escaneados, preparar documentos para un portal público o normalizar adjuntos antes de enviarlos por correo. Si el PDF lo genera tu propia aplicación, es mejor ajustar la resolución de las imágenes al exportarlo; si el PDF ya existe, Ghostscript es el paso práctico de posprocesado.

Relacionado: comprimir PDF desde Angular, convertir PDF a Word por correo y proteger una API de Angular con Nginx y FastAPI.