Core operations: - Compress PDF (3 presets: screen, ebook, printer, prepress) - PDF/A detection and conversion to regular PDF - Compress while preserving PDF/A structure - Merge multiple PDFs - Split/extract pages by range - Rotate pages - Extract text and images Interface: - Streamlit web GUI (app.py) - FastAPI REST API with auto OpenAPI docs (api.py) Docs: - README.md, ARCHITECTURE.md, API.md - Unit tests (15/15 passing)
99 lines
3.1 KiB
Python
99 lines
3.1 KiB
Python
"""
|
|
Orquestrador principal de transformações de PDF.
|
|
|
|
Coordena validação, compressão, operações e conversão PDF/A
|
|
encadeando as funções dos submódulos.
|
|
"""
|
|
|
|
import io
|
|
from typing import List, Tuple
|
|
|
|
from .validators import validate_pdf, assert_safe_filename
|
|
from .compress import compress_pdf
|
|
from .pdfa import detect_pdfa, strip_pdfa, compress_pdfa
|
|
from .operations import merge_pdfs, split_pdf, extract_pages, rotate_pages
|
|
from .operations import extract_text, extract_images, preview_pages
|
|
|
|
|
|
class PDFEngine:
|
|
"""
|
|
Interface única para todas as operações do toolkit.
|
|
Cada método recebe bytes e retorna bytes (ou metadados).
|
|
"""
|
|
|
|
@staticmethod
|
|
def inspect(data: bytes, filename: str = "") -> dict:
|
|
"""Valida e retorna metadados do PDF."""
|
|
info = validate_pdf(data, filename)
|
|
if info["valid"]:
|
|
pdfa_info = detect_pdfa(data)
|
|
info.update(pdfa_info)
|
|
return info
|
|
|
|
@staticmethod
|
|
def compress(data: bytes, preset: str = "ebook") -> bytes:
|
|
"""Comprime PDF com re-amostragem de imagens."""
|
|
return compress_pdf(data, preset=preset)
|
|
|
|
@staticmethod
|
|
def pdfa_to_regular(data: bytes) -> bytes:
|
|
"""Remove restrições PDF/A gerando PDF comum."""
|
|
return strip_pdfa(data)
|
|
|
|
@staticmethod
|
|
def compress_pdfa(data: bytes, quality: int = 60) -> bytes:
|
|
"""Comprime mantendo a estrutura PDF/A."""
|
|
return compress_pdfa(data, quality=quality)
|
|
|
|
@staticmethod
|
|
def detect_pdfa(data: bytes) -> dict:
|
|
"""Detecta se é PDF/A e qual nível."""
|
|
return detect_pdfa(data)
|
|
|
|
@staticmethod
|
|
def merge(files: List[Tuple[bytes, str]]) -> bytes:
|
|
"""Junta múltiplos PDFs."""
|
|
return merge_pdfs(files)
|
|
|
|
@staticmethod
|
|
def split(data: bytes, ranges: List[Tuple[int, int]], base: str = "pagina"):
|
|
"""Divide PDF em intervalos de páginas."""
|
|
return split_pdf(data, ranges, base)
|
|
|
|
@staticmethod
|
|
def extract(data: bytes, pages: List[int]) -> bytes:
|
|
"""Extrai páginas específicas."""
|
|
return extract_pages(data, pages)
|
|
|
|
@staticmethod
|
|
def rotate(data: bytes, rotations: List[Tuple[int, int]]) -> bytes:
|
|
"""Rotaciona páginas."""
|
|
return rotate_pages(data, rotations)
|
|
|
|
@staticmethod
|
|
def text(data: bytes) -> str:
|
|
"""Extrai texto de todas as páginas."""
|
|
return extract_text(data)
|
|
|
|
@staticmethod
|
|
def images(data: bytes):
|
|
"""Extrai imagens do PDF."""
|
|
return extract_images(data)
|
|
|
|
@staticmethod
|
|
def get_info_string(info: dict) -> str:
|
|
"""Formata metadados para exibição."""
|
|
lines = [
|
|
f"📄 **{info.get('filename', 'documento')}**",
|
|
f" - Tamanho: {info['file_size_mb']:.2f} MB",
|
|
f" - Páginas: {info['pages']}",
|
|
]
|
|
if info.get("valid"):
|
|
if info["is_pdfa"]:
|
|
level = info.get("level", "PDF/A")
|
|
lines.append(f" - 📋 Tipo: {level}")
|
|
else:
|
|
lines.append(" - 📋 Tipo: PDF comum")
|
|
else:
|
|
lines.append(f" - ⚠️ Erro: {info['error']}")
|
|
return "\n".join(lines) |