Ferramentas/AjustaFundos/actions/file/tif_to_pdf_converter.py

218 lines
8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from PIL import Image, UnidentifiedImageError
from pathlib import Path
from pypdf import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from io import BytesIO
from pypdf.constants import UserAccessPermissions
import base64
from typing import Optional
from actions.ui.ui import info, ok, fail, status, progress
# Alias para manter compatibilidade e usar "error" na interface
def error(message: str):
fail(message)
class TifToPdfConverter:
"""
Converte um arquivo TIFF/SPD específico em PDF protegido e confidencial.
Totalmente em memória, com marca d'água e feedback visual via UI.
"""
def __init__(
self, input_file: str, output_folder: Optional[str] = "storage/output"
):
self.input_file = Path(input_file)
if not self.input_file.exists():
raise FileNotFoundError(f"Arquivo não encontrado: {self.input_file}")
# Pasta de saída padrão
self.output_folder = Path(output_folder)
self.output_folder.mkdir(parents=True, exist_ok=True)
self.watermark_text = "DISPONÍVEL APENAS PARA ATIVIDADE CORRECIONAL ONLINE"
self.confidential_metadata = {
"/Title": "Documento Confidencial",
"/Author": "Sistema MirrorSYNC",
"/Subject": "Documento protegido",
"/Keywords": "Confidencial, Corregedoria, MirrorSYNC, Orius",
"/Producer": "Orius Tecnologia",
"/Creator": "MirrorSYNC Automação",
}
# =========================================================
# Helper 1 — Converte imagem em PDF (BytesIO)
# =========================================================
def _convert_image_to_pdf(self) -> Optional[BytesIO]:
try:
with Image.open(self.input_file) as img:
frames = []
# Converte todas as páginas do TIFF para RGB
for frame in range(getattr(img, "n_frames", 1)):
img.seek(frame)
frame_rgb = img.convert("RGB")
frames.append(frame_rgb.copy())
if not frames:
info(f"Nenhuma página encontrada em {self.input_file}")
return None
# Buffer em memória
buffer = BytesIO()
# Salva todas as páginas como um único PDF
frames[0].save(
buffer,
format="PDF",
resolution=150,
save_all=True,
append_images=frames[1:],
quality=80,
optimize=True,
)
buffer.seek(0)
ok(
f"Imagem convertida para PDF em memória "
f"({len(frames)} página(s))."
)
return buffer
except UnidentifiedImageError:
error(f"Arquivo não reconhecido como imagem: {self.input_file}")
except Exception as e:
error(f"Erro ao converter {self.input_file}: {e}")
return None
# =========================================================
# Helper 2 — Gera PDF de marca dágua
# =========================================================
def _create_watermark_pdf(self, page_width: float, page_height: float) -> BytesIO:
buffer = BytesIO()
c = canvas.Canvas(buffer, pagesize=(page_width, page_height))
c.saveState()
# Fonte adaptativa (limites 24..72)
base_font = max(24, min(72, max(page_width, page_height) * 0.04))
c.setFont("Helvetica-Bold", base_font)
# Cor vermelha com transparência (~30%)
try:
c.setFillColorRGB(1, 0, 0) # vermelho
c.setFillAlpha(0.3)
except Exception:
# Fallback sem alpha (ainda vermelho)
c.setFillColorRGB(1, 0, 0)
# Centro e rotação para deixar na diagonal
x_center = page_width / 2.0
y_center = page_height / 2.0
c.translate(x_center, y_center)
c.rotate(45) # ângulo da marca d'água (diagonal)
# Linhas igualmente espaçadas ao longo do eixo Y rotacionado
span = max(page_width, page_height) * 0.4
y_positions = [-span, -span * 0.5, 0, span * 0.5, span]
for y in y_positions:
c.drawCentredString(0, y, self.watermark_text)
c.restoreState()
c.save()
buffer.seek(0)
return buffer
# =========================================================
# Helper 3 — Aplica marca d'água e confidencialidade
# =========================================================
def _apply_watermark_and_security(self, pdf_buffer: BytesIO) -> BytesIO:
reader = PdfReader(pdf_buffer)
writer = PdfWriter()
for page in reader.pages:
width = float(page.mediabox.width)
height = float(page.mediabox.height)
watermark_stream = self._create_watermark_pdf(width, height)
watermark_pdf = PdfReader(watermark_stream)
watermark_page = watermark_pdf.pages[0]
page.merge_page(watermark_page)
writer.add_page(page)
# Aplica metadados confidenciais
writer.add_metadata(self.confidential_metadata)
# Bloqueia ações do usuário
block_permissions = (
UserAccessPermissions.PRINT
| UserAccessPermissions.MODIFY
| UserAccessPermissions.EXTRACT
| UserAccessPermissions.ASSEMBLE_DOC
| UserAccessPermissions.PRINT_TO_REPRESENTATION
)
writer.encrypt(
user_password="",
owner_password="correcional",
permissions_flag=int(block_permissions),
)
output_buffer = BytesIO()
writer.write(output_buffer)
output_buffer.seek(0)
ok("Marca dágua e proteção aplicadas em memória.")
return output_buffer
# =========================================================
# Helper 4 — Converte PDF final para Base64
# =========================================================
def _to_base64(self, pdf_buffer: BytesIO) -> str:
b64 = base64.b64encode(pdf_buffer.getvalue()).decode("ascii")
ok("Arquivo convertido para Base64.")
return b64
# =========================================================
# Método principal — Processamento completo
# =========================================================
def convert(self, return_base64: bool = False) -> Optional[str]:
"""
Executa a conversão e retorna:
- Base64 (string), se return_base64=True
- Caminho do arquivo PDF salvo em disco, se return_base64=False
"""
info(f"📄 Iniciando conversão de: {self.input_file.name}")
# Barra de progresso única para todo o fluxo
with progress(f"Processando {self.input_file.name}...", total=3) as step:
# 1) Converter imagem em PDF
with status("Convertendo imagem para PDF..."):
pdf_buffer = self._convert_image_to_pdf()
step()
if not pdf_buffer:
error("Falha na conversão da imagem. Processo interrompido.")
return None
# 2) Aplicar marca dágua e segurança
with status("Aplicando marca dágua e proteção..."):
protected_buffer = self._apply_watermark_and_security(pdf_buffer)
step()
# 3) Gerar Base64 ou salvar em disco
if return_base64:
with status("Gerando conteúdo Base64..."):
result = self._to_base64(protected_buffer)
ok("Conversão concluída com sucesso (Base64 gerado).")
step()
return result
with status("Salvando arquivo PDF em disco..."):
output_path = self.output_folder / f"{self.input_file.stem}.pdf"
with open(output_path, "wb") as f:
f.write(protected_buffer.getvalue())
ok(f"Arquivo PDF salvo em: {output_path}")
step()
return str(output_path)