Update core.py
Browse files
core.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
-
# Versi贸n 1.
|
| 2 |
# Autor: Gemini (AI Assistant)
|
| 3 |
# Descripci贸n: Motor l贸gico de manipulaci贸n de PDFs. Independiente de la interfaz gr谩fica.
|
| 4 |
|
| 5 |
import os
|
| 6 |
import zipfile
|
| 7 |
import uuid
|
|
|
|
| 8 |
from pypdf import PdfWriter, PdfReader
|
| 9 |
from pdf2image import convert_from_path
|
| 10 |
from pdf2docx import Converter
|
|
@@ -14,7 +15,7 @@ from config import TEMP_DIR
|
|
| 14 |
class PDFEngine:
|
| 15 |
"""
|
| 16 |
Clase principal que encapsula toda la l贸gica de manipulaci贸n de archivos.
|
| 17 |
-
Sigue el principio de Responsabilidad 脷nica (SRP)
|
| 18 |
"""
|
| 19 |
|
| 20 |
# --- UTILIDADES INTERNAS ---
|
|
@@ -75,52 +76,6 @@ class PDFEngine:
|
|
| 75 |
|
| 76 |
# --- PREVISUALIZACI脫N ---
|
| 77 |
|
| 78 |
-
def compress_pdf(self, file_path: str, power: int = 2) -> str:
|
| 79 |
-
"""
|
| 80 |
-
Comprime PDF usando Ghostscript.
|
| 81 |
-
power:
|
| 82 |
-
0: /default (casi nada)
|
| 83 |
-
1: /prepress (alta calidad, poco tama帽o)
|
| 84 |
-
2: /printer (calidad media-alta)
|
| 85 |
-
3: /ebook (calidad media, buena compresi贸n - RECOMENDADO)
|
| 86 |
-
4: /screen (calidad baja, m谩xima compresi贸n 72dpi)
|
| 87 |
-
"""
|
| 88 |
-
if not file_path: raise ValueError("Falta archivo.")
|
| 89 |
-
|
| 90 |
-
# Mapeo de niveles de Ghostscript
|
| 91 |
-
quality = {
|
| 92 |
-
0: "/default",
|
| 93 |
-
1: "/prepress",
|
| 94 |
-
2: "/printer",
|
| 95 |
-
3: "/ebook",
|
| 96 |
-
4: "/screen"
|
| 97 |
-
}
|
| 98 |
-
gs_setting = quality.get(power, "/ebook")
|
| 99 |
-
|
| 100 |
-
output_path = self._get_output_path("comprimido.pdf")
|
| 101 |
-
|
| 102 |
-
# Comando Ghostscript
|
| 103 |
-
# gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=out.pdf in.pdf
|
| 104 |
-
cmd = [
|
| 105 |
-
"gs",
|
| 106 |
-
"-sDEVICE=pdfwrite",
|
| 107 |
-
"-dCompatibilityLevel=1.4",
|
| 108 |
-
f"-dPDFSETTINGS={gs_setting}",
|
| 109 |
-
"-dNOPAUSE",
|
| 110 |
-
"-dQUIET",
|
| 111 |
-
"-dBATCH",
|
| 112 |
-
f"-sOutputFile={output_path}",
|
| 113 |
-
file_path
|
| 114 |
-
]
|
| 115 |
-
|
| 116 |
-
try:
|
| 117 |
-
subprocess.run(cmd, check=True)
|
| 118 |
-
return output_path
|
| 119 |
-
except subprocess.CalledProcessError as e:
|
| 120 |
-
raise RuntimeError(f"Error en compresi贸n (Ghostscript): {e}")
|
| 121 |
-
except FileNotFoundError:
|
| 122 |
-
raise RuntimeError("Ghostscript no est谩 instalado en el sistema (packages.txt).")
|
| 123 |
-
|
| 124 |
def generate_preview(self, file_path: str, page_number: int) -> str:
|
| 125 |
"""Genera JPG de una p谩gina espec铆fica (page_number es 1-based)."""
|
| 126 |
try:
|
|
@@ -246,6 +201,51 @@ class PDFEngine:
|
|
| 246 |
|
| 247 |
return zip_path
|
| 248 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
def protect_pdf(self, file_path: str, password: str) -> str:
|
| 250 |
"""Cifra el PDF con contrase帽a."""
|
| 251 |
if not file_path or not password: raise ValueError("Faltan datos.")
|
|
|
|
| 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 |
|
| 5 |
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
|
|
|
|
| 15 |
class PDFEngine:
|
| 16 |
"""
|
| 17 |
Clase principal que encapsula toda la l贸gica de manipulaci贸n de archivos.
|
| 18 |
+
Sigue el principio de Responsabilidad 脷nica (SRP).
|
| 19 |
"""
|
| 20 |
|
| 21 |
# --- UTILIDADES INTERNAS ---
|
|
|
|
| 76 |
|
| 77 |
# --- PREVISUALIZACI脫N ---
|
| 78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
def generate_preview(self, file_path: str, page_number: int) -> str:
|
| 80 |
"""Genera JPG de una p谩gina espec铆fica (page_number es 1-based)."""
|
| 81 |
try:
|
|
|
|
| 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 |
+
|
| 216 |
+
# Mapeo de niveles de Ghostscript
|
| 217 |
+
quality = {
|
| 218 |
+
0: "/default",
|
| 219 |
+
1: "/prepress",
|
| 220 |
+
2: "/printer",
|
| 221 |
+
3: "/ebook",
|
| 222 |
+
4: "/screen"
|
| 223 |
+
}
|
| 224 |
+
gs_setting = quality.get(power, "/ebook")
|
| 225 |
+
|
| 226 |
+
output_path = self._get_output_path("comprimido.pdf")
|
| 227 |
+
|
| 228 |
+
# Comando Ghostscript
|
| 229 |
+
cmd = [
|
| 230 |
+
"gs",
|
| 231 |
+
"-sDEVICE=pdfwrite",
|
| 232 |
+
"-dCompatibilityLevel=1.4",
|
| 233 |
+
f"-dPDFSETTINGS={gs_setting}",
|
| 234 |
+
"-dNOPAUSE",
|
| 235 |
+
"-dQUIET",
|
| 236 |
+
"-dBATCH",
|
| 237 |
+
f"-sOutputFile={output_path}",
|
| 238 |
+
file_path
|
| 239 |
+
]
|
| 240 |
+
|
| 241 |
+
try:
|
| 242 |
+
subprocess.run(cmd, check=True)
|
| 243 |
+
return output_path
|
| 244 |
+
except subprocess.CalledProcessError as e:
|
| 245 |
+
raise RuntimeError(f"Error en compresi贸n (Ghostscript): {e}")
|
| 246 |
+
except FileNotFoundError:
|
| 247 |
+
raise RuntimeError("Error: Ghostscript no est谩 instalado en el sistema (packages.txt).")
|
| 248 |
+
|
| 249 |
def protect_pdf(self, file_path: str, password: str) -> str:
|
| 250 |
"""Cifra el PDF con contrase帽a."""
|
| 251 |
if not file_path or not password: raise ValueError("Faltan datos.")
|