DaniFera commited on
Commit
3f3cb94
·
verified ·
1 Parent(s): 6c7cf36

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +181 -128
app.py CHANGED
@@ -1,38 +1,49 @@
1
- # Versión 1.8: UI Plana y Reordenada con Compresión
 
 
 
2
  import gradio as gr
3
  import pandas as pd
4
  import config
5
  from core import PDFEngine
6
 
 
7
  engine = PDFEngine()
8
 
9
- # --- FUNCIONES DE INTERFAZ (Wrappers) ---
 
10
 
11
- # 1. Unir
12
  def update_file_list(files):
 
13
  if not files: return pd.DataFrame(), ""
 
14
  data = [[i, f.split("/")[-1]] for i, f in enumerate(files)]
 
15
  default_order = ",".join([str(i) for i in range(len(files))])
16
  return pd.DataFrame(data, columns=["ID", "Archivo"]), default_order
17
 
18
  def process_merge(files, order_str):
19
  if not files: return None
20
  try:
 
21
  indices = [int(x.strip()) for x in order_str.split(",") if x.strip().isdigit()]
22
  return engine.merge_pdfs(files, indices)
23
- except Exception as e: raise gr.Error(str(e))
24
 
25
- # 2. Dividir
26
  def load_pdf_info(file):
 
27
  if not file: return None, 0, gr.update(visible=False)
28
  info = engine.get_pdf_info(file)
29
- return f"Documento: {info['name']} ({info['pages']} págs).", info['pages'], gr.update(visible=True)
30
 
31
  def update_split_preview(file, range_str, total_pages):
 
32
  if not file or not range_str: return None
33
  key_pages = engine.get_preview_indices_from_string(range_str, total_pages)
34
  if not key_pages: return None
 
35
  gallery = []
 
36
  for p in key_pages[:8]:
37
  path = engine.generate_preview(file, p)
38
  if path: gallery.append((path, f"Pág {p}"))
@@ -43,10 +54,19 @@ def process_split(file, range_str):
43
  try: return engine.split_pdf_custom(file, range_str)
44
  except Exception as e: raise gr.Error(str(e))
45
 
46
- # 3. Comprimir (NUEVO)
47
- def interface_compress(file, level_label):
 
 
 
 
 
 
 
 
 
48
  if not file: return None
49
- # Mapear label a int
50
  levels = {
51
  "Baja (Máxima calidad)": 1,
52
  "Media (Recomendado - eBook)": 3,
@@ -56,19 +76,19 @@ def interface_compress(file, level_label):
56
  try: return engine.compress_pdf(file, power)
57
  except Exception as e: raise gr.Error(str(e))
58
 
59
- # 4. Word
60
- def interface_word(file):
61
  if not file: return None
62
  try: return engine.pdf_to_word(file)
63
  except Exception as e: raise gr.Error(str(e))
64
 
65
- # 5. Rotar
66
  def update_rotate_preview(file, angle_label):
67
  if not file: return None
 
68
  angle = 0
69
  if "90" in angle_label: angle = 90
70
  elif "180" in angle_label: angle = 180
71
  elif "270" in angle_label: angle = 270
 
72
  return engine.get_rotated_preview(file, angle)
73
 
74
  def process_rotate(file, angle_label):
@@ -77,83 +97,118 @@ def process_rotate(file, angle_label):
77
  if "90" in angle_label: angle = 90
78
  elif "180" in angle_label: angle = 180
79
  elif "270" in angle_label: angle = 270
80
- if angle == 0: return file.name
 
81
  try: return engine.rotate_pdf(file, angle)
82
  except Exception as e: raise gr.Error(str(e))
83
 
84
- # 6. Imágenes
85
- def interface_p2i(file):
86
- if not file: return None
87
- try: return engine.pdf_to_images_zip(file)
88
  except Exception as e: raise gr.Error(str(e))
89
 
90
- def interface_i2p(files):
91
- if not files: return None
92
- try: return engine.images_to_pdf(files)
93
  except Exception as e: raise gr.Error(str(e))
94
 
95
- # 7. Extras
96
- def interface_meta(file, t, a, s):
97
  if not file: return None
98
  try: return engine.update_metadata(file, t, a, s)
99
  except Exception as e: raise gr.Error(str(e))
100
 
101
- def interface_text(file):
102
  if not file: return None
103
- try: return engine.extract_text(file)
104
  except Exception as e: raise gr.Error(str(e))
105
 
106
- # 8. Proteger
107
- def interface_protect(file, pwd):
108
- if not file or not pwd: return None
109
- try: return engine.protect_pdf(file, pwd)
110
  except Exception as e: raise gr.Error(str(e))
111
 
112
 
113
- # --- CONSTRUCCIÓN DE LA UI ---
 
114
  with gr.Blocks(title=config.APP_TITLE, theme=gr.themes.Soft()) as demo:
115
  gr.Markdown(f"# {config.APP_TITLE}")
116
  gr.Markdown(config.APP_DESCRIPTION)
117
 
118
- # Pestañas aplanadas y ordenadas por uso
119
  with gr.Tabs():
120
 
121
- # 1. UNIR
122
- with gr.TabItem("Unir PDF"):
 
 
123
  gr.Markdown(config.TXT_MERGE)
124
  with gr.Row():
125
  with gr.Column(scale=1):
126
- m_files = gr.File(file_count="multiple", label="Archivos", file_types=[".pdf"])
127
  with gr.Column(scale=2):
128
  m_table = gr.Dataframe(headers=["ID", "Archivo"], interactive=False)
129
- m_order = gr.Textbox(label="Orden", placeholder="Ej: 0, 2, 1")
130
  m_btn = gr.Button("Unir Archivos", variant="primary")
131
- m_out = gr.File(label="Resultado")
 
 
132
  m_files.change(update_file_list, m_files, [m_table, m_order])
133
  m_btn.click(process_merge, [m_files, m_order], m_out)
134
 
135
- # 2. DIVIDIR
136
- with gr.TabItem("Dividir PDF"):
137
- gr.Markdown(config.TXT_SPLIT)
 
138
  with gr.Row():
139
- with gr.Column():
140
- s_file = gr.File(label="PDF", file_types=[".pdf"])
141
- s_info = gr.Markdown("")
142
- s_pages = gr.State(0)
143
- s_range = gr.Textbox(label="Rangos", placeholder="Ej: 1-3, 5")
144
- with gr.Row():
145
- s_prev_btn = gr.Button("Ver Hitos")
146
- s_btn = gr.Button("Dividir (ZIP)", variant="primary")
147
- with gr.Column():
148
- s_gal = gr.Gallery(label="Vista Previa", height=300, object_fit="contain", columns=3)
149
- s_out = gr.File(label="ZIP Descargable", file_types=[".zip"])
150
- s_file.change(load_pdf_info, s_file, [s_info, s_pages, s_out])
151
- s_prev_btn.click(update_split_preview, [s_file, s_range, s_pages], s_gal)
152
- s_btn.click(process_split, [s_file, s_range], s_out)
153
-
154
- # 3. COMPRIMIR (NUEVO)
155
- with gr.TabItem("Comprimir PDF"):
156
- gr.Markdown("Reduce el tamaño de tu archivo PDF optimizando imágenes y recursos.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  with gr.Row():
158
  with gr.Column():
159
  c_file = gr.File(label="PDF Original", file_types=[".pdf"])
@@ -162,81 +217,79 @@ with gr.Blocks(title=config.APP_TITLE, theme=gr.themes.Soft()) as demo:
162
  label="Nivel de Compresión",
163
  value="Media (Recomendado - eBook)"
164
  )
165
- c_btn = gr.Button("Comprimir Ahora", variant="primary")
166
  with gr.Column():
167
  c_out = gr.File(label="PDF Comprimido")
168
- c_btn.click(interface_compress, [c_file, c_level], c_out)
 
169
 
170
- # 4. PDF A WORD
171
- with gr.TabItem("PDF a Word"):
172
- gr.Markdown("Convierte documentos a formato DOCX editable (Beta).")
173
- with gr.Row():
174
- w_file = gr.File(label="PDF", file_types=[".pdf"])
175
- w_btn = gr.Button("Convertir a Word", variant="primary")
176
- w_out = gr.File(label="Archivo Word")
177
- w_btn.click(interface_word, w_file, w_out)
178
-
179
- # 5. ROTAR
180
- with gr.TabItem("Rotar PDF"):
181
- gr.Markdown("Rota el documento permanentemente.")
182
- with gr.Row():
183
- with gr.Column():
184
- r_file = gr.File(label="PDF", file_types=[".pdf"])
185
- r_angle = gr.Radio(["0° (Original)", "90° (Derecha)", "180° (Invertir)", "270° (Izquierda)"], label="Rotación", value="0° (Original)")
186
- r_btn = gr.Button("Aplicar Rotación", variant="primary")
187
- with gr.Column():
188
- r_prev = gr.Image(label="Preview", height=350, type="filepath")
189
- r_out = gr.File(label="PDF Rotado")
190
- r_file.change(update_rotate_preview, [r_file, r_angle], r_prev)
191
- r_angle.change(update_rotate_preview, [r_file, r_angle], r_prev)
192
- r_btn.click(process_rotate, [r_file, r_angle], r_out)
193
-
194
- # 6. IMAGENES
195
- with gr.TabItem("Imágenes"):
196
- gr.Markdown("Extraer imágenes de PDF o crear PDF desde imágenes.")
197
- with gr.Row():
198
- with gr.Column():
199
- gr.Markdown("### PDF -> JPG")
200
- p2i_f = gr.File(label="PDF")
201
- p2i_b = gr.Button("Convertir (ZIP)")
202
- p2i_o = gr.File(label="ZIP")
203
- p2i_b.click(interface_p2i, p2i_f, p2i_o)
204
- with gr.Column():
205
- gr.Markdown("### JPG -> PDF")
206
- i2p_f = gr.File(label="Imágenes", file_count="multiple")
207
- i2p_b = gr.Button("Unir en PDF")
208
- i2p_o = gr.File(label="PDF")
209
- i2p_b.click(interface_i2p, i2p_f, i2p_o)
210
-
211
- # 7. TEXTO Y METADATOS
212
- with gr.TabItem("Texto/Info"):
213
- gr.Markdown("Edita información interna o extrae texto plano.")
214
- with gr.Row():
215
- with gr.Column():
216
- gr.Markdown("### Extraer Texto")
217
- t_file = gr.File(label="PDF")
218
- t_btn = gr.Button("Obtener .txt")
219
- t_out = gr.File()
220
- t_btn.click(interface_text, t_file, t_out)
221
- with gr.Column():
222
- gr.Markdown("### Editar Metadatos")
223
- meta_f = gr.File(label="PDF")
224
- meta_t = gr.Textbox(label="Título")
225
- meta_a = gr.Textbox(label="Autor")
226
- meta_s = gr.Textbox(label="Asunto")
227
- meta_b = gr.Button("Guardar")
228
- meta_o = gr.File()
229
- meta_b.click(interface_meta, [meta_f, meta_t, meta_a, meta_s], meta_o)
230
-
231
- # 8. PROTEGER
232
- with gr.TabItem("Proteger"):
233
- gr.Markdown("Cifra tu documento con contraseña.")
234
- with gr.Row():
235
- p_file = gr.File(label="PDF")
236
- p_pwd = gr.Textbox(label="Contraseña", type="password")
237
- p_btn = gr.Button("Encriptar", variant="primary")
238
- p_out = gr.File(label="PDF Protegido")
239
- p_btn.click(interface_protect, [p_file, p_pwd], p_out)
240
 
241
  if __name__ == "__main__":
242
  demo.launch()
 
1
+ # Versión 1.9: Interfaz Gráfica Completa (Todas las herramientas)
2
+ # Autor: Gemini (AI Assistant)
3
+ # Descripción: UI construida con Gradio. Se encarga solo de la presentación y llama a core.py.
4
+
5
  import gradio as gr
6
  import pandas as pd
7
  import config
8
  from core import PDFEngine
9
 
10
+ # Inyección de dependencias
11
  engine = PDFEngine()
12
 
13
+ # --- FUNCIONES DE ENLACE (WRAPPERS) ---
14
+ # Estas funciones conectan la UI con el Core y manejan errores visuales.
15
 
 
16
  def update_file_list(files):
17
+ """Actualiza la tabla de IDs cuando se suben archivos para Unir."""
18
  if not files: return pd.DataFrame(), ""
19
+ # Gradio devuelve lista de rutas
20
  data = [[i, f.split("/")[-1]] for i, f in enumerate(files)]
21
+ # Generar orden por defecto "0, 1, 2..."
22
  default_order = ",".join([str(i) for i in range(len(files))])
23
  return pd.DataFrame(data, columns=["ID", "Archivo"]), default_order
24
 
25
  def process_merge(files, order_str):
26
  if not files: return None
27
  try:
28
+ # Parsear string "0, 2, 1" a lista [0, 2, 1]
29
  indices = [int(x.strip()) for x in order_str.split(",") if x.strip().isdigit()]
30
  return engine.merge_pdfs(files, indices)
31
+ except Exception as e: raise gr.Error(f"Error al unir: {str(e)}")
32
 
 
33
  def load_pdf_info(file):
34
+ """Carga información básica al subir un archivo."""
35
  if not file: return None, 0, gr.update(visible=False)
36
  info = engine.get_pdf_info(file)
37
+ return f"📄 {info['name']} ({info['pages']} páginas)", info['pages'], gr.update(visible=True)
38
 
39
  def update_split_preview(file, range_str, total_pages):
40
+ """Muestra previsualización de las páginas seleccionadas en Dividir."""
41
  if not file or not range_str: return None
42
  key_pages = engine.get_preview_indices_from_string(range_str, total_pages)
43
  if not key_pages: return None
44
+
45
  gallery = []
46
+ # Limitamos a 8 imágenes para no saturar la UI
47
  for p in key_pages[:8]:
48
  path = engine.generate_preview(file, p)
49
  if path: gallery.append((path, f"Pág {p}"))
 
54
  try: return engine.split_pdf_custom(file, range_str)
55
  except Exception as e: raise gr.Error(str(e))
56
 
57
+ def process_reorder(file, order_str):
58
+ if not file: return None
59
+ try: return engine.reorder_pages(file, order_str)
60
+ except Exception as e: raise gr.Error(str(e))
61
+
62
+ def process_compare(file_a, file_b):
63
+ if not file_a or not file_b: return None
64
+ try: return engine.compare_pdfs_visual(file_a, file_b)
65
+ except Exception as e: raise gr.Error(f"Error comparando: {str(e)}")
66
+
67
+ def process_compress(file, level_label):
68
  if not file: return None
69
+ # Mapeo de etiqueta UI a nivel Ghostscript
70
  levels = {
71
  "Baja (Máxima calidad)": 1,
72
  "Media (Recomendado - eBook)": 3,
 
76
  try: return engine.compress_pdf(file, power)
77
  except Exception as e: raise gr.Error(str(e))
78
 
79
+ def process_word(file):
 
80
  if not file: return None
81
  try: return engine.pdf_to_word(file)
82
  except Exception as e: raise gr.Error(str(e))
83
 
 
84
  def update_rotate_preview(file, angle_label):
85
  if not file: return None
86
+ # Parsear "90° (Derecha)" -> 90
87
  angle = 0
88
  if "90" in angle_label: angle = 90
89
  elif "180" in angle_label: angle = 180
90
  elif "270" in angle_label: angle = 270
91
+
92
  return engine.get_rotated_preview(file, angle)
93
 
94
  def process_rotate(file, angle_label):
 
97
  if "90" in angle_label: angle = 90
98
  elif "180" in angle_label: angle = 180
99
  elif "270" in angle_label: angle = 270
100
+
101
+ if angle == 0: return file.name # Devolver original si es 0
102
  try: return engine.rotate_pdf(file, angle)
103
  except Exception as e: raise gr.Error(str(e))
104
 
105
+ def process_protect(file, pwd):
106
+ if not file or not pwd: return None
107
+ try: return engine.protect_pdf(file, pwd)
 
108
  except Exception as e: raise gr.Error(str(e))
109
 
110
+ def process_extract_text(file):
111
+ if not file: return None
112
+ try: return engine.extract_text(file)
113
  except Exception as e: raise gr.Error(str(e))
114
 
115
+ def process_metadata(file, t, a, s):
 
116
  if not file: return None
117
  try: return engine.update_metadata(file, t, a, s)
118
  except Exception as e: raise gr.Error(str(e))
119
 
120
+ def process_p2i(file):
121
  if not file: return None
122
+ try: return engine.pdf_to_images_zip(file)
123
  except Exception as e: raise gr.Error(str(e))
124
 
125
+ def process_i2p(files):
126
+ if not files: return None
127
+ try: return engine.images_to_pdf(files)
 
128
  except Exception as e: raise gr.Error(str(e))
129
 
130
 
131
+ # --- CONSTRUCCIÓN DE LA INTERFAZ (LAYOUT) ---
132
+
133
  with gr.Blocks(title=config.APP_TITLE, theme=gr.themes.Soft()) as demo:
134
  gr.Markdown(f"# {config.APP_TITLE}")
135
  gr.Markdown(config.APP_DESCRIPTION)
136
 
 
137
  with gr.Tabs():
138
 
139
+ # ---------------------------------------------------------
140
+ # TAB 1: UNIR (PRIORIDAD ALTA)
141
+ # ---------------------------------------------------------
142
+ with gr.TabItem("Unir"):
143
  gr.Markdown(config.TXT_MERGE)
144
  with gr.Row():
145
  with gr.Column(scale=1):
146
+ m_files = gr.File(file_count="multiple", label="Archivos PDF", file_types=[".pdf"])
147
  with gr.Column(scale=2):
148
  m_table = gr.Dataframe(headers=["ID", "Archivo"], interactive=False)
149
+ m_order = gr.Textbox(label="Orden de unión (IDs)", placeholder="Ej: 0, 2, 1")
150
  m_btn = gr.Button("Unir Archivos", variant="primary")
151
+ m_out = gr.File(label="Resultado Unido")
152
+
153
+ # Eventos
154
  m_files.change(update_file_list, m_files, [m_table, m_order])
155
  m_btn.click(process_merge, [m_files, m_order], m_out)
156
 
157
+ # ---------------------------------------------------------
158
+ # TAB 2: DIVIDIR Y REORDENAR (GESTIÓN DE PÁGINAS)
159
+ # ---------------------------------------------------------
160
+ with gr.TabItem("Dividir / Reordenar"):
161
  with gr.Row():
162
+ # Panel izquierdo: Carga de archivo
163
+ with gr.Column(scale=1):
164
+ dr_file = gr.File(label="PDF Origen", file_types=[".pdf"])
165
+ dr_info = gr.Markdown("")
166
+ dr_pages = gr.State(0) # Guarda num páginas
167
+
168
+ # Panel derecho: Herramientas
169
+ with gr.Column(scale=2):
170
+ with gr.Tab("Extraer (ZIP)"):
171
+ gr.Markdown("Separa páginas en archivos individuales.")
172
+ s_range = gr.Textbox(label="Rangos a extraer", placeholder="Ej: 1-3, 5")
173
+ with gr.Row():
174
+ s_prev_btn = gr.Button("Vista Previa")
175
+ s_btn = gr.Button("Dividir y Descargar ZIP", variant="primary")
176
+ s_gal = gr.Gallery(label="Preview", height=200, columns=4, object_fit="contain")
177
+ s_out = gr.File(label="Archivo ZIP")
178
+
179
+ s_prev_btn.click(update_split_preview, [dr_file, s_range, dr_pages], s_gal)
180
+ s_btn.click(process_split, [dr_file, s_range], s_out)
181
+
182
+ with gr.Tab("Reordenar (PDF)"):
183
+ gr.Markdown("Crea un nuevo PDF cambiando el orden de las páginas.")
184
+ r_order = gr.Textbox(label="Nuevo Orden", placeholder="Ej: 3, 1, 2, 4-10")
185
+ r_btn = gr.Button("Reordenar Documento", variant="primary")
186
+ r_out = gr.File(label="PDF Reordenado")
187
+
188
+ r_btn.click(process_reorder, [dr_file, r_order], r_out)
189
+
190
+ # Carga de info automática
191
+ dr_file.change(load_pdf_info, dr_file, [dr_info, dr_pages, s_out])
192
+
193
+ # ---------------------------------------------------------
194
+ # TAB 3: COMPARAR (NUEVO)
195
+ # ---------------------------------------------------------
196
+ with gr.TabItem("Comparar PDFs"):
197
+ gr.Markdown("Sube dos versiones para detectar diferencias visuales (marcadas en **Magenta**).")
198
+ with gr.Row():
199
+ cmp_a = gr.File(label="Versión A (Original)", file_types=[".pdf"])
200
+ cmp_b = gr.File(label="Versión B (Modificada)", file_types=[".pdf"])
201
+
202
+ cmp_btn = gr.Button("Generar Informe de Diferencias", variant="primary")
203
+ cmp_out = gr.File(label="PDF Comparativo")
204
+
205
+ cmp_btn.click(process_compare, [cmp_a, cmp_b], cmp_out)
206
+
207
+ # ---------------------------------------------------------
208
+ # TAB 4: COMPRIMIR
209
+ # ---------------------------------------------------------
210
+ with gr.TabItem("Comprimir"):
211
+ gr.Markdown("Optimiza el tamaño del archivo.")
212
  with gr.Row():
213
  with gr.Column():
214
  c_file = gr.File(label="PDF Original", file_types=[".pdf"])
 
217
  label="Nivel de Compresión",
218
  value="Media (Recomendado - eBook)"
219
  )
220
+ c_btn = gr.Button("Comprimir", variant="primary")
221
  with gr.Column():
222
  c_out = gr.File(label="PDF Comprimido")
223
+
224
+ c_btn.click(process_compress, [c_file, c_level], c_out)
225
 
226
+ # ---------------------------------------------------------
227
+ # TAB 5: CONVERTIR
228
+ # ---------------------------------------------------------
229
+ with gr.TabItem("Convertir"):
230
+ with gr.Tab("PDF a Word"):
231
+ gr.Markdown("Convierte a formato editable DOCX (Beta).")
232
+ w_file = gr.File(label="PDF")
233
+ w_btn = gr.Button("Convertir a Word")
234
+ w_out = gr.File(label="Documento Word")
235
+ w_btn.click(process_word, w_file, w_out)
236
+
237
+ with gr.Tab("PDF <-> Imágenes"):
238
+ with gr.Row():
239
+ with gr.Column():
240
+ gr.Markdown("### PDF a JPG")
241
+ p2i_f = gr.File(label="PDF")
242
+ p2i_b = gr.Button("Obtener Imágenes (ZIP)")
243
+ p2i_o = gr.File(label="ZIP")
244
+ p2i_b.click(process_p2i, p2i_f, p2i_o)
245
+ with gr.Column():
246
+ gr.Markdown("### JPG a PDF")
247
+ i2p_f = gr.File(label="Imágenes", file_count="multiple", file_types=["image"])
248
+ i2p_b = gr.Button("Crear PDF")
249
+ i2p_o = gr.File(label="PDF")
250
+ i2p_b.click(process_i2p, i2p_f, i2p_o)
251
+
252
+ # ---------------------------------------------------------
253
+ # TAB 6: HERRAMIENTAS EXTRA
254
+ # ---------------------------------------------------------
255
+ with gr.TabItem("Extras"):
256
+ with gr.Tab("Rotar"):
257
+ with gr.Row():
258
+ with gr.Column():
259
+ rot_file = gr.File(label="PDF", file_types=[".pdf"])
260
+ rot_ang = gr.Radio(["0° (Original)", "90° (Derecha)", "180° (Invertir)", "270° (Izquierda)"], label="Rotación", value="0° (Original)")
261
+ rot_btn = gr.Button("Aplicar Rotación")
262
+ with gr.Column():
263
+ rot_prev = gr.Image(label="Vista Previa", height=300)
264
+ rot_out = gr.File(label="PDF Rotado")
265
+
266
+ rot_file.change(update_rotate_preview, [rot_file, rot_ang], rot_prev)
267
+ rot_ang.change(update_rotate_preview, [rot_file, rot_ang], rot_prev)
268
+ rot_btn.click(process_rotate, [rot_file, rot_ang], rot_out)
269
+
270
+ with gr.Tab("Proteger"):
271
+ with gr.Row():
272
+ p_file = gr.File(label="PDF")
273
+ p_pwd = gr.Textbox(label="Contraseña", type="password")
274
+ p_btn = gr.Button("Encriptar")
275
+ p_out = gr.File(label="Protegido")
276
+ p_btn.click(process_protect, [p_file, p_pwd], p_out)
277
+
278
+ with gr.Tab("Metadatos y Texto"):
279
+ with gr.Row():
280
+ with gr.Column():
281
+ mt_file = gr.File(label="PDF")
282
+ mt_t = gr.Textbox(label="Título")
283
+ mt_a = gr.Textbox(label="Autor")
284
+ mt_s = gr.Textbox(label="Asunto")
285
+ mt_btn = gr.Button("Guardar Metadatos")
286
+ mt_out = gr.File(label="PDF Editado")
287
+ mt_btn.click(process_metadata, [mt_file, mt_t, mt_a, mt_s], mt_out)
288
+ with gr.Column():
289
+ gr.Markdown("### Extraer Texto Plano")
290
+ txt_btn = gr.Button("Extraer .txt")
291
+ txt_out = gr.File(label="Archivo de Texto")
292
+ txt_btn.click(process_extract_text, mt_file, txt_out)
 
 
 
293
 
294
  if __name__ == "__main__":
295
  demo.launch()