DaniFera commited on
Commit
6c7cf36
verified
1 Parent(s): 25d0b92

Update core.py

Browse files
Files changed (1) hide show
  1. core.py +118 -30
core.py CHANGED
@@ -1,4 +1,4 @@
1
- # Versi贸n 1.8: Core Completo con Compresi贸n Ghostscript
2
  # Autor: Gemini (AI Assistant)
3
  # Descripci贸n: Motor l贸gico de manipulaci贸n de PDFs. Independiente de la interfaz gr谩fica.
4
 
@@ -6,6 +6,8 @@ import os
6
  import zipfile
7
  import uuid
8
  import subprocess
 
 
9
  from pypdf import PdfWriter, PdfReader
10
  from pdf2image import convert_from_path
11
  from pdf2docx import Converter
@@ -134,7 +136,7 @@ class PDFEngine:
134
  except ValueError: continue
135
  return sorted(list(set(key_pages)))
136
 
137
- # --- FUNCIONALIDADES CORE ---
138
 
139
  def merge_pdfs(self, file_paths: list, order_indices: list = None) -> str:
140
  """Une m煤ltiples PDFs respetando el orden indicado."""
@@ -201,15 +203,45 @@ class PDFEngine:
201
 
202
  return zip_path
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  def compress_pdf(self, file_path: str, power: int = 2) -> str:
205
  """
206
  Comprime PDF usando Ghostscript.
207
  power:
208
- 0: /default (casi nada)
209
- 1: /prepress (alta calidad, poco tama帽o)
210
- 2: /printer (calidad media-alta)
211
- 3: /ebook (calidad media, buena compresi贸n - RECOMENDADO)
212
- 4: /screen (calidad baja, m谩xima compresi贸n 72dpi)
213
  """
214
  if not file_path: raise ValueError("Falta archivo.")
215
 
@@ -284,6 +316,32 @@ class PDFEngine:
284
  except Exception as e:
285
  raise RuntimeError(f"Error al rotar: {e}")
286
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  # --- CONVERSIONES ---
288
 
289
  def pdf_to_images_zip(self, file_path: str) -> str:
@@ -372,28 +430,58 @@ class PDFEngine:
372
  except Exception as e:
373
  raise RuntimeError(f"Error extrayendo texto: {e}")
374
 
375
- def update_metadata(self, file_path: str, title: str, author: str, subject: str) -> str:
376
- """Actualiza t铆tulo, autor y asunto."""
377
- if not file_path: raise ValueError("Falta archivo.")
378
-
 
 
 
 
 
379
  try:
380
- reader = PdfReader(file_path)
381
- writer = PdfWriter()
382
-
383
- for page in reader.pages:
384
- writer.add_page(page)
385
-
386
- new_meta = {
387
- "/Title": title,
388
- "/Author": author,
389
- "/Subject": subject,
390
- "/Producer": "OpenPDF Tools (Libre)"
391
- }
392
- writer.add_metadata(new_meta)
393
-
394
- output_path = self._get_output_path("editado_meta.pdf")
395
- with open(output_path, "wb") as f:
396
- writer.write(f)
397
- return output_path
398
  except Exception as e:
399
- raise RuntimeError(f"Error actualizando metadatos: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Versi贸n 1.9: Core Completo (Todas las herramientas integradas)
2
  # Autor: Gemini (AI Assistant)
3
  # Descripci贸n: Motor l贸gico de manipulaci贸n de PDFs. Independiente de la interfaz gr谩fica.
4
 
 
6
  import zipfile
7
  import uuid
8
  import subprocess
9
+ import cv2
10
+ import numpy as np
11
  from pypdf import PdfWriter, PdfReader
12
  from pdf2image import convert_from_path
13
  from pdf2docx import Converter
 
136
  except ValueError: continue
137
  return sorted(list(set(key_pages)))
138
 
139
+ # --- FUNCIONALIDADES DE GESTI脫N DE P脕GINAS ---
140
 
141
  def merge_pdfs(self, file_paths: list, order_indices: list = None) -> str:
142
  """Une m煤ltiples PDFs respetando el orden indicado."""
 
203
 
204
  return zip_path
205
 
206
+ def reorder_pages(self, file_path: str, order_str: str) -> str:
207
+ """
208
+ Crea un nuevo PDF con las p谩ginas en el orden especificado.
209
+ order_str ejemplo: "3, 1, 2, 4-6"
210
+ """
211
+ if not file_path: raise ValueError("Falta archivo.")
212
+
213
+ reader = PdfReader(file_path)
214
+ total_pages = len(reader.pages)
215
+
216
+ # Reutilizamos el parser de rangos para obtener la lista de 铆ndices
217
+ groups = self._parse_range_groups(order_str, total_pages)
218
+ if not groups: raise ValueError("Orden inv谩lido.")
219
+
220
+ flat_indices = []
221
+ for g in groups:
222
+ flat_indices.extend(g["indices"])
223
+
224
+ writer = PdfWriter()
225
+ for idx in flat_indices:
226
+ writer.add_page(reader.pages[idx])
227
+
228
+ output_path = self._get_output_path("reordenado.pdf")
229
+ with open(output_path, "wb") as f:
230
+ writer.write(f)
231
+
232
+ return output_path
233
+
234
+ # --- EDICI脫N Y SEGURIDAD ---
235
+
236
  def compress_pdf(self, file_path: str, power: int = 2) -> str:
237
  """
238
  Comprime PDF usando Ghostscript.
239
  power:
240
+ 0: /default
241
+ 1: /prepress
242
+ 2: /printer
243
+ 3: /ebook (Recomendado)
244
+ 4: /screen
245
  """
246
  if not file_path: raise ValueError("Falta archivo.")
247
 
 
316
  except Exception as e:
317
  raise RuntimeError(f"Error al rotar: {e}")
318
 
319
+ def update_metadata(self, file_path: str, title: str, author: str, subject: str) -> str:
320
+ """Actualiza t铆tulo, autor y asunto."""
321
+ if not file_path: raise ValueError("Falta archivo.")
322
+
323
+ try:
324
+ reader = PdfReader(file_path)
325
+ writer = PdfWriter()
326
+
327
+ for page in reader.pages:
328
+ writer.add_page(page)
329
+
330
+ new_meta = {
331
+ "/Title": title,
332
+ "/Author": author,
333
+ "/Subject": subject,
334
+ "/Producer": "OpenPDF Tools (Libre)"
335
+ }
336
+ writer.add_metadata(new_meta)
337
+
338
+ output_path = self._get_output_path("editado_meta.pdf")
339
+ with open(output_path, "wb") as f:
340
+ writer.write(f)
341
+ return output_path
342
+ except Exception as e:
343
+ raise RuntimeError(f"Error actualizando metadatos: {e}")
344
+
345
  # --- CONVERSIONES ---
346
 
347
  def pdf_to_images_zip(self, file_path: str) -> str:
 
430
  except Exception as e:
431
  raise RuntimeError(f"Error extrayendo texto: {e}")
432
 
433
+ # --- AN脕LISIS Y COMPARACI脫N ---
434
+
435
+ def compare_pdfs_visual(self, path_a: str, path_b: str) -> str:
436
+ """
437
+ Compara visualmente dos PDFs p谩gina por p谩gina y resalta diferencias.
438
+ Devuelve un PDF compuesto por im谩genes de las diferencias.
439
+ """
440
+ if not path_a or not path_b: raise ValueError("Se requieren dos archivos.")
441
+
442
  try:
443
+ imgs_a = convert_from_path(path_a, dpi=100)
444
+ imgs_b = convert_from_path(path_b, dpi=100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  except Exception as e:
446
+ raise RuntimeError(f"Error leyendo PDFs para comparar: {e}")
447
+
448
+ min_pages = min(len(imgs_a), len(imgs_b))
449
+ diff_pages = []
450
+
451
+ for i in range(min_pages):
452
+ # Convertir PIL a Numpy Array (RGB)
453
+ arr_a = np.array(imgs_a[i])
454
+ arr_b = np.array(imgs_b[i])
455
+
456
+ # Asegurar mismo tama帽o
457
+ if arr_a.shape != arr_b.shape:
458
+ h, w = arr_a.shape[:2]
459
+ arr_b = cv2.resize(arr_b, (w, h))
460
+
461
+ # Calcular diferencia
462
+ gray_a = cv2.cvtColor(arr_a, cv2.COLOR_RGB2GRAY)
463
+ gray_b = cv2.cvtColor(arr_b, cv2.COLOR_RGB2GRAY)
464
+
465
+ diff = cv2.absdiff(gray_a, gray_b)
466
+ _, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
467
+ contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
468
+
469
+ result_img = arr_a.copy()
470
+ for cnt in contours:
471
+ x, y, w, h = cv2.boundingRect(cnt)
472
+ cv2.rectangle(result_img, (x, y), (x + w, y + h), (255, 0, 255), 2)
473
+
474
+ diff_pages.append(Image.fromarray(result_img))
475
+
476
+ if not diff_pages:
477
+ raise ValueError("No se pudieron generar p谩ginas de comparaci贸n o no hay p谩ginas comunes.")
478
+
479
+ output_path = self._get_output_path("comparativa_diferencias.pdf")
480
+ diff_pages[0].save(
481
+ output_path, "PDF",
482
+ resolution=100.0,
483
+ save_all=True,
484
+ append_images=diff_pages[1:]
485
+ )
486
+
487
+ return output_path