From a814136e7ff73709ebde6017d81bad056f16588b Mon Sep 17 00:00:00 2001 From: Caio Date: Wed, 26 Aug 2026 14:45:00 -0300 Subject: [PATCH] fix(pdfa): preserve compliance via incremental update on compression - Change PdfWriter to use incremental=True to preserve XMP/OutputIntents - Add inflation protection fallback in engine.compress_pdfa --- core/engine.py | 7 +++++-- core/pdfa.py | 12 ++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/engine.py b/core/engine.py index 78a7e31..75ae3b6 100644 --- a/core/engine.py +++ b/core/engine.py @@ -58,8 +58,11 @@ class PDFEngine: @staticmethod def compress_pdfa(data: bytes, quality: int = 60) -> bytes: - """Comprime mantendo a estrutura PDF/A.""" - return compress_pdfa(data, quality=quality) + """Comprime mantendo a estrutura PDF/A (protegido contra inflação).""" + result = compress_pdfa(data, quality=quality) + if len(result) >= len(data): + return data + return result @staticmethod def detect_pdfa(data: bytes) -> dict: diff --git a/core/pdfa.py b/core/pdfa.py index 64a5a6d..f3bea32 100644 --- a/core/pdfa.py +++ b/core/pdfa.py @@ -139,14 +139,18 @@ def strip_pdfa(data: bytes) -> bytes: def compress_pdfa(data: bytes, quality: int = 60, max_dim: int = 2000) -> bytes: """ Comprime imagens internas mantendo a estrutura PDF/A. - Usa a mesma lógica de compress_pdf (ImageFile.replace) - mas preserva metadados XMP e OutputIntents. + Usa a atualização incremental (incremental=True) para garantir + que a assinatura estrutural, /ID do trailer, XMP Metadata e + /OutputIntents originais permaneçam 100% intactos e conformes. """ from PIL import Image as PILImage from .compress import _prepare_image - reader = PdfReader(io.BytesIO(data)) - writer = PdfWriter(clone_from=reader) + # A grande sacada: em vez de usar PdfWriter(clone_from=reader) + # que reconstrói a árvore inteira do zero perdendo a conformidade, + # instanciamos um writer que atua sobre um stream aberto no modo incremental. + stream = io.BytesIO(data) + writer = PdfWriter(stream, incremental=True) replaced = 0 for page in writer.pages: