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
This commit is contained in:
Caio
2026-08-26 14:45:00 -03:00
parent 0856e00402
commit a814136e7f
2 changed files with 13 additions and 6 deletions

View File

@@ -58,8 +58,11 @@ class PDFEngine:
@staticmethod @staticmethod
def compress_pdfa(data: bytes, quality: int = 60) -> bytes: def compress_pdfa(data: bytes, quality: int = 60) -> bytes:
"""Comprime mantendo a estrutura PDF/A.""" """Comprime mantendo a estrutura PDF/A (protegido contra inflação)."""
return compress_pdfa(data, quality=quality) result = compress_pdfa(data, quality=quality)
if len(result) >= len(data):
return data
return result
@staticmethod @staticmethod
def detect_pdfa(data: bytes) -> dict: def detect_pdfa(data: bytes) -> dict:

View File

@@ -139,14 +139,18 @@ def strip_pdfa(data: bytes) -> bytes:
def compress_pdfa(data: bytes, quality: int = 60, max_dim: int = 2000) -> bytes: def compress_pdfa(data: bytes, quality: int = 60, max_dim: int = 2000) -> bytes:
""" """
Comprime imagens internas mantendo a estrutura PDF/A. Comprime imagens internas mantendo a estrutura PDF/A.
Usa a mesma lógica de compress_pdf (ImageFile.replace) Usa a atualização incremental (incremental=True) para garantir
mas preserva metadados XMP e OutputIntents. 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 PIL import Image as PILImage
from .compress import _prepare_image from .compress import _prepare_image
reader = PdfReader(io.BytesIO(data)) # A grande sacada: em vez de usar PdfWriter(clone_from=reader)
writer = 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 replaced = 0
for page in writer.pages: for page in writer.pages: