""" Validação de arquivos PDF recebidos. """ import io import struct from pathlib import Path from typing import Union from pypdf import PdfReader PDF_MAGIC = b"%PDF" def is_pdf_stream(data: bytes) -> bool: """Verifica se o buffer começa com a magic number de PDF.""" return data[:4] == PDF_MAGIC def is_pdf_file(path: Union[str, Path]) -> bool: """Verifica se o arquivo é um PDF válido.""" path = Path(path) if not path.exists() or not path.is_file(): return False with open(path, "rb") as f: header = f.read(4) return header == PDF_MAGIC def validate_pdf(data: bytes, filename: str = "") -> dict: """ Valida um PDF em memória. Retorna dict com: - valid: bool - pages: int (se válido) - error: str (se inválido) - is_pdfa: bool (provável PDF/A) - file_size_mb: float """ result = { "valid": False, "pages": 0, "error": "", "is_pdfa": False, "file_size_mb": round(len(data) / (1024 * 1024), 2), "filename": filename, } if not is_pdf_stream(data): result["error"] = "Arquivo não é um PDF (magic number inválido)" return result try: reader = PdfReader(io.BytesIO(data)) result["pages"] = len(reader.pages) result["valid"] = True # Detecção básica de PDF/A via metadados XMP if reader.metadata: xmp = str(reader.metadata.raw_data or "") if "/pdfaid:part" in xmp.lower() or "pdfa" in xmp.lower(): result["is_pdfa"] = True except Exception as e: result["error"] = f"Erro ao ler PDF: {str(e)}" return result def assert_safe_filename(name: str) -> str: """Remove caracteres perigosos de nomes de arquivo.""" import re safe = re.sub(r'[^\w\-. ]', "_", name) if not safe: safe = "documento" if not safe.lower().endswith(".pdf"): safe += ".pdf" return safe