JersonRuizAlva commited on
Commit
14d7429
1 Parent(s): 1bcef05
Files changed (3) hide show
  1. app.py +513 -0
  2. install_fonts.sh +29 -0
  3. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,513 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import platform
4
+ import locale
5
+ import logging
6
+ import tempfile
7
+
8
+ import torch
9
+ from transformers import MarianMTModel, MarianTokenizer
10
+ from langdetect import detect
11
+
12
+ import fitz # PyMuPDF
13
+ from reportlab.pdfgen import canvas
14
+ from reportlab.lib.pagesizes import A4
15
+ from reportlab.pdfbase import pdfmetrics
16
+ from reportlab.pdfbase.ttfonts import TTFont
17
+
18
+ import gradio as gr
19
+ import numpy as np
20
+
21
+ # Configuraci贸n del logger
22
+ logging.basicConfig(level=logging.INFO)
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # Definici贸n inicial de los modelos de traducci贸n
26
+ MODELOS_TRADUCCION = {
27
+ 'Ingl茅s a Espa帽ol': 'Helsinki-NLP/opus-mt-en-es',
28
+ 'Espa帽ol a Ingl茅s': 'Helsinki-NLP/opus-mt-es-en',
29
+ 'Ingl茅s a Franc茅s': 'Helsinki-NLP/opus-mt-en-fr',
30
+ 'Franc茅s a Ingl茅s': 'Helsinki-NLP/opus-mt-fr-en',
31
+ 'Ingl茅s a Alem谩n': 'Helsinki-NLP/opus-mt-en-de',
32
+ 'Alem谩n a Ingl茅s': 'Helsinki-NLP/opus-mt-de-en',
33
+ 'Ingl茅s a Italiano': 'Helsinki-NLP/opus-mt-en-it',
34
+ 'Italiano a Ingl茅s': 'Helsinki-NLP/opus-mt-it-en',
35
+ 'Ingl茅s a Portugu茅s': 'Helsinki-NLP/opus-mt-en-pt',
36
+ 'Portugu茅s a Ingl茅s': 'Helsinki-NLP/opus-mt-pt-en',
37
+ }
38
+
39
+ # Mapeo de nombres completos de idiomas a c贸digos de idioma
40
+ LANGUAGE_MAP = {
41
+ 'english': 'en',
42
+ 'spanish': 'es',
43
+ 'french': 'fr',
44
+ 'german': 'de',
45
+ 'italian': 'it',
46
+ 'portuguese': 'pt',
47
+ # Agrega m谩s idiomas seg煤n sea necesario
48
+ }
49
+
50
+ def detectar_idioma_sistema():
51
+ """
52
+ Detecta el idioma del sistema operativo utilizando locale.
53
+ Retorna el c贸digo del idioma (e.g., 'en', 'es').
54
+ """
55
+ try:
56
+ # Establecer la configuraci贸n regional para evitar DeprecationWarning
57
+ locale.setlocale(locale.LC_ALL, '')
58
+ idioma, _ = locale.getlocale()
59
+ if idioma:
60
+ idioma = idioma.split('_')[0]
61
+ idioma_lower = idioma.lower()
62
+ idioma_code = LANGUAGE_MAP.get(idioma_lower, 'es') # Predeterminado a 'es' si no se encuentra
63
+ else:
64
+ idioma_code = 'es' # Predeterminado a espa帽ol si no se detecta
65
+ logger.info(f"Idioma del sistema detectado: {idioma_code}")
66
+ return idioma_code
67
+ except Exception as e:
68
+ logger.warning(f"No se pudo detectar el idioma del sistema: {e}")
69
+ return 'es' # Predeterminado a espa帽ol en caso de error
70
+
71
+ def detectar_idioma_texto(texto):
72
+ """
73
+ Detecta el idioma predominante del texto utilizando langdetect.
74
+ Retorna el c贸digo del idioma (e.g., 'en', 'es').
75
+ """
76
+ try:
77
+ idioma = detect(texto)
78
+ logger.info(f"Idioma detectado del texto: {idioma}")
79
+ return idioma
80
+ except Exception as e:
81
+ logger.error(f"Error al detectar el idioma: {e}")
82
+ return 'en' # Predeterminado a ingl茅s si falla la detecci贸n
83
+
84
+ def actualizar_modelos_traduccion(idioma_origen, idioma_destino):
85
+ """
86
+ Actualiza din谩micamente los modelos de traducci贸n disponibles basado en el par de idiomas.
87
+ Retorna una tupla (clave, modelo_nombre) si existe el modelo, de lo contrario (None, None).
88
+ """
89
+ mapa_idiomas = {
90
+ 'en': 'Ingl茅s',
91
+ 'es': 'Espa帽ol',
92
+ 'fr': 'Franc茅s',
93
+ 'de': 'Alem谩n',
94
+ 'it': 'Italiano',
95
+ 'pt': 'Portugu茅s',
96
+ # Agrega m谩s idiomas seg煤n sea necesario
97
+ }
98
+
99
+ clave_origen = mapa_idiomas.get(idioma_origen, idioma_origen.capitalize())
100
+ clave_destino = mapa_idiomas.get(idioma_destino, idioma_destino.capitalize())
101
+ clave = f"{clave_origen} a {clave_destino}"
102
+ modelo = MODELOS_TRADUCCION.get(clave)
103
+
104
+ if modelo:
105
+ logger.info(f"Modelo de traducci贸n encontrado para {clave}: {modelo}")
106
+ return clave, modelo
107
+ else:
108
+ logger.warning(f"No se encontr贸 modelo de traducci贸n para {clave}")
109
+ return None, None
110
+
111
+ def cargar_modelo_traduccion(origen, destino):
112
+ """
113
+ Carga el modelo de traducci贸n basado en los idiomas de origen y destino.
114
+ Retorna una tupla (tokenizer, model, dispositivo).
115
+ """
116
+ clave, modelo_nombre = actualizar_modelos_traduccion(origen, destino)
117
+ if not modelo_nombre:
118
+ raise ValueError(f"No hay modelo de traducci贸n disponible para {origen} a {destino}")
119
+
120
+ logger.info(f"Cargando el modelo de traducci贸n: {clave}...")
121
+ tokenizer = MarianTokenizer.from_pretrained(modelo_nombre)
122
+ model = MarianMTModel.from_pretrained(modelo_nombre)
123
+
124
+ dispositivo = torch.device("cuda" if torch.cuda.is_available() else "cpu")
125
+ model.to(dispositivo)
126
+ logger.info(f"Modelo '{clave}' cargado en: {dispositivo}\n")
127
+ return tokenizer, model, dispositivo
128
+
129
+ def traducir_texto(tokenizer, model, textos, dispositivo, batch_size=8):
130
+ """
131
+ Traduce una lista de textos utilizando el modelo y tokenizer proporcionados.
132
+ """
133
+ traducciones = []
134
+ for i in range(0, len(textos), batch_size):
135
+ batch = textos[i:i+batch_size]
136
+ inputs = tokenizer(batch, return_tensors="pt", padding=True, truncation=True)
137
+ inputs = {k: v.to(dispositivo) for k, v in inputs.items()} # Mover inputs al dispositivo
138
+ with torch.no_grad():
139
+ traduccion = model.generate(**inputs)
140
+ traducciones += [tokenizer.decode(t, skip_special_tokens=True) for t in traduccion]
141
+ return traducciones
142
+
143
+ def obtener_rutas_fuentes():
144
+ """
145
+ Obtiene las rutas de las fuentes del sistema operativo.
146
+ """
147
+ sistema = platform.system()
148
+ rutas_fuentes = []
149
+
150
+ if sistema == 'Windows':
151
+ rutas_fuentes = [
152
+ os.path.join(os.environ.get('WINDIR', 'C:\\Windows'), 'Fonts'),
153
+ os.path.expanduser('~\\AppData\\Local\\Microsoft\\Windows\\Fonts'),
154
+ os.path.join(os.path.expanduser('~'), 'AppData', 'Local', 'Microsoft', 'Windows', 'Fonts')
155
+ ]
156
+ elif sistema == 'Darwin': # macOS
157
+ rutas_fuentes = [
158
+ '/System/Library/Fonts',
159
+ '/Library/Fonts',
160
+ os.path.expanduser('~/Library/Fonts')
161
+ ]
162
+ elif sistema == 'Linux':
163
+ rutas_fuentes = [
164
+ '/usr/share/fonts',
165
+ '/usr/local/share/fonts',
166
+ os.path.expanduser('~/.fonts')
167
+ ]
168
+ else:
169
+ logger.warning(f"Sistema operativo no soportado: {sistema}")
170
+
171
+ return rutas_fuentes
172
+
173
+ def cachear_fuentes():
174
+ """
175
+ Cachea las fuentes disponibles en el sistema en un archivo JSON.
176
+ """
177
+ rutas_fuentes = obtener_rutas_fuentes()
178
+ fuentes = {}
179
+
180
+ for ruta in rutas_fuentes:
181
+ if os.path.exists(ruta):
182
+ for root, dirs, files in os.walk(ruta):
183
+ for file in files:
184
+ if file.lower().endswith(('.ttf', '.otf')):
185
+ nombre_fuente = os.path.splitext(file)[0]
186
+ path_fuente = os.path.join(root, file)
187
+ # Evitar sobrescribir fuentes con el mismo nombre
188
+ if nombre_fuente not in fuentes:
189
+ fuentes[nombre_fuente] = path_fuente
190
+
191
+ cache_path = os.path.join(tempfile.gettempdir(), 'fuentes_sistema.json')
192
+ with open(cache_path, 'w', encoding='utf-8') as f:
193
+ json.dump(fuentes, f, ensure_ascii=False, indent=4)
194
+
195
+ logger.info(f"Fuentes cacheadas en: {cache_path}")
196
+ return fuentes
197
+
198
+ def cargar_fuentes_cache():
199
+ """
200
+ Carga las fuentes desde el cach茅 o crea una nueva cach茅 si no existe.
201
+ """
202
+ cache_path = os.path.join(tempfile.gettempdir(), 'fuentes_sistema.json')
203
+ if not os.path.exists(cache_path):
204
+ logger.info("Cache de fuentes no encontrado. Creando cache...")
205
+ return cachear_fuentes()
206
+
207
+ with open(cache_path, 'r', encoding='utf-8') as f:
208
+ fuentes = json.load(f)
209
+ logger.info("Fuentes cargadas desde el cache.")
210
+ return fuentes
211
+
212
+ def registrar_fuentes(fuentes_sistema):
213
+ """
214
+ Registra las fuentes disponibles en ReportLab.
215
+ Solo registra fuentes .ttf compatibles.
216
+ """
217
+ fuentes_registradas = set(pdfmetrics.getRegisteredFontNames())
218
+
219
+ for nombre, path in fuentes_sistema.items():
220
+ # Verificar si el archivo es .ttf
221
+ if not path.lower().endswith('.ttf'):
222
+ logger.warning(f"Fuente {nombre} no es .ttf. Se omite su registro.")
223
+ continue
224
+ # Crear un nombre 煤nico para la fuente
225
+ nombre_registro = nombre
226
+ if nombre_registro not in fuentes_registradas:
227
+ try:
228
+ pdfmetrics.registerFont(TTFont(nombre_registro, path))
229
+ fuentes_registradas.add(nombre_registro)
230
+ logger.info(f"Fuente registrada: {nombre_registro}")
231
+ except Exception as e:
232
+ logger.warning(f"No se pudo registrar la fuente {nombre}: {e}")
233
+
234
+ def buscar_fuente_similar(nombre_fuente_pdf, fuentes_sistema):
235
+ """
236
+ Busca una fuente similar en las fuentes del sistema.
237
+ Si no encuentra una, retorna 'Helvetica'.
238
+ """
239
+ nombre_fuente_pdf_lower = nombre_fuente_pdf.lower()
240
+ for nombre, path in fuentes_sistema.items():
241
+ if nombre_fuente_pdf_lower in nombre.lower():
242
+ return nombre # Retorna el nombre registrado en ReportLab
243
+ logger.warning(f"No se encontr贸 una fuente similar para '{nombre_fuente_pdf}'. Usando 'Helvetica'.")
244
+ return "Helvetica"
245
+
246
+ def ajustar_tamano_fuente(texto, bbox, c, max_width, tama帽o_fuente_original):
247
+ """
248
+ Ajusta el tama帽o de la fuente para que el texto se ajuste al ancho m谩ximo.
249
+ """
250
+ width_texto = c.stringWidth(texto, c._fontname, tama帽o_fuente_original)
251
+ if width_texto > max_width:
252
+ nuevo_tama帽o = tama帽o_fuente_original * (max_width / width_texto)
253
+ return max(min(nuevo_tama帽o, tama帽o_fuente_original), 6)
254
+ return tama帽o_fuente_original
255
+
256
+ def extraer_y_traducir_pdf(archivo_pdf, tokenizer, model, dispositivo, idioma_destino):
257
+ """
258
+ Extrae el contenido del PDF, traduce el texto y crea un nuevo PDF traducido.
259
+ """
260
+ documento = fitz.open(archivo_pdf.name)
261
+ pdf_traducido_path = os.path.splitext(archivo_pdf.name)[0] + f"_traducido_{idioma_destino}.pdf"
262
+
263
+ fuentes_sistema = cargar_fuentes_cache()
264
+ registrar_fuentes(fuentes_sistema)
265
+
266
+ # Crear un canvas ReportLab con el tama帽o de la primera p谩gina
267
+ primera_pagina = documento.load_page(0)
268
+ rect = primera_pagina.rect
269
+ ancho, alto = rect.width, rect.height
270
+ c = canvas.Canvas(pdf_traducido_path, pagesize=(ancho, alto))
271
+
272
+ textos = []
273
+ posiciones = []
274
+
275
+ # Extraer todos los textos y sus posiciones
276
+ for numero_pagina in range(len(documento)):
277
+ pagina = documento.load_page(numero_pagina)
278
+ bloques = pagina.get_text("dict")["blocks"]
279
+ for bloque in bloques:
280
+ if bloque['type'] == 0: # texto
281
+ for linea in bloque["lines"]:
282
+ for span in linea["spans"]:
283
+ textos.append(span["text"])
284
+ posiciones.append((span["bbox"], span["font"], span["size"], numero_pagina))
285
+
286
+ # Traducir texto
287
+ traducciones = traducir_texto(tokenizer, model, textos, dispositivo)
288
+
289
+ # Dibujar el texto traducido
290
+ idx_texto = 0
291
+ total_paginas = len(documento)
292
+ for numero_pagina in range(total_paginas):
293
+ pagina = documento.load_page(numero_pagina)
294
+ rect = pagina.rect
295
+ ancho, alto = rect.width, rect.height
296
+
297
+ # Ajustar el tama帽o de p谩gina al tama帽o original del PDF
298
+ c.setPageSize((ancho, alto))
299
+
300
+ # Definir m谩rgenes din谩micos en base al tama帽o de la p谩gina, ej: 5% de ancho y alto
301
+ margen_x = ancho * 0.05
302
+ margen_y = alto * 0.05
303
+
304
+ # Procesar texto de esta p谩gina
305
+ pagina_bloques = pagina.get_text("dict")["blocks"]
306
+
307
+ for bloque in pagina_bloques:
308
+ if bloque['type'] == 0:
309
+ for linea in bloque["lines"]:
310
+ for span in linea["spans"]:
311
+ # Obtener el texto traducido correspondiente
312
+ texto_traducido = traducciones[idx_texto]
313
+ bbox, font, size, span_pagina = posiciones[idx_texto]
314
+ idx_texto += 1
315
+
316
+ x0, y0, x1, y1 = bbox
317
+
318
+ # Ajustar coordenadas al sistema de ReportLab (y invertida)
319
+ x = x0
320
+ y = alto - y1
321
+
322
+ # Buscar fuente similar
323
+ fuente_encontrada = buscar_fuente_similar(font, fuentes_sistema)
324
+
325
+ # Intentar establecer la fuente encontrada
326
+ try:
327
+ c.setFont(fuente_encontrada, size)
328
+ except:
329
+ logger.warning(f"Fuente '{fuente_encontrada}' no registrada. Usando 'Helvetica'.")
330
+ fuente_encontrada = "Helvetica"
331
+ c.setFont(fuente_encontrada, size)
332
+
333
+ # Ajustar el tama帽o del texto si excede el ancho disponible
334
+ max_width = (x1 - x0) - margen_x if (x1 - x0) > 0 else (ancho - 2 * margen_x)
335
+ nuevo_tama帽o = ajustar_tamano_fuente(texto_traducido, bbox, c, max_width, size)
336
+
337
+ # Establecer el nuevo tama帽o de fuente
338
+ try:
339
+ c.setFont(fuente_encontrada, nuevo_tama帽o)
340
+ except:
341
+ logger.warning(f"No se pudo ajustar el tama帽o de la fuente para '{fuente_encontrada}'. Usando 'Helvetica'.")
342
+ fuente_encontrada = "Helvetica"
343
+ c.setFont(fuente_encontrada, nuevo_tama帽o)
344
+
345
+ # Dibujar texto
346
+ try:
347
+ c.drawString(x, y, texto_traducido)
348
+ except Exception as e:
349
+ logger.error(f"Error al dibujar texto: {e}")
350
+ # Intentar con Helvetica por defecto
351
+ c.setFont("Helvetica", nuevo_tama帽o)
352
+ c.drawString(x, y, texto_traducido)
353
+
354
+ # Procesar im谩genes
355
+ imagenes = [b for b in pagina_bloques if b['type'] == 1]
356
+ for imagen in imagenes:
357
+ if 'xref' not in imagen:
358
+ continue
359
+ try:
360
+ x0, y0, x1, y1 = imagen["bbox"]
361
+ ancho_img, alto_img = x1 - x0, y1 - y0
362
+ img = fitz.Pixmap(documento, imagen["xref"])
363
+
364
+ if img.n > 4:
365
+ img = fitz.Pixmap(fitz.csRGB, img)
366
+
367
+ imagen_path = os.path.join(tempfile.gettempdir(), f"imagen_{numero_pagina}.png")
368
+ img.save(imagen_path)
369
+ img.close()
370
+ c.drawImage(imagen_path, x0, alto - y1 - alto_img, width=ancho_img, height=alto_img)
371
+
372
+ except Exception as e:
373
+ logger.error(f"Error al procesar imagen: {e}")
374
+ continue
375
+
376
+ c.showPage()
377
+
378
+ c.save()
379
+ return pdf_traducido_path
380
+
381
+ def pdf_preview(file):
382
+ """
383
+ Previsualiza la primera p谩gina del PDF como una imagen.
384
+ """
385
+ try:
386
+ doc = fitz.open(file.name)
387
+ page = doc[0]
388
+ pix = page.get_pixmap()
389
+ image = np.frombuffer(pix.samples, np.uint8).reshape(pix.height, pix.width, pix.n)
390
+ if pix.n == 4:
391
+ image = image[:, :, :3]
392
+ return image
393
+ except Exception as e:
394
+ logger.error(f"Error al previsualizar el PDF: {e}")
395
+ return None
396
+
397
+ def boton_actualizar_fuentes(files):
398
+ """
399
+ Actualiza las fuentes del sistema subiendo nuevas fuentes.
400
+ """
401
+ try:
402
+ if files:
403
+ fuentes_cache = cargar_fuentes_cache()
404
+ for file in files:
405
+ if file.name.lower().endswith('.ttf'):
406
+ destino = os.path.join(tempfile.gettempdir(), file.name)
407
+ with open(destino, 'wb') as f_dest:
408
+ f_dest.write(file.read())
409
+ nombre_fuente = os.path.splitext(file.name)[0]
410
+ fuentes_cache[nombre_fuente] = destino
411
+ logger.info(f"Fuente '{file.name}' subida y guardada en {destino}")
412
+ else:
413
+ logger.warning(f"Archivo '{file.name}' no es una fuente .ttf y ser谩 omitido.")
414
+ # Actualizar el cach茅
415
+ cache_path = os.path.join(tempfile.gettempdir(), 'fuentes_sistema.json')
416
+ with open(cache_path, 'w', encoding='utf-8') as f:
417
+ json.dump(fuentes_cache, f, ensure_ascii=False, indent=4)
418
+
419
+ # Volver a registrar fuentes
420
+ registrar_fuentes(fuentes_cache)
421
+ else:
422
+ cachear_fuentes()
423
+ return "Fuentes actualizadas exitosamente."
424
+ except Exception as e:
425
+ logger.error(f"Error al actualizar fuentes: {e}")
426
+ return f"Error al actualizar fuentes: {e}"
427
+
428
+ def procesar_pdf(archivo_pdf, fuentes_subidas):
429
+ """
430
+ Funci贸n principal para procesar y traducir el PDF.
431
+ """
432
+ try:
433
+ if not archivo_pdf:
434
+ return None, "No se ha subido ning煤n archivo PDF."
435
+
436
+ # Extraer texto para detectar el idioma
437
+ documento = fitz.open(archivo_pdf.name)
438
+ texto_completo = ""
439
+ for pagina in documento:
440
+ texto_completo += pagina.get_text()
441
+
442
+ idioma_origen = detectar_idioma_texto(texto_completo)
443
+ idioma_sistema = detectar_idioma_sistema()
444
+
445
+ # Si el idioma de origen y destino son iguales, no realizar traducci贸n
446
+ if idioma_origen == idioma_sistema:
447
+ logger.info("El idioma de origen y destino son iguales. No se realizar谩 la traducci贸n.")
448
+ return archivo_pdf.name, "El idioma de origen y destino son iguales. No se realiz贸 la traducci贸n."
449
+
450
+ # Cargar el modelo de traducci贸n autom谩ticamente
451
+ tokenizer, model, dispositivo = cargar_modelo_traduccion(idioma_origen, idioma_sistema)
452
+
453
+ # Traducir el PDF
454
+ pdf_traducido_path = extraer_y_traducir_pdf(archivo_pdf, tokenizer, model, dispositivo, idioma_sistema)
455
+
456
+ return pdf_traducido_path, "Traducci贸n completada exitosamente."
457
+ except Exception as e:
458
+ logger.error(f"Error en procesar_pdf: {e}")
459
+ return None, f"Error en la traducci贸n: {e}"
460
+
461
+ def actualizar_fuentes_cache():
462
+ """
463
+ Funci贸n para actualizar el cach茅 de fuentes.
464
+ """
465
+ try:
466
+ cachear_fuentes()
467
+ return "Fuentes cacheadas exitosamente."
468
+ except Exception as e:
469
+ logger.error(f"Error al cachear fuentes: {e}")
470
+ return f"Error al cachear fuentes: {e}"
471
+
472
+ # Interfaz de usuario con Gradio
473
+ with gr.Blocks(
474
+ title="Traductor de PDF Multilenguaje",
475
+ theme=gr.themes.Default(
476
+ primary_hue="blue", spacing_size="md", radius_size="lg"
477
+ )
478
+ ) as iface:
479
+
480
+ with gr.Row():
481
+ with gr.Column(scale=1):
482
+ gr.Markdown("# Traductor de PDF Multilenguaje")
483
+ pdf_input = gr.File(label="Sube tu PDF", file_types=['.pdf'])
484
+ # Eliminamos el Dropdown de selecci贸n manual del modelo de traducci贸n
485
+ fuentes_subidas = gr.File(label="Sube fuentes faltantes (opcional)", file_count="multiple", file_types=['.ttf'])
486
+ actualizar_fuentes_btn = gr.Button("Actualizar Fuentes del Sistema")
487
+ actualizar_fuentes_output = gr.Textbox(label="Actualizaci贸n de Fuentes", interactive=False)
488
+ actualizar_fuentes_btn.click(
489
+ fn=boton_actualizar_fuentes,
490
+ inputs=fuentes_subidas,
491
+ outputs=actualizar_fuentes_output
492
+ )
493
+
494
+ with gr.Column(scale=1):
495
+ gr.Markdown("## Vista Previa")
496
+ preview = gr.Image(label="Vista Previa", visible=True)
497
+ traducir_btn = gr.Button("Traducir PDF")
498
+ estado_traduccion = gr.Textbox(label="Estado", interactive=False)
499
+ traducir_btn.click(
500
+ fn=procesar_pdf,
501
+ inputs=[pdf_input, fuentes_subidas],
502
+ outputs=[gr.File(label="Descargar PDF traducido"), estado_traduccion]
503
+ )
504
+
505
+ # Vista previa del PDF
506
+ pdf_input.change(
507
+ fn=pdf_preview,
508
+ inputs=pdf_input,
509
+ outputs=preview
510
+ )
511
+
512
+ # Ejecutar la interfaz de usuario con la opci贸n de compartir p煤blicamente
513
+ iface.launch(share=True)
install_fonts.sh ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Create fonts directory if it doesn't exist
4
+ mkdir -p ~/.fonts
5
+
6
+ # Download and install some common Unicode fonts
7
+ # You can customize this list based on your specific language requirements
8
+
9
+ # Noto Fonts (Google's comprehensive Unicode font family)
10
+ wget https://noto-website-2.storage.googleapis.com/pkgs/Noto_Sans.zip
11
+ unzip Noto_Sans.zip -d ~/.fonts
12
+ rm Noto_Sans.zip
13
+
14
+ # DejaVu Fonts (Comprehensive Unicode font)
15
+ wget https://github.com/dejavu-fonts/dejavu-fonts/releases/download/v2.37/dejavu-fonts-ttf-2.37.zip
16
+ unzip dejavu-fonts-ttf-2.37.zip
17
+ cp dejavu-fonts-ttf-2.37/ttf/*.ttf ~/.fonts/
18
+ rm -rf dejavu-fonts-ttf-2.37*
19
+
20
+ # Roboto Fonts (Google's standard font)
21
+ wget https://github.com/google/fonts/raw/main/apache/roboto/static/Roboto-Regular.ttf -O ~/.fonts/Roboto-Regular.ttf
22
+ wget https://github.com/google/fonts/raw/main/apache/roboto/static/Roboto-Bold.ttf -O ~/.fonts/Roboto-Bold.ttf
23
+
24
+ # Additional language-specific fonts (optional)
25
+ # Spanish
26
+ wget https://github.com/google/fonts/raw/main/ofl/sourcesanspro/SourceSansPro-Regular.ttf -O ~/.fonts/SourceSansPro-Regular.ttf
27
+
28
+ # Rebuild font cache
29
+ fc-cache -fv
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ torch
2
+ Transformers
3
+ langdetect
4
+ PyMuPDF
5
+ reportlab
6
+ gradio
7
+ numpy