# Preenchimento automático e geração final de PDFs

## Objetivo

A versão 0.3.0 transforma um modelo publicado do editor visual num PDF preenchido e achatado, guarda o resultado no armazenamento privado e cria automaticamente um `Document` dentro do dossiê e da pasta escolhidos.

A geração é executada em background pela fila `pdf`. O pedido web apenas resolve e valida os dados, cria uma cópia imutável do modelo e coloca o trabalho na fila.

## Fluxo funcional

1. Publicar um modelo em **Formulários PDF**.
2. Escolher **Gerar PDF**.
3. Selecionar o dossiê.
4. Rever os dados resolvidos automaticamente e substituir valores editáveis quando necessário.
5. Escolher a pasta e o nome final.
6. Submeter a geração.
7. O worker importa cada página do PDF original, aplica as regiões e arquiva o resultado no dossiê.
8. O ecrã de detalhe acompanha `queued`, `processing`, `completed`, `failed` ou `cancelled`.

## Resolução de dados

`PdfDataContextBuilder` carrega apenas o tenant, dossiê, eventual perfil de utente, sala, responsáveis e sócios necessários ao preenchimento. `PdfDataSourceResolver` aceita apenas origens conhecidas ou caminhos seguros:

- `tenant.name`, `tenant.email`, `tenant.vat_number` e `tenant.settings.*`;
- `dossier.name`, `dossier.external_reference`, `dossier.type`, `dossier.status` e `dossier.metadata.*`;
- dados do utente, sala e ano letivo;
- pai, mãe, encarregado, titular do contrato e outros;
- até quatro registos de sócio;
- data e data/hora atuais.

Caminhos arbitrários, passwords, relações Eloquent e atributos fora da allowlist não são resolvidos.

## Transformações

Cada região pode configurar:

- formato de data PHP e presets `long_pt`, `datetime_pt`, `long_en` e `iso`;
- limite de caracteres;
- prefixo e sufixo;
- maiúsculas, minúsculas ou título;
- redução automática do tamanho da fonte;
- fonte mínima;
- alinhamento vertical;
- margem interna em milímetros;
- ajuste de imagem por conter, cobrir ou esticar;
- impressão opcional de fundo e contorno, sem imprimir por defeito a guia azul do editor.

Checkboxes são convertidos em booleano e desenhados vetorialmente. Imagens são aceites apenas a partir do armazenamento privado do próprio tenant ou de um `data:image` validado e limitado.

## Imutabilidade e auditoria

Ao criar uma geração são guardados:

- versão e checksum do modelo;
- cópia privada imutável do PDF de origem;
- snapshot de cada região;
- valor automático ou override aplicado;
- origem e resultado da validação;
- utilizador, dossiê, pasta e hora do pedido;
- tentativas do worker, duração e erros;
- checksum, tamanho e número de páginas do resultado.

Alterações posteriores ao modelo, utente ou dossiê não modificam uma geração já criada.

## Regiões de assinatura

Regiões `signature` e `initials` sem imagem não bloqueiam a geração. Ficam com estado `pending_signature`, o documento é criado com estado `prepared` e os campos pendentes ficam no metadata. O módulo de assinatura pública utiliza estes snapshots para aplicar a assinatura visual sem alterar o modelo original.

## Motor PDF

A aplicação usa `setasign/fpdi` com `tecnickcom/tcpdf`:

- FPDI importa as páginas existentes;
- TCPDF desenha texto UTF-8, caixas, checkboxes e imagens;
- coordenadas normalizadas do editor são convertidas para as dimensões reais de cada página;
- o resultado é escrito num ficheiro temporário e enviado por stream para o storage, evitando manter o PDF completo em memória.

PDFs protegidos por password ou com estruturas não suportadas pelo parser são recusados com erro explícito e podem ser repetidos depois de substituir o modelo.

## Segurança

- todas as rotas exigem autenticação, subscrição ativa, módulo `pdf_forms` e função `owner`, `admin` ou `editor`;
- o controller confirma explicitamente o `tenant_id` dos modelos e gerações;
- o PDF de origem é verificado novamente por checksum antes de renderizar;
- imagens só podem usar caminhos iniciados por `tenants/{tenant_id}/`;
- PNG, JPEG e WebP são validados por conteúdo, tamanho e resolução;
- caminhos com travessia (`..`), discos não autorizados e assets de outro modelo/tenant são recusados;
- o tamanho máximo do PDF final é validado antes do arquivo;
- temporários têm permissões `0700`, são eliminados no fim e existe limpeza diária de resíduos;
- downloads são privados e enviados com `no-store` e `nosniff`.

## Filas e operação

O Docker Compose inclui um worker com:

```bash
php artisan queue:work --queue=pdf,default --sleep=2 --tries=3 --timeout=300
```

Comandos úteis:

```bash
php artisan dossier:generate-pdf TENANT_ID TEMPLATE_ID DOSSIER_ID --folder=PASTA_ID --name="Ficha.pdf"
php artisan pdf:purge-temporary --hours=24
php artisan queue:failed
php artisan queue:retry all
```

Variáveis:

```dotenv
PDF_GENERATION_QUEUE=pdf
PDF_GENERATION_MAX_PAGES=2000
PDF_GENERATION_MAX_IMAGE_BYTES=10485760
PDF_GENERATION_MAX_IMAGE_PIXELS=40000000
PDF_GENERATION_MAX_OUTPUT_BYTES=104857600
```

## Tabelas

- `pdf_generations`: pedido, origem imutável, destino, estado e resultado;
- `pdf_generation_fields`: snapshot e valor final de cada região;
- `pdf_generation_attempts`: execuções e erros dos workers.
