Comprimir un PDF con Telegram es posible gracias a los Bots. Anteriormente en mi blog te contaba cómo comprimir un PDF con Python usando la línea de comandos, PIL y pypdfium2.
Ahora he conectado la compresión de PDF con un Bot de Telegram para que puedas reducir el tamaño de cualquier PDF usando este Bot de Telegram de manera gratis, además de que el código fuente del Bot es Open source.
Veamos cómo funciona este Bot de Telegram para comprimir un PDF reduciendo su tamaño. Cuando le envíes un documento PDF al Bot de Telegram puedes especificar la escala de extracción de imagen así como la calidad resultante, siendo capaz de comprimir el PDF encontrando un equilibrio entre calidad y tamaño.
Como ya lo expliqué en el post que contiene el código Python, para reducir el tamaño de un PDF con el Bot de Telegram lo que se hace es convertir cada página del documento a una imagen (como si le tomaras capturas de pantalla, pero manteniendo la calidad), comprimir esa imagen y luego unir cada imagen en un PDF ya comprimido.
Estoy explicando el funcionamiento ya que a partir de esto el tamaño del PDF reducido puede variar. No pesa lo mismo un PDF con varias ilustraciones que uno de puro texto, pero según mis pruebas siempre se reduce el tamaño cuando otros compresores no lo han hecho.
He encerrado la reducción del tamaño de PDF en la siguiente función de Python. Más adelante esa función será invocada desde Telegram, o mejor dicho, cuando se reciba una actualización del Bot de Telegram al hacer polling:
def comprimir_pdf(nombre_pdf, escala: int = 2, calidad: int = 70):
nombre_pdf_comprimido = uuid.uuid4().hex + ".pdf"
nombre_pdf_sin_extension = Path(nombre_pdf).stem
"""
Extraer cada página del PDF como imagen
"""
pdf = pdfium.PdfDocument(nombre_pdf)
cantidad_paginas = len(pdf)
imagenes = []
for indice_pagina in range(cantidad_paginas):
numero_pagina = indice_pagina+1
nombre_imagen = f"{nombre_pdf_sin_extension}_{numero_pagina}.jpg"
imagenes.append(nombre_imagen)
pagina = pdf.get_page(indice_pagina)
imagen_para_pil = pagina.render(scale=escala).to_pil()
imagen_para_pil.save(nombre_imagen)
imagenes_comprimidas = []
"""
Comprimir imágenes.
Entre menor calidad, menos peso del PDF resultante
"""
for nombre_imagen in imagenes:
nombre_imagen_sin_extension = Path(nombre_imagen).stem
nombre_imagen_salida = nombre_imagen_sin_extension + \
"_comprimida" + nombre_imagen[nombre_imagen.rfind("."):]
imagen = Image.open(nombre_imagen)
"""
El parámetro quality para JPEG se especifica en:
https://pillow-wiredfool.readthedocs.io/en/latest/handbook/image-file-formats.html#jpeg
"""
imagen.save(nombre_imagen_salida, optimize=True, quality=calidad)
imagenes_comprimidas.append(nombre_imagen_salida)
"""
Escribir imágenes en un nuevo PDF
"""
with open(nombre_pdf_comprimido, "wb") as documento:
documento.write(img2pdf.convert(imagenes_comprimidas))
"""
Eliminar imágenes temporales
"""
for imagen in imagenes + imagenes_comprimidas:
os.remove(imagen)
pdf.close()
return nombre_pdf_comprimido
Esta función recibe el nombre del PDF que se va a comprimir (o mejor dicho, su ubicación), la escala para extraer las imágenes y la calidad a la que se va a reducir cada página. Devuelve la ubicación del PDF ya comprimido.
Ahora solo hay que escuchar las actualizaciones para ver si un usuario ha enviado un mensaje o un documento PDF al Bot:
async def manejadorDeActualizaciones(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if update.message == None:
return
if update.message.text =="/start":
await update.message.reply_text(MENSAJE_AYUDA)
if update.message.document != None:
if update.message.document.mime_type != "application/pdf":
return
escala = ESCALA_POR_DEFECTO
calidad = CALIDAD_POR_DEFECTO
if update.message.caption != None:
escala, calidad = parsear_escala_y_calidad(
update.message.caption)
await update.message.reply_text(f"Extrayendo páginas con escala {escala} y reduciendo calidad al {calidad} %.\n"+MENSAJE_AYUDA+"\nComprimiendo PDF...")
archivo_recibido_desde_telegram = await update.message.effective_attachment.get_file()
nombre_aleatorio_pdf_recibido = uuid.uuid4().hex
ubicacion_archivo_recibido = await archivo_recibido_desde_telegram.download_to_drive(nombre_aleatorio_pdf_recibido + ".pdf")
ubicacion_pdf_comprimido = comprimir_pdf(
ubicacion_archivo_recibido, escala, calidad)
await update.message.chat.send_document(ubicacion_pdf_comprimido, "Aquí tienes tu PDF comprimido según los parámetros indicados")
os.remove(ubicacion_archivo_recibido)
os.remove(ubicacion_pdf_comprimido)
Por ahora solo estoy manejando dos opciones: cuando el usuario envía el comando /start
para mostrarle la ayuda, y cuando el usuario envía un documento.
Cuando el usuario envía un documento al Bot de Telegram compruebo que su tipo sea application/pdf
, reviso si tiene una descripción (ya que se puede especificar la escala y calidad en el caption
del documento), calculo un nombre único y aleatorio con uuid4, después lo descargo de la nube de Telegram con download_to_drive
, invoco a la función que reduce el tamaño del PDF y envío el PDF comprimido con send_document
.
Finalmente elimino el PDF que descargué (el que estaba sin comprimir) así como el PDF ya comprimido que envié a Telegram como respuesta a la solicitud del usuario de reducir el peso de su PDF.
El código fuente completo del Bot así como sus dependencias de Python se encuentran en el siguiente repositorio de GitHub: https://github.com/parzibyte/pdf_compress_telegram_bot
Puedes alojar este Bot en cualquier lugar, incluso en tu equipo local siempre y cuando tengas conexión a internet. Así cualquier usuario será capaz de enviar documentos PDF al Bot y comprimirlos.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.