Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import platform | |
| import locale | |
| import logging | |
| import tempfile | |
| import shutil | |
| import torch | |
| from transformers import MarianMTModel, MarianTokenizer | |
| from langdetect import detect | |
| import fitz # PyMuPDF | |
| from reportlab.pdfgen import canvas | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.pdfbase import pdfmetrics | |
| from reportlab.pdfbase.ttfonts import TTFont | |
| import gradio as gr | |
| import numpy as np | |
| # Configuraci贸n del logger | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Definici贸n inicial de los modelos de traducci贸n | |
| MODELOS_TRADUCCION = { | |
| 'Ingl茅s a Espa帽ol': 'Helsinki-NLP/opus-mt-en-es', | |
| 'Espa帽ol a Ingl茅s': 'Helsinki-NLP/opus-mt-es-en', | |
| 'Ingl茅s a Franc茅s': 'Helsinki-NLP/opus-mt-en-fr', | |
| 'Franc茅s a Ingl茅s': 'Helsinki-NLP/opus-mt-fr-en', | |
| 'Ingl茅s a Alem谩n': 'Helsinki-NLP/opus-mt-en-de', | |
| 'Alem谩n a Ingl茅s': 'Helsinki-NLP/opus-mt-de-en', | |
| 'Ingl茅s a Italiano': 'Helsinki-NLP/opus-mt-en-it', | |
| 'Italiano a Ingl茅s': 'Helsinki-NLP/opus-mt-it-en', | |
| 'Ingl茅s a Portugu茅s': 'Helsinki-NLP/opus-mt-en-pt', | |
| 'Portugu茅s a Ingl茅s': 'Helsinki-NLP/opus-mt-pt-en', | |
| } | |
| # Mapeo de nombres completos de idiomas a c贸digos de idioma | |
| LANGUAGE_MAP = { | |
| 'english': 'en', | |
| 'spanish': 'es', | |
| 'french': 'fr', | |
| 'german': 'de', | |
| 'italian': 'it', | |
| 'portuguese': 'pt', | |
| # Agrega m谩s idiomas seg煤n sea necesario | |
| } | |
| def detectar_idioma_sistema(): | |
| """ | |
| Detecta el idioma del sistema operativo utilizando locale. | |
| Retorna el c贸digo del idioma (e.g., 'en', 'es'). | |
| """ | |
| try: | |
| # Establecer la configuraci贸n regional para evitar DeprecationWarning | |
| locale.setlocale(locale.LC_ALL, '') | |
| idioma, _ = locale.getlocale() | |
| if idioma: | |
| idioma = idioma.split('_')[0] | |
| idioma_lower = idioma.lower() | |
| idioma_code = LANGUAGE_MAP.get(idioma_lower, 'es') # Predeterminado a 'es' si no se encuentra | |
| else: | |
| idioma_code = 'es' # Predeterminado a espa帽ol si no se detecta | |
| logger.info(f"Idioma del sistema detectado: {idioma_code}") | |
| return idioma_code | |
| except Exception as e: | |
| logger.warning(f"No se pudo detectar el idioma del sistema: {e}") | |
| return 'es' # Predeterminado a espa帽ol en caso de error | |
| def detectar_idioma_texto(texto): | |
| """ | |
| Detecta el idioma predominante del texto utilizando langdetect. | |
| Retorna el c贸digo del idioma (e.g., 'en', 'es'). | |
| """ | |
| try: | |
| idioma = detect(texto) | |
| logger.info(f"Idioma detectado del texto: {idioma}") | |
| return idioma | |
| except Exception as e: | |
| logger.error(f"Error al detectar el idioma: {e}") | |
| return 'en' # Predeterminado a ingl茅s si falla la detecci贸n | |
| def actualizar_modelos_traduccion(idioma_origen, idioma_destino): | |
| """ | |
| Actualiza din谩micamente los modelos de traducci贸n disponibles basado en el par de idiomas. | |
| Retorna una tupla (clave, modelo_nombre) si existe el modelo, de lo contrario (None, None). | |
| """ | |
| mapa_idiomas = { | |
| 'en': 'Ingl茅s', | |
| 'es': 'Espa帽ol', | |
| 'fr': 'Franc茅s', | |
| 'de': 'Alem谩n', | |
| 'it': 'Italiano', | |
| 'pt': 'Portugu茅s', | |
| # Agrega m谩s idiomas seg煤n sea necesario | |
| } | |
| clave_origen = mapa_idiomas.get(idioma_origen, idioma_origen.capitalize()) | |
| clave_destino = mapa_idiomas.get(idioma_destino, idioma_destino.capitalize()) | |
| clave = f"{clave_origen} a {clave_destino}" | |
| modelo = MODELOS_TRADUCCION.get(clave) | |
| if modelo: | |
| logger.info(f"Modelo de traducci贸n encontrado para {clave}: {modelo}") | |
| return clave, modelo | |
| else: | |
| logger.warning(f"No se encontr贸 modelo de traducci贸n para {clave}") | |
| return None, None | |
| def cargar_modelo_traduccion(origen, destino): | |
| """ | |
| Carga el modelo de traducci贸n basado en los idiomas de origen y destino. | |
| Retorna una tupla (tokenizer, model, dispositivo). | |
| """ | |
| clave, modelo_nombre = actualizar_modelos_traduccion(origen, destino) | |
| if not modelo_nombre: | |
| raise ValueError(f"No hay modelo de traducci贸n disponible para {origen} a {destino}") | |
| logger.info(f"Cargando el modelo de traducci贸n: {clave}...") | |
| tokenizer = MarianTokenizer.from_pretrained(modelo_nombre) | |
| model = MarianMTModel.from_pretrained(modelo_nombre) | |
| dispositivo = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| model.to(dispositivo) | |
| logger.info(f"Modelo '{clave}' cargado en: {dispositivo}\n") | |
| return tokenizer, model, dispositivo | |
| def traducir_texto(tokenizer, model, textos, dispositivo, batch_size=8): | |
| """ | |
| Traduce una lista de textos utilizando el modelo y tokenizer proporcionados. | |
| """ | |
| if not textos: # Add a check for empty text list | |
| logger.warning("Lista de textos vac铆a. Saltando traducci贸n.") | |
| return [] | |
| traducciones = [] | |
| try: | |
| for i in range(0, len(textos), batch_size): | |
| batch = textos[i:i+batch_size] | |
| # Skip empty texts within the batch | |
| batch = [texto for texto in batch if texto and texto.strip()] | |
| if not batch: | |
| continue | |
| inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True) | |
| inputs = {k: v.to(dispositivo) for k, v in inputs.items()} # Mover inputs al dispositivo | |
| with torch.no_grad(): | |
| traduccion = model.generate(**inputs) | |
| traducciones += [tokenizer.decode(t, skip_special_tokens=True) for t in traduccion] | |
| except Exception as e: | |
| logger.error(f"Error en traducci贸n de texto: {e}") | |
| # Fallback: devolver textos originales si la traducci贸n falla | |
| traducciones = textos | |
| return traducciones | |
| def obtener_rutas_fuentes(): | |
| """ | |
| Obtiene las rutas de las fuentes del sistema operativo. | |
| """ | |
| sistema = platform.system() | |
| rutas_fuentes = [] | |
| if sistema == 'Windows': | |
| rutas_fuentes = [ | |
| os.path.join(os.environ.get('WINDIR', 'C:\\Windows'), 'Fonts'), | |
| os.path.expanduser('~\\AppData\\Local\\Microsoft\\Windows\\Fonts'), | |
| os.path.join(os.path.expanduser('~'), 'AppData', 'Local', 'Microsoft', 'Windows', 'Fonts') | |
| ] | |
| elif sistema == 'Darwin': # macOS | |
| rutas_fuentes = [ | |
| '/System/Library/Fonts', | |
| '/Library/Fonts', | |
| os.path.expanduser('~/Library/Fonts') | |
| ] | |
| elif sistema == 'Linux': | |
| rutas_fuentes = [ | |
| '/usr/share/fonts', | |
| '/usr/local/share/fonts', | |
| os.path.expanduser('~/.fonts') | |
| ] | |
| else: | |
| logger.warning(f"Sistema operativo no soportado: {sistema}") | |
| return rutas_fuentes | |
| def cachear_fuentes(): | |
| """ | |
| Cachea las fuentes disponibles en el sistema en un archivo JSON. | |
| """ | |
| rutas_fuentes = obtener_rutas_fuentes() | |
| fuentes = {} | |
| for ruta in rutas_fuentes: | |
| if os.path.exists(ruta): | |
| for root, dirs, files in os.walk(ruta): | |
| for file in files: | |
| if file.lower().endswith(('.ttf', '.otf')): | |
| nombre_fuente = os.path.splitext(file)[0] | |
| path_fuente = os.path.join(root, file) | |
| # Evitar sobrescribir fuentes con el mismo nombre | |
| if nombre_fuente not in fuentes: | |
| fuentes[nombre_fuente] = path_fuente | |
| cache_path = os.path.join(tempfile.gettempdir(), 'fuentes_sistema.json') | |
| with open(cache_path, 'w', encoding='utf-8') as f: | |
| json.dump(fuentes, f, ensure_ascii=False, indent=4) | |
| logger.info(f"Fuentes cacheadas en: {cache_path}") | |
| return fuentes | |
| def cargar_fuentes_cache(): | |
| """ | |
| Carga las fuentes desde el cach茅 o crea una nueva cach茅 si no existe. | |
| """ | |
| cache_path = os.path.join(tempfile.gettempdir(), 'fuentes_sistema.json') | |
| if not os.path.exists(cache_path): | |
| logger.info("Cache de fuentes no encontrado. Creando cache...") | |
| return cachear_fuentes() | |
| with open(cache_path, 'r', encoding='utf-8') as f: | |
| fuentes = json.load(f) | |
| logger.info("Fuentes cargadas desde el cache.") | |
| return fuentes | |
| def registrar_fuentes(fuentes_sistema): | |
| """ | |
| Registra las fuentes disponibles en ReportLab. | |
| Solo registra fuentes .ttf compatibles. | |
| """ | |
| fuentes_registradas = set(pdfmetrics.getRegisteredFontNames()) | |
| for nombre, path in fuentes_sistema.items(): | |
| # Verificar si el archivo es .ttf | |
| if not path.lower().endswith('.ttf'): | |
| logger.warning(f"Fuente {nombre} no es .ttf. Se omite su registro.") | |
| continue | |
| # Crear un nombre 煤nico para la fuente | |
| nombre_registro = nombre | |
| if nombre_registro not in fuentes_registradas: | |
| try: | |
| pdfmetrics.registerFont(TTFont(nombre_registro, path)) | |
| fuentes_registradas.add(nombre_registro) | |
| logger.info(f"Fuente registrada: {nombre_registro}") | |
| except Exception as e: | |
| logger.warning(f"No se pudo registrar la fuente {nombre}: {e}") | |
| def buscar_fuente_similar(nombre_fuente_pdf, fuentes_sistema): | |
| """ | |
| Busca una fuente similar en las fuentes del sistema. | |
| Si no encuentra una, retorna 'Helvetica'. | |
| """ | |
| nombre_fuente_pdf_lower = nombre_fuente_pdf.lower() | |
| for nombre, path in fuentes_sistema.items(): | |
| if nombre_fuente_pdf_lower in nombre.lower(): | |
| return nombre # Retorna el nombre registrado en ReportLab | |
| logger.warning(f"No se encontr贸 una fuente similar para '{nombre_fuente_pdf}'. Usando 'Helvetica'.") | |
| return "Helvetica" | |
| def ajustar_tamano_fuente(texto, bbox, c, max_width, tama帽o_fuente_original): | |
| """ | |
| Ajusta el tama帽o de la fuente para que el texto se ajuste al ancho m谩ximo. | |
| """ | |
| # Prevent division by zero | |
| if not texto or max_width <= 0 or tama帽o_fuente_original <= 0: | |
| return tama帽o_fuente_original | |
| try: | |
| width_texto = c.stringWidth(texto, c._fontname, tama帽o_fuente_original) | |
| if width_texto > max_width: | |
| nuevo_tama帽o = tama帽o_fuente_original * (max_width / width_texto) | |
| return max(min(nuevo_tama帽o, tama帽o_fuente_original), 6) | |
| return tama帽o_fuente_original | |
| except Exception as e: | |
| logger.warning(f"Error al ajustar tama帽o de fuente: {e}") | |
| return tama帽o_fuente_original | |
| def extraer_y_traducir_pdf_generador(archivo_pdf, tokenizer, model, dispositivo, idioma_destino): | |
| """ | |
| Extrae el contenido del PDF, traduce el texto y crea un nuevo PDF traducido. | |
| Esta versi贸n utiliza 'yield' para emitir actualizaciones. | |
| """ | |
| try: | |
| documento = fitz.open(archivo_pdf.name) | |
| pdf_traducido_path = os.path.splitext(archivo_pdf.name)[0] + f"_traducido_{idioma_destino}.pdf" | |
| fuentes_sistema = cargar_fuentes_cache() | |
| registrar_fuentes(fuentes_sistema) | |
| # Crear un canvas ReportLab con el tama帽o de la primera p谩gina | |
| primera_pagina = documento.load_page(0) | |
| rect = primera_pagina.rect | |
| ancho, alto = rect.width, rect.height | |
| c = canvas.Canvas(pdf_traducido_path, pagesize=(ancho, alto)) | |
| textos = [] | |
| posiciones = [] | |
| # Extraer todos los textos y sus posiciones | |
| for numero_pagina in range(len(documento)): | |
| pagina = documento.load_page(numero_pagina) | |
| bloques = pagina.get_text("dict")["blocks"] | |
| for bloque in bloques: | |
| if bloque['type'] == 0: # texto | |
| for linea in bloque["lines"]: | |
| for span in linea["spans"]: | |
| # Filtrar textos no vac铆os y que no sean solo espacios o caracteres especiales | |
| texto_limpio = span["text"].strip() | |
| if texto_limpio and len(texto_limpio) > 1: | |
| textos.append(texto_limpio) | |
| posiciones.append((span["bbox"], span["font"], span["size"], numero_pagina)) | |
| # Verificar si hay textos para traducir | |
| if not textos: | |
| logger.warning("No se encontraron textos v谩lidos para traducir en el PDF.") | |
| documento.close() | |
| yield ("No se encontraron textos para traducir.", None) | |
| return pdf_traducido_path | |
| # Traducir texto | |
| try: | |
| traducciones = traducir_texto(tokenizer, model, textos, dispositivo) | |
| except Exception as e: | |
| logger.error(f"Error en traducci贸n: {e}") | |
| traducciones = textos # Fallback a textos originales | |
| # Asegurar que el n煤mero de traducciones coincida con el n煤mero de textos originales | |
| if len(traducciones) != len(textos): | |
| logger.warning(f"Discrepancia en traducciones. Textos: {len(textos)}, Traducciones: {len(traducciones)}") | |
| # Rellenar con textos originales si es necesario | |
| traducciones = traducciones + textos[len(traducciones):] | |
| # Dibujar el texto traducido | |
| idx_texto = 0 | |
| total_paginas = len(documento) | |
| for numero_pagina in range(total_paginas): | |
| pagina = documento.load_page(numero_pagina) | |
| rect = pagina.rect | |
| ancho_pagina, alto_pagina = rect.width, rect.height | |
| # Ajustar el tama帽o de p谩gina al tama帽o original del PDF | |
| c.setPageSize((ancho_pagina, alto_pagina)) | |
| # Definir m谩rgenes din谩micos en base al tama帽o de la p谩gina, ej: 5% de ancho y alto | |
| margen_x = ancho_pagina * 0.05 | |
| margen_y = alto_pagina * 0.05 | |
| # Procesar texto de esta p谩gina | |
| pagina_bloques = pagina.get_text("dict")["blocks"] | |
| for bloque in pagina_bloques: | |
| if bloque['type'] == 0: | |
| for linea in bloque["lines"]: | |
| for span in linea["spans"]: | |
| texto_original = span["text"].strip() | |
| # Saltar textos vac铆os o muy cortos | |
| if not texto_original or len(texto_original) <= 1: | |
| continue | |
| # Obtener el texto traducido correspondiente | |
| if idx_texto < len(traducciones): | |
| texto_traducido = traducciones[idx_texto] | |
| bbox, font, size, span_pagina = posiciones[idx_texto] | |
| idx_texto += 1 | |
| else: | |
| # Fallback si se agotan las traducciones | |
| texto_traducido = texto_original | |
| bbox, font, size, span_pagina = posiciones[idx_texto - 1] | |
| x0, y0, x1, y1 = bbox | |
| # Ajustar coordenadas al sistema de ReportLab (y invertida) | |
| x = x0 | |
| y = alto_pagina - y1 | |
| # Buscar fuente similar | |
| fuente_encontrada = buscar_fuente_similar(font, fuentes_sistema) | |
| # Intentar establecer la fuente encontrada | |
| try: | |
| c.setFont(fuente_encontrada, size) | |
| except: | |
| logger.warning(f"Fuente '{fuente_encontrada}' no registrada. Usando 'Helvetica'.") | |
| fuente_encontrada = "Helvetica" | |
| c.setFont(fuente_encontrada, size) | |
| # Ajustar el tama帽o del texto si excede el ancho disponible | |
| max_width = (x1 - x0) - margen_x if (x1 - x0) > 0 else (ancho_pagina - 2 * margen_x) | |
| nuevo_tama帽o = ajustar_tamano_fuente(texto_traducido, bbox, c, max_width, size) | |
| # Establecer el nuevo tama帽o de fuente | |
| try: | |
| c.setFont(fuente_encontrada, nuevo_tama帽o) | |
| except: | |
| logger.warning(f"No se pudo ajustar el tama帽o de la fuente para '{fuente_encontrada}'. Usando 'Helvetica'.") | |
| fuente_encontrada = "Helvetica" | |
| c.setFont(fuente_encontrada, nuevo_tama帽o) | |
| # Dibujar texto | |
| try: | |
| c.drawString(x, y, texto_traducido) | |
| except Exception as e: | |
| logger.error(f"Error al dibujar texto: {e}") | |
| # Intentar con Helvetica por defecto | |
| c.setFont("Helvetica", nuevo_tama帽o) | |
| c.drawString(x, y, texto_traducido) | |
| # Procesar im谩genes | |
| imagenes = [b for b in pagina_bloques if b['type'] == 1] | |
| for imagen in imagenes: | |
| if 'xref' not in imagen: | |
| continue | |
| try: | |
| x0, y0, x1, y1 = imagen["bbox"] | |
| ancho_img, alto_img = x1 - x0, y1 - y0 | |
| img = fitz.Pixmap(documento, imagen["xref"]) | |
| if img.n > 4: | |
| img = fitz.Pixmap(fitz.csRGB, img) | |
| imagen_path = os.path.join(tempfile.gettempdir(), f"imagen_{numero_pagina}.png") | |
| img.save(imagen_path) | |
| img.close() | |
| c.drawImage(imagen_path, x0, alto_pagina - y1 - alto_img, width=ancho_img, height=alto_img) | |
| except Exception as e: | |
| logger.error(f"Error al procesar imagen: {e}") | |
| continue | |
| # Emitir estado y vista previa de la p谩gina actual | |
| yield (f"Traduciendo p谩gina {numero_pagina + 1} de {total_paginas} al idioma {idioma_destino}...", pdf_preview(archivo_pdf.name, numero_pagina)) | |
| c.showPage() | |
| c.save() | |
| documento.close() | |
| yield ("Traducci贸n completada exitosamente.", pdf_preview(pdf_traducido_path, pagina=0)) | |
| return pdf_traducido_path | |
| except Exception as e: | |
| logger.error(f"Error al extraer y traducir pdf: {e}") | |
| def pdf_preview(file_path, pagina=0): | |
| """ | |
| Previsualiza una p谩gina espec铆fica del PDF como una imagen. | |
| """ | |
| try: | |
| doc = fitz.open(file_path) | |
| if pagina >= len(doc): | |
| pagina = 0 # Fallback a la primera p谩gina si el n煤mero es inv谩lido | |
| page = doc[pagina] | |
| pix = page.get_pixmap() | |
| image = np.frombuffer(pix.samples, np.uint8).reshape(pix.height, pix.width, pix.n) | |
| if pix.n == 4: | |
| image = image[:, :, :3] | |
| return image | |
| except Exception as e: | |
| logger.error(f"Error al previsualizar el PDF: {e}") | |
| return None | |
| def boton_actualizar_fuentes(files): | |
| """ | |
| Actualiza las fuentes del sistema subiendo nuevas fuentes. | |
| """ | |
| try: | |
| if files: | |
| fuentes_cache = cargar_fuentes_cache() | |
| # Definir el subdirectorio para fuentes subidas | |
| subdir_fuentes = os.path.join(tempfile.gettempdir(), 'fuentes_subidas') | |
| os.makedirs(subdir_fuentes, exist_ok=True) | |
| for file in files: | |
| if file.name.lower().endswith('.ttf'): | |
| # Obtener solo el nombre del archivo sin el path completo | |
| nombre_archivo = os.path.basename(file.name) | |
| destino = os.path.join(subdir_fuentes, nombre_archivo) | |
| # Verificar si el archivo ya existe para evitar sobrescritura | |
| if os.path.exists(destino): | |
| # Puedes optar por sobrescribir, renombrar o saltar | |
| # Aqu铆 optamos por renombrar a帽adiendo un sufijo num茅rico | |
| base, ext = os.path.splitext(nombre_archivo) | |
| contador = 1 | |
| while os.path.exists(os.path.join(subdir_fuentes, f"{base}_{contador}{ext}")): | |
| contador += 1 | |
| destino = os.path.join(subdir_fuentes, f"{base}_{contador}{ext}") | |
| # Copiar el archivo al subdirectorio | |
| shutil.copyfile(file.name, destino) | |
| nombre_fuente = os.path.splitext(nombre_archivo)[0] | |
| fuentes_cache[nombre_fuente] = destino | |
| logger.info(f"Fuente '{nombre_archivo}' subida y guardada en {destino}") | |
| else: | |
| logger.warning(f"Archivo '{file.name}' no es una fuente .ttf y ser谩 omitido.") | |
| # Actualizar el cach茅 | |
| cache_path = os.path.join(tempfile.gettempdir(), 'fuentes_sistema.json') | |
| with open(cache_path, 'w', encoding='utf-8') as f: | |
| json.dump(fuentes_cache, f, ensure_ascii=False, indent=4) | |
| # Volver a registrar fuentes | |
| registrar_fuentes(fuentes_cache) | |
| else: | |
| cachear_fuentes() | |
| return "Fuentes actualizadas exitosamente." | |
| except Exception as e: | |
| logger.error(f"Error al actualizar fuentes: {e}") | |
| return f"Error al actualizar fuentes: {e}" | |
| def procesar_pdf(archivo_pdf, fuentes_subidas): | |
| """ | |
| Funci贸n generadora para procesar y traducir el PDF. | |
| Emite actualizaciones de estado y vista previa durante el proceso. | |
| """ | |
| try: | |
| if not archivo_pdf: | |
| yield ("No se ha subido ning煤n archivo PDF.", None, None) | |
| return | |
| # Emitir estado inicial | |
| yield ("Iniciando traducci贸n...", pdf_preview(archivo_pdf.name, pagina=0), None) | |
| # Extraer texto para detectar el idioma | |
| documento = fitz.open(archivo_pdf.name) | |
| texto_completo = "" | |
| for pagina in documento: | |
| texto_completo += pagina.get_text() | |
| idioma_origen = detectar_idioma_texto(texto_completo) | |
| idioma_sistema = detectar_idioma_sistema() | |
| # Si el idioma de origen y destino son iguales, no realizar traducci贸n | |
| if idioma_origen == idioma_sistema: | |
| logger.info("El idioma de origen y destino son iguales. No se realizar谩 la traducci贸n.") | |
| yield ("El idioma de origen y destino son iguales. No se realiz贸 la traducci贸n.", pdf_preview(archivo_pdf.name, pagina=0), None) | |
| return | |
| # Cargar el modelo de traducci贸n autom谩ticamente | |
| tokenizer, model, dispositivo = cargar_modelo_traduccion(idioma_origen, idioma_sistema) | |
| # Realizar la traducci贸n y emitir actualizaciones | |
| generator = extraer_y_traducir_pdf_generador( | |
| archivo_pdf, tokenizer, model, dispositivo, idioma_sistema | |
| ) | |
| for update in generator: | |
| status, imagen = update | |
| yield (status, imagen, None) | |
| # Emitir estado final | |
| pdf_traducido_path = os.path.splitext(archivo_pdf.name)[0] + f"_traducido_{idioma_sistema}.pdf" | |
| yield ("Traducci贸n completada exitosamente.", pdf_preview(pdf_traducido_path, pagina=0), pdf_traducido_path) | |
| except Exception as e: | |
| logger.error(f"Error en procesar_pdf: {e}") | |
| yield (f"Error en la traducci贸n: {e}", None, None) | |
| # Interfaz de usuario con Gradio | |
| with gr.Blocks( | |
| title="Traductor de PDF Multilenguaje", | |
| theme=gr.themes.Default( | |
| primary_hue="blue", spacing_size="md", radius_size="lg" | |
| ) | |
| ) as iface: | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("# Traductor de PDF Multilenguaje") | |
| pdf_input = gr.File(label="Sube tu PDF", file_types=['.pdf']) | |
| # Eliminamos el Dropdown de selecci贸n manual del modelo de traducci贸n | |
| fuentes_subidas = gr.File(label="Sube fuentes faltantes (opcional)", file_count="multiple", file_types=['.ttf']) | |
| actualizar_fuentes_btn = gr.Button("Actualizar Fuentes del Sistema") | |
| actualizar_fuentes_output = gr.Textbox(label="Actualizaci贸n de Fuentes", interactive=False) | |
| actualizar_fuentes_btn.click( | |
| fn=boton_actualizar_fuentes, | |
| inputs=fuentes_subidas, | |
| outputs=actualizar_fuentes_output | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("## Vista Previa") | |
| preview = gr.Image(label="Vista Previa", visible=True) | |
| status_md = gr.Markdown("**Estado:** Esperando traducci贸n...") | |
| traducir_btn = gr.Button("Traducir PDF") | |
| traducir_output = gr.File(label="Descargar PDF traducido", visible=True) | |
| # Configurar el bot贸n para usar la funci贸n generadora | |
| traducir_btn.click( | |
| fn=procesar_pdf, | |
| inputs=[pdf_input, fuentes_subidas], | |
| outputs=[status_md, preview, traducir_output], | |
| show_progress=True # Opcional: muestra una barra de progreso | |
| ) | |
| # Vista previa del PDF | |
| def actualizar_preview(file): | |
| return pdf_preview(file.name, pagina=0) if file else None | |
| pdf_input.change( | |
| fn=actualizar_preview, | |
| inputs=pdf_input, | |
| outputs=preview | |
| ) | |
| # Ejecutar la interfaz de usuario con la opci贸n de compartir p煤blicamente | |
| iface.launch(share=True) |