Herramienta de escritorio para comprimir archivos PDF convirtiendo cada página a imagen JPEG con calidad ajustable. Incluye una versión CLI de consola (compressPdf_cli.py) y una versión GUI completa con Flet (kompressPdf.py).
El archivo original nunca se modifica. El resultado se guarda con el sufijo
_comprimidoen la misma carpeta.
| Archivo | Versión | Descripción |
|---|---|---|
kompressPdf.py |
v2.4 — GUI | Aplicación de escritorio completa con interfaz gráfica, control de rendimiento y monitor en tiempo real |
compressPdf_cli.py |
v1.0 — CLI | Script original de consola: rápido y sin dependencias de UI |
requirements.txt |
— | Dependencias del proyecto |
Interfaz de tres columnas con control total del proceso de compresión.
- Previsualización de la primera página del PDF al seleccionarlo
- Slider de calidad (10–95 %) con etiqueta descriptiva en tiempo real
- Botones de escala de renderizado (0.75× → 2.5×)
- Perfiles de rendimiento: Eco / Equilibrado / Rápido / Máximo
- Control granular de workers, tamaño de lote y límite de RAM
- Monitor de CPU y RAM en tiempo real durante la compresión
- Log de consola integrado en la UI
- Cancelación del proceso en cualquier momento
- Tema oscuro / claro alternables
- SnackBar de notificaciones tras cada operación
El motor de compresión resuelve dos problemas críticos para PDFs grandes (1–3 GB, 1000+ páginas):
1. Thread-safety de pypdfium2
pypdfium2 no es thread-safe: abrir el mismo archivo desde múltiples hilos causa OSError: Failed to load page. La solución es un lock global (_PDF_LOCK) que serializa el acceso al documento PDF. La escritura del JPEG ocurre fuera del lock, permitiendo I/O paralelo real.
Renderizado (serializado): hilo-0 → [_PDF_LOCK] → pil → save .jpg
hilo-1 → [espera] → pil → save .jpg
Compresión JPEG (paralelo): hilo-0 → comprimir img-0
hilo-1 → comprimir img-1 ← al mismo tiempo
2. Race condition de Python 3.14 con ThreadPoolExecutor
ThreadPoolExecutor.shutdown() tiene una race condition con señales del SO en Python 3.14 que genera OSError: Signal 2 ignored due to race condition. Se reemplaza con SafeThreadPool — un pool propio basado en queue.Queue y threading.Thread que no llama a shutdown().
3. Procesamiento por lotes con liberación de memoria
Las páginas se procesan en lotes (batch_size). Los archivos raw se eliminan del disco durante la fase 2, no al final, lo que permite comprimir PDFs mucho más grandes que la RAM disponible.
PDF de entrada
│
▼ Fase 1: Renderizado (pypdfium2)
│ ├── [_PDF_LOCK] render página i → imagen PIL
│ └── guardar como raw_XXXXX.jpg (calidad 95%)
│
▼ Fase 2: Compresión JPEG (Pillow)
│ ├── abrir raw_XXXXX.jpg
│ ├── guardar como cmp_XXXXX.jpg (calidad configurada)
│ └── eliminar raw_XXXXX.jpg (liberar disco)
│
▼ Fase 3: Ensamblado (img2pdf)
│ └── combinar cmp_XXXXX.jpg → PDF de salida
│
▼ Limpieza
└── eliminar carpeta temporal _tmp_*/
# Clonar el repositorio
git clone https://github.com/wilbert-nahuatlato/kompressPdf.git
cd kompressPdf
# Crear entorno virtual (recomendado)
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux / macOS
# Instalar dependencias
pip install -r requirements.txtpython kompressPdf.py- Clic en Seleccionar PDF → elige el archivo
- El panel izquierdo muestra la previsualización de la primera página
- Ajusta calidad y escala en el panel central
- Elige un perfil de rendimiento en el panel derecho según tu equipo
- Clic en Comprimir PDF
- El resultado se guarda como
<nombre>_comprimido.pdfen la misma carpeta
Edita las variables al inicio del archivo:
# compressPdf_cli.py
nombre_pdf = "mi_archivo.pdf"
nombre_pdf_comprimido = "mi_archivo_comprimido.pdf"
escala = 2 # resolución de renderizado
calidad = 70 # calidad JPEG (1–95)Luego ejecuta:
python compressPdf_cli.pySalida esperada:
Extrayendo página 1 de 120
Extrayendo página 2 de 120
...
Comprimiendo mi_archivo_1.jpg...
Comprimiendo mi_archivo_2.jpg...
...
Creando PDF comprimido...
| Parámetro | Descripción | Rango | Perfil Eco | Perfil Máximo |
|---|---|---|---|---|
| Workers | Hilos de procesamiento paralelo | 1 – N° CPUs | 1 | todos los CPUs |
| Tamaño de lote | Páginas procesadas por ciclo | 1 – 50 | 4 | 30 |
| RAM mínima libre | Pausa automática si la RAM cae bajo este umbral | 1 GB – 85% RAM | 55% RAM | 1 GB |
| Calidad | Escala | Uso recomendado |
|---|---|---|
| 40–50% | 1.0× | Máxima compresión — adjuntos de email, archivos para web |
| 70% | 1.5× | Equilibrio calidad/tamaño ← recomendado |
| 85% | 2.0× | Alta fidelidad — documentos técnicos con imágenes |
| 95% | 2.5× | Calidad casi original — poca reducción de tamaño |
Para PDFs de +500 páginas o +500 MB: usa perfil Eco o configura manualmente workers=1, lote=4.
| Librería | Versión | Uso |
|---|---|---|
| Flet | ≥ 0.85 | Interfaz gráfica de escritorio |
| pypdfium2 | latest | Renderizado de páginas PDF con PDFium |
| Pillow | latest | Compresión JPEG de imágenes |
| img2pdf | latest | Ensamblado de imágenes en PDF |
| psutil | latest | Monitor de CPU y RAM |
- El PDF de salida convierte páginas vectoriales a imágenes — el texto no será seleccionable. Si necesitas preservar texto e hipervínculos usa compresión lossless a nivel de stream (por ejemplo, con
pypdf). - La versión CLI (
compressPdf_cli.py) es el punto de partida original del proyecto. No tiene UI ni control de rendimiento, pero es suficiente para compresiones rápidas sin instalar Flet.
Wilbert Miguel Nahuatlato · Ingeniero Mecatrónico
GitHub: @wilbert-nahuatlato
MIT — ver LICENSE.