# Merge de Anexos PDF — Mesmo Escopo (Categoria + Subcategoria)

## Propósito

Quando múltiplos anexos PDF são adicionados sob o **mesmo escopo** (`id_area` + `id_subarea`), o sistema agora os concatena em um único arquivo PDF antes do processamento. Isso elimina as quebras de página/coluna que ocorriam entre anexos consecutivos do mesmo escopo.

---

## Motivação

**Problema original:** Ao adicionar vários PDFs na mesma categoria e subcategoria, cada um era processado individualmente, causando:

- `checkIfIsNeedBreakPage(60)` entre cada anexo → espaço vazio
- Quebra de coluna ou página forçada entre anexos
- Coluna inteira vazia em layout de 2 colunas

**Solução:** Concatenar os PDFs em um único arquivo via FPDI antes do processamento. O loop principal vê um único multi-páginas e processa como se fosse um anexo só — sem quebras intermediárias.

---

## Implementação

### Arquivo modificado

`app/Repositories/Eloquent/Prefecture/DiarioOficial/DiarioOficialRepository.php`

### Novos componentes

#### 1. `mergePdfs()` (privado, ~linha 553)

Concatena múltiplos PDFs em um único arquivo temporário via FPDI:

```php
private function mergePdfs(array $items, int $diarioId): string
```

- Recebe array de objetos `DiarioHasAreas` (todos `type=file`, mesmo escopo)
- Cria instância FPDI separada (não interfere com o `$pdf` principal)
- Para cada item: `setSourceFile()` → `importPage()` por página → `AddPage()` + `useTemplate()`
- Salva o resultado em `_merged_{uniqid}.pdf` no mesmo diretório do PDF final
- Retorna caminho absoluto do PDF concatenado
- Se recebe apenas 1 item, retorna o caminho original sem merge

#### 2. Pré-processamento no `generatePdfFile()` (~linha 143-178)

Executado logo após o `->groupBy('id_area')`, **antes** do loop principal de renderização:

```php
$areas = $areas->map(function ($items) {
    // Identifica runs consecutivos de PDFs mergeáveis
    // Mergeáveis: type === 'file', path preenchido, mesmo id_subarea
    // Para cada run com 2+ itens: chama mergePdfs()
    // Remove itens excedentes da coleção
    // Reindexa com ->values()
});
```

**Critérios de merge:**

| Condição | Merge? |
|----------|--------|
| Consecutivos na ordenação (`position`) | ✅ Sim |
| Mesmo `id_area` | ✅ Sim |
| Mesmo `id_subarea` | ✅ Sim |
| `type === 'file'` | ✅ Sim |
| `path` preenchido | ✅ Sim |
| Separação por conteúdo textual entre eles | ❌ Não (grupos separados) |
| Subáreas diferentes | ❌ Não |

#### 3. Redirecionamento de path (~linha 336-337)

```php
$customPath = data_get($materia, '_merged_path');
$arquivo = $customPath ?: public_path($materia->getOriginal('path'));
```

Se o item tem `_merged_path` (definido no merge), usa o PDF concatenado. Caso contrário, usa o caminho original.

#### 4. Limpeza de temporários (~linha 451-460 e 465-474)

Arquivos `_merged_*` são removidos em dois cenários:
- **Sucesso:** antes de `return true`
- **Exceção:** dentro do `catch`, antes de `return false`

---

## Comportamento

| Situação | Antes | Depois |
|----------|-------|--------|
| 3 PDFs consecutivos, mesmo escopo | 3 processamentos → 3 quebras de página | 1 processamento → páginas contínuas |
| PDF único | Processamento normal | Inalterado |
| PDFs separados por texto | Cada PDF independente | Inalterado (não são consecutivos) |
| PDFs de subáreas diferentes | Independentes | Inalterado (escopo diferente) |
| Falha no merge de um PDF corrompido | — | Log warning + pulado no merge |

---

## Performance

O `mergePdfs()` usa FPDI para concatenar, que re-encoda cada página. Para PDFs típicos de governo (texto + logos), a perda de qualidade é imperceptível. O custo é O(n páginas) e ocorre uma única vez antes do loop principal.

---

## Limpeza

Arquivos temporários `_merged_*.pdf` são removidos automaticamente ao final (sucesso ou exceção). Não há acúmulo em disco.
