DaniFera commited on
Commit
1e79d66
·
verified ·
1 Parent(s): dd0b049

Update core.py

Browse files
Files changed (1) hide show
  1. core.py +124 -56
core.py CHANGED
@@ -1,99 +1,167 @@
1
- # Versión 1.0: Lógica para Unir, Dividir y Proteger PDFs
2
  import os
3
  from pypdf import PdfWriter, PdfReader
 
4
  from config import TEMP_DIR
5
  import uuid
6
 
7
  class PDFEngine:
8
- """
9
- Motor de procesamiento de PDFs.
10
- Principio SRP: Solo se encarga de manipular bytes y archivos.
11
- """
12
-
13
  @staticmethod
14
  def _get_output_path(filename: str) -> str:
15
- """Genera una ruta única para evitar colisiones."""
16
  unique_name = f"{uuid.uuid4().hex[:8]}_{filename}"
17
  return os.path.join(TEMP_DIR, unique_name)
18
 
19
- def merge_pdfs(self, file_paths: list) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  """
21
- Une múltiples PDFs en uno solo.
22
  Args:
23
- file_paths: Lista de rutas de archivos temporales.
24
- Returns:
25
- Ruta del archivo generado.
26
  """
27
  if not file_paths:
28
- raise ValueError("No se han proporcionado archivos.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  merger = PdfWriter()
31
-
32
  try:
33
- for path in file_paths:
34
  merger.append(path)
35
 
36
- output_path = self._get_output_path("unido.pdf")
37
  with open(output_path, "wb") as f:
38
  merger.write(f)
39
-
40
  return output_path
41
  except Exception as e:
42
- raise RuntimeError(f"Error al unir PDFs: {str(e)}")
43
  finally:
44
  merger.close()
45
 
46
- def split_pdf(self, file_path: str) -> list:
47
  """
48
- Divide un PDF en páginas individuales.
49
- Nota: Devuelve una lista de archivos.
50
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  if not file_path:
52
  raise ValueError("Archivo no proporcionado.")
53
 
54
- output_files = []
55
- try:
56
- reader = PdfReader(file_path)
57
- base_name = os.path.basename(file_path).replace(".pdf", "")
 
 
 
 
58
 
59
- for i, page in enumerate(reader.pages):
60
- writer = PdfWriter()
61
- writer.add_page(page)
62
-
63
- out_name = f"{base_name}_pag_{i+1}.pdf"
64
- out_path = self._get_output_path(out_name)
65
-
66
- with open(out_path, "wb") as f:
67
- writer.write(f)
68
-
69
- output_files.append(out_path)
 
 
70
 
71
- return output_files
72
- except Exception as e:
73
- raise RuntimeError(f"Error al dividir PDF: {str(e)}")
 
 
 
 
74
 
75
  def protect_pdf(self, file_path: str, password: str) -> str:
76
- """
77
- Encripta un PDF con contraseña.
78
- """
79
  if not file_path or not password:
80
- raise ValueError("Faltan el archivo o la contraseña.")
81
-
82
  try:
83
  reader = PdfReader(file_path)
84
  writer = PdfWriter()
85
-
86
- # Copiar todas las páginas
87
- for page in reader.pages:
88
- writer.add_page(page)
89
-
90
- # Encriptar
91
  writer.encrypt(password)
92
-
93
  output_path = self._get_output_path("protegido.pdf")
94
- with open(output_path, "wb") as f:
95
- writer.write(f)
96
-
97
  return output_path
98
- except Exception as e:
99
- raise RuntimeError(f"Error al proteger PDF: {str(e)}")
 
1
+ # Versión 1.1: Soporte para rangos, reordenamiento y previsualización (poppler)
2
  import os
3
  from pypdf import PdfWriter, PdfReader
4
+ from pdf2image import convert_from_path
5
  from config import TEMP_DIR
6
  import uuid
7
 
8
  class PDFEngine:
9
+
 
 
 
 
10
  @staticmethod
11
  def _get_output_path(filename: str) -> str:
 
12
  unique_name = f"{uuid.uuid4().hex[:8]}_{filename}"
13
  return os.path.join(TEMP_DIR, unique_name)
14
 
15
+ def get_pdf_info(self, file_path: str) -> dict:
16
+ """Obtiene metadatos básicos del PDF."""
17
+ try:
18
+ reader = PdfReader(file_path)
19
+ return {"pages": len(reader.pages), "name": os.path.basename(file_path)}
20
+ except Exception:
21
+ return {"pages": 0, "name": "Error"}
22
+
23
+ def generate_preview(self, file_path: str, page_number: int) -> str:
24
+ """
25
+ Genera una imagen de una página específica.
26
+ Args:
27
+ page_number: Número de página (1-based index para el usuario)
28
+ """
29
+ try:
30
+ # Convertir solo la página solicitada (first_page es 1-based, last_page es 1-based)
31
+ images = convert_from_path(
32
+ file_path,
33
+ first_page=page_number,
34
+ last_page=page_number,
35
+ size=(None, 400) # Altura fija para eficiencia
36
+ )
37
+ if images:
38
+ output_path = self._get_output_path(f"preview_pg{page_number}.jpg")
39
+ images[0].save(output_path, "JPEG")
40
+ return output_path
41
+ return None
42
+ except Exception as e:
43
+ print(f"Error generando preview: {e}")
44
+ return None
45
+
46
+ def merge_pdfs(self, file_paths: list, order_indices: list = None) -> str:
47
  """
48
+ Une PDFs permitiendo reordenamiento.
49
  Args:
50
+ order_indices: Lista de enteros indicando el orden (ej: [2, 0, 1]).
51
+ Si es None, usa el orden de llegada.
 
52
  """
53
  if not file_paths:
54
+ raise ValueError("No hay archivos para unir.")
55
+
56
+ # Reordenar si se especifican índices
57
+ ordered_paths = []
58
+ if order_indices:
59
+ try:
60
+ # Validar que los índices estén dentro del rango
61
+ if len(order_indices) != len(file_paths):
62
+ # Fallback si las longitudes no coinciden
63
+ ordered_paths = file_paths
64
+ else:
65
+ for idx in order_indices:
66
+ ordered_paths.append(file_paths[int(idx)])
67
+ except (ValueError, IndexError):
68
+ # Si el usuario mete datos inválidos, usamos orden original
69
+ ordered_paths = file_paths
70
+ else:
71
+ ordered_paths = file_paths
72
 
73
  merger = PdfWriter()
 
74
  try:
75
+ for path in ordered_paths:
76
  merger.append(path)
77
 
78
+ output_path = self._get_output_path("unido_ordenado.pdf")
79
  with open(output_path, "wb") as f:
80
  merger.write(f)
 
81
  return output_path
82
  except Exception as e:
83
+ raise RuntimeError(f"Error al unir: {str(e)}")
84
  finally:
85
  merger.close()
86
 
87
+ def parse_range_string(self, range_str: str, max_pages: int) -> list:
88
  """
89
+ Convierte "1-3, 5" en [0, 1, 2, 4] (0-based indices).
 
90
  """
91
+ pages = set()
92
+ parts = range_str.split(',')
93
+
94
+ for part in parts:
95
+ part = part.strip()
96
+ if '-' in part:
97
+ try:
98
+ start, end = map(int, part.split('-'))
99
+ # Ajustar a rango válido y convertir a 0-index
100
+ start = max(1, start)
101
+ end = min(max_pages, end)
102
+ if start <= end:
103
+ for p in range(start - 1, end):
104
+ pages.add(p)
105
+ except ValueError:
106
+ continue
107
+ else:
108
+ try:
109
+ p = int(part)
110
+ if 1 <= p <= max_pages:
111
+ pages.add(p - 1)
112
+ except ValueError:
113
+ continue
114
+
115
+ return sorted(list(pages))
116
+
117
+ def split_pdf_custom(self, file_path: str, range_str: str) -> list:
118
+ """Divide basándose en un rango string."""
119
  if not file_path:
120
  raise ValueError("Archivo no proporcionado.")
121
 
122
+ reader = PdfReader(file_path)
123
+ total_pages = len(reader.pages)
124
+
125
+ # Obtener índices (0-based) a extraer
126
+ selected_indices = self.parse_range_string(range_str, total_pages)
127
+
128
+ if not selected_indices:
129
+ raise ValueError("El rango proporcionado no es válido o está vacío.")
130
 
131
+ output_files = []
132
+ base_name = os.path.basename(file_path).replace(".pdf", "")
133
+
134
+ # Agrupar páginas consecutivas podría ser una opción,
135
+ # pero la herramienta dice "Dividir", así que generamos un PDF por selección
136
+ # o un solo PDF con la selección?
137
+ # Interpretación estándar: "Extraer páginas" -> Crea un nuevo PDF con SOLO esas páginas
138
+ # Interpretación "Split": Rompe en archivos.
139
+ # Asumiré: Crear UN nuevo archivo con las páginas seleccionadas (Extract).
140
+
141
+ writer = PdfWriter()
142
+ for idx in selected_indices:
143
+ writer.add_page(reader.pages[idx])
144
 
145
+ out_name = f"{base_name}_extracto.pdf"
146
+ output_path = self._get_output_path(out_name)
147
+
148
+ with open(output_path, "wb") as f:
149
+ writer.write(f)
150
+
151
+ return output_path # Retornamos str, no lista, para esta variante de extracción.
152
 
153
  def protect_pdf(self, file_path: str, password: str) -> str:
154
+ # (Sin cambios respecto a v1.0, mantener código anterior)
155
+ # ... (copiar lógica previa) ...
156
+ # Por brevedad en la respuesta, asumo que mantienes el método protect_pdf de la v1.0
157
  if not file_path or not password:
158
+ raise ValueError("Faltan datos")
 
159
  try:
160
  reader = PdfReader(file_path)
161
  writer = PdfWriter()
162
+ for page in reader.pages: writer.add_page(page)
 
 
 
 
 
163
  writer.encrypt(password)
 
164
  output_path = self._get_output_path("protegido.pdf")
165
+ with open(output_path, "wb") as f: writer.write(f)
 
 
166
  return output_path
167
+ except Exception as e: raise RuntimeError(f"Error: {e}")