Compare commits
4 Commits
a65066fe08
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
055531236b | ||
|
|
a06a5190f0 | ||
|
|
a814136e7f | ||
|
|
0856e00402 |
59
AGENTS.md
Normal file
59
AGENTS.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# 📄 PDF Toolkit — Manipulação, Otimização e Conversão de PDFs
|
||||||
|
|
||||||
|
@~/AGENTS.md
|
||||||
|
|
||||||
|
## 🧠 Contexto do Projeto & Memória no Obsidian
|
||||||
|
- **Cofre Principal:** `$OBSIDIAN_VAULT_PATH/`
|
||||||
|
- **Ficha do Projeto:** `$OBSIDIAN_VAULT_PATH/Projects/pdf-toolkit/Index.md`
|
||||||
|
- **Padrões de Arquitetura:** `$OBSIDIAN_VAULT_PATH/Projects/pdf-toolkit/padroes-arquitetura.md`
|
||||||
|
- **Registro de Gotchas & Lições:** `$OBSIDIAN_VAULT_PATH/Projects/pdf-toolkit/gotchas-e-bugs.md`
|
||||||
|
- **Especificação de Escopo:** [docs/escopo.md](docs/escopo.md)
|
||||||
|
- **Roadmap & Fases:** [docs/fases.md](docs/fases.md)
|
||||||
|
- **Documentação de Arquitetura Local:** [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
||||||
|
- **Documentação de Endpoints da API:** [docs/API.md](docs/API.md)
|
||||||
|
- **Repositório Gitea:** `https://gitea.cmconsultor.com.br/cmconsultor/pdf-toolkit`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Stack Tecnológica & Comandos
|
||||||
|
- **Linguagem & Runtime:** Python 3.12+
|
||||||
|
- **Bibliotecas Core:** `pypdf` (manipulação de estrutura/streams), `Pillow` (reamostragem de imagens JPEG/PNG)
|
||||||
|
- **Camada Web / GUI:** Streamlit (porta `8550`)
|
||||||
|
- **Camada API REST:** FastAPI + Uvicorn (porta `8555`)
|
||||||
|
- **Containerização:** Docker e Docker Compose (`Dockerfile.api`, `Dockerfile.web`, `docker-compose.yml`)
|
||||||
|
- **Testes Unitários:** Pytest
|
||||||
|
|
||||||
|
### Comandos de Execução Local:
|
||||||
|
```bash
|
||||||
|
# Instalação de dependências
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Executar Interface Web (Streamlit)
|
||||||
|
streamlit run app.py --server.port 8550
|
||||||
|
|
||||||
|
# Executar API REST (FastAPI com recarregamento dinâmico)
|
||||||
|
uvicorn api:app --host 0.0.0.0 --port 8555 --reload
|
||||||
|
|
||||||
|
# Executar suíte de testes unitários
|
||||||
|
pytest tests/ -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comandos Docker:
|
||||||
|
```bash
|
||||||
|
# Subir ambos os serviços (GUI + API)
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Visualizar logs
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Parar serviços
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 Diretrizes de Engenharia Locais
|
||||||
|
1. **Processamento In-Memory:** Todas as operações do `core/` devem operar recebendo `bytes` e devolvendo `bytes` via `io.BytesIO`. Não grave arquivos temporários em disco sem necessidade expressa.
|
||||||
|
2. **Proteção contra Inflação:** No pipeline de compressão, sempre compare o tamanho final gerado com o original. Caso o resultado seja maior, descarte a re-escrita e preserve os bytes originais (`reduced: False`).
|
||||||
|
3. **Desacoplamento Core / Interfaces:** Nenhuma lógica de negócio ou manipulação de PDF deve residir diretamente em `api.py` ou `app.py`. Ambas as interfaces devem chamar exclusivamente a fachada `PDFEngine` de `core/engine.py`.
|
||||||
|
4. **Validação Rigorosa:** Sempre validar o cabeçalho do arquivo (`validate_pdf`) antes de processar operações para evitar exceções não tratadas em arquivos corrompidos.
|
||||||
13
CLAUDE.md
Normal file
13
CLAUDE.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# CLAUDE.md — Diretrizes de Desenvolvimento (PDF Toolkit)
|
||||||
|
|
||||||
|
@~/AGENTS.md
|
||||||
|
|
||||||
|
## 📌 Contexto Rápido
|
||||||
|
- **Cliente:** CM Consultor (Interno)
|
||||||
|
- **Memória no Vault:** `$OBSIDIAN_VAULT_PATH/Projects/pdf-toolkit/Index.md`
|
||||||
|
- **Escopo e Fases:** `docs/escopo.md` e `docs/fases.md`
|
||||||
|
- **Repositório Gitea:** `https://gitea.cmconsultor.com.br/cmconsultor/pdf-toolkit`
|
||||||
|
|
||||||
|
## 🧪 Padrões de Testes & Execução
|
||||||
|
- Use `pytest` para testes unitários.
|
||||||
|
- Sempre processar PDFs em memória via `io.BytesIO`.
|
||||||
@@ -90,8 +90,11 @@ pdf-toolkit/
|
|||||||
│ └── run-api.sh # Inicia API
|
│ └── run-api.sh # Inicia API
|
||||||
├── tests/ # Testes unitários
|
├── tests/ # Testes unitários
|
||||||
├── docs/
|
├── docs/
|
||||||
|
│ ├── escopo.md # Especificação de escopo e anti-scope
|
||||||
|
│ ├── fases.md # Roadmap de execução e fases
|
||||||
│ ├── ARCHITECTURE.md # Detalhamento técnico
|
│ ├── ARCHITECTURE.md # Detalhamento técnico
|
||||||
│ └── API.md # Documentação da API
|
│ └── API.md # Documentação da API
|
||||||
|
├── AGENTS.md # Ponte de contexto para Agentes de IA e Obsidian
|
||||||
└── tmp/ # Arquivos temporários (gitignored)
|
└── tmp/ # Arquivos temporários (gitignored)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
12
core/pdfa.py
12
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:
|
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:
|
||||||
|
|||||||
50
docs/escopo.md
Normal file
50
docs/escopo.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# 📋 Especificação de Escopo — PDF Toolkit
|
||||||
|
|
||||||
|
## 1. Visão Geral do Produto & Personas
|
||||||
|
|
||||||
|
O **PDF Toolkit** é uma plataforma utilitária completa de processamento, otimização e saneamento de arquivos PDF desenvolvida para resolver as necessidades diárias de manipulação documental de forma rápida, privada e desacoplada de soluções em nuvem públicas de terceiros.
|
||||||
|
|
||||||
|
### Personas:
|
||||||
|
- **Consultores Técnicos & Usuários Administrativos:** Necessitam compactar arquivos pesados para envio por e-mail/WhatsApp, converter arquivos restritos no padrão PDF/A para PDF editável, rotacionar digitalizações e unir/separar relatórios através de uma interface web simples e interativa.
|
||||||
|
- **Desenvolvedores & Agentes Autônomos de IA:** Necessitam de uma API REST stateless de alto desempenho para submeter fluxos de arquivos e receber PDFs transformados ou dados extraídos (texto/imagens/metadados) em pipelines de automação.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Requisitos Funcionais do MVP (In-Scope)
|
||||||
|
|
||||||
|
1. **Otimização & Compressão Inteligente:**
|
||||||
|
- Compressão de arquivos através da reamostragem e re-compressão de imagens rasterizadas com algoritmo LANCZOS.
|
||||||
|
- Quatro presets pré-configurados: `screen` (30% / 1200px), `ebook` (60% / 2000px), `printer` (85% / 3500px) e `prepress` (95% / 5000px).
|
||||||
|
- Mecanismo de guarda anti-inflação: se a regravação gerar um arquivo maior, o sistema descarta e entrega o original intacto.
|
||||||
|
|
||||||
|
2. **Saneamento & Gestão de PDF/A:**
|
||||||
|
- Detecção automática de conformidade PDF/A via análise de metadados XMP (`pdfaid:part`/`conformance`) e `/OutputIntents`.
|
||||||
|
- Conversão e desregulamentação (`pdfa-to-regular`): remoção cirúrgica de restrições PDF/A mantendo a fidelidade visual das páginas.
|
||||||
|
- Compressão preservando a estrutura PDF/A (`compress-pdfa`).
|
||||||
|
|
||||||
|
3. **Operações Estruturais de Páginas:**
|
||||||
|
- **Merge (Concatenação):** Junção ordenada de múltiplos arquivos PDF com preservação de estrutura.
|
||||||
|
- **Split (Divisão):** Separação de documentos em intervalos de páginas configuráveis.
|
||||||
|
- **Extract Pages:** Extração pontual de listas de páginas (ex: 1, 3, 5-8) em um único arquivo de saída.
|
||||||
|
- **Rotate:** Rotação de páginas individuais ou em lote em ângulos de 90°, 180° ou 270°.
|
||||||
|
|
||||||
|
4. **Extração de Informações:**
|
||||||
|
- **Extração de Texto:** Extração de texto bruto indexável por página.
|
||||||
|
- **Extração de Imagens:** Localização e download de todas as imagens embutidas no documento no formato PNG/JPEG.
|
||||||
|
- **Inspeção de Metadados:** Análise de número de páginas, tamanho, integridade e diagnóstico de conformidade PDF/A.
|
||||||
|
|
||||||
|
5. **Interfaces & Acesso:**
|
||||||
|
- **Interface Web (Streamlit):** GUI em abas temáticas, drag-and-drop, sliders interativos e cálculo de delta de compressão.
|
||||||
|
- **API REST (FastAPI):** Endpoints assíncronos, documentação interativa Swagger (`/docs`) e Redoc (`/redoc`), suporte a autenticação por `X-API-Key`.
|
||||||
|
|
||||||
|
6. **Containerização & Deploy:**
|
||||||
|
- Empacotamento em Docker multi-container com `docker-compose` para execução simultânea da API (`:8555`) e da Web (`:8550`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Limites do MVP (Anti-Scope)
|
||||||
|
|
||||||
|
- ❌ **OCR (Optical Character Recognition) Complexo:** O MVP não executa OCR com Tesseract/EasyOCR em imagens escaneadas sem camada de texto (extração de texto limita-se ao texto vetorial/digital existente).
|
||||||
|
- ❌ **Linearização Binária True Fast Web View:** Não inclui reestruturação de baixo nível com dependências binárias C (qpdf/Ghostscript) para manter a portabilidade 100% Python.
|
||||||
|
- ❌ **Assinatura Digital Criptográfica / PKCS#7:** Não realiza assinatura de certificados A1/A3 (ICP-Brasil) ou aposição de carimbos do tempo nesta versão.
|
||||||
|
- ❌ **Persistência de Histórico de Arquivos em Banco de Dados:** A aplicação é 100% stateless; arquivos processados não são salvos em banco nem retidos após a entrega do stream.
|
||||||
88
docs/fases.md
Normal file
88
docs/fases.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# 🗺️ Roadmap de Execução & Fases — PDF Toolkit
|
||||||
|
|
||||||
|
Documento de acompanhamento do ciclo de vida e marcos do **PDF Toolkit**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 1: Arquitetura Base & Motor Orquestrador (`core/`)
|
||||||
|
- [x] Configuração central em `config.py` com limites de payload, presets e portas.
|
||||||
|
- [x] Criação do validador de integridade e magic bytes (`core/validators.py`).
|
||||||
|
- [x] Implementação da fachada orquestradora `PDFEngine` (`core/engine.py`).
|
||||||
|
- [x] Testes unitários do motor base com geração de PDFs sintéticos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 2: Módulo de Otimização & Compressão
|
||||||
|
- [x] Algoritmo de reamostragem de imagens via Pillow com filtro LANCZOS (`core/compress.py`).
|
||||||
|
- [x] Mapeamento dos 4 presets padrão (`screen`, `ebook`, `printer`, `prepress`).
|
||||||
|
- [x] Implementação de guarda defensiva anti-inflação de arquivo.
|
||||||
|
- [x] Coleta de métricas e estatísticas comparativas de redução.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 3: Detecção & Higienização de PDF/A
|
||||||
|
- [x] Detecção de nós XMP `<pdfaid:part>` / `conformance` via regex (`core/pdfa.py`).
|
||||||
|
- [x] Detecção de `/OutputIntents` no dicionário raiz.
|
||||||
|
- [x] Conversão de PDF/A para PDF comum (`strip_pdfa`) removendo restrições.
|
||||||
|
- [x] Compressão compatível com PDF/A (`compress_pdfa`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 4: Operações Estruturais & Extração
|
||||||
|
- [x] Concatenação de múltiplos arquivos (`merge_pdfs` em `core/operations.py`).
|
||||||
|
- [x] Divisão por intervalos de páginas (`split_pdf`).
|
||||||
|
- [x] Extração de páginas específicas (`extract_pages`).
|
||||||
|
- [x] Rotação de páginas em 90°, 180° e 270° (`rotate_pages`).
|
||||||
|
- [x] Extração de texto bruto (`extract_text`).
|
||||||
|
- [x] Extração de imagens embutidas (`extract_images`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 5: Interface Web Interativa (Streamlit)
|
||||||
|
- [x] Criação da aplicação Streamlit (`app.py`).
|
||||||
|
- [x] Aba de compressão com slider de preset e cálculo de economia percentual.
|
||||||
|
- [x] Aba de conversão e saneamento de PDF/A.
|
||||||
|
- [x] Abas de operações estruturais (Merge, Split, Extract, Rotate).
|
||||||
|
- [x] Aba de extração de texto e imagens com download imediato.
|
||||||
|
- [x] Aba de inspeção e metadados com badge de conformidade.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 6: API REST & Documentação OpenAPI (FastAPI)
|
||||||
|
- [x] Criação dos endpoints em `api.py` correspondentes a todas as operações.
|
||||||
|
- [x] Documentação automática Swagger UI (`/docs`) e Redoc (`/redoc`).
|
||||||
|
- [x] Middleware CORS para conexões externas.
|
||||||
|
- [x] Autenticação opcional por header `X-API-Key`.
|
||||||
|
- [x] Endpoint de monitoramento de integridade (`GET /health`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 7: Containerização & Scripts de Execução
|
||||||
|
- [x] Criação de `Dockerfile.web` (Streamlit) e `Dockerfile.api` (FastAPI).
|
||||||
|
- [x] Configuração de `docker-compose.yml` para orquestração de ambos os serviços.
|
||||||
|
- [x] Scripts de inicialização Linux `scripts/run-web.sh` e `scripts/run-api.sh`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 8: Testes Automatizados & Qualidade
|
||||||
|
- [x] Criação da suíte de testes em `tests/test_core.py`.
|
||||||
|
- [x] Validação de compressão, detecção PDF/A, rotação, extração e merge.
|
||||||
|
- [x] Verificação de integridade dos streams gerados.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Fase 9: Integração com Obsidian Vault & Memória Técnica
|
||||||
|
- [x] Criação da ficha mestra `00_Index.md` em `03_Dev_Projetos/PDF_Toolkit/`.
|
||||||
|
- [x] Criação do documento `padroes-arquitetura.md` no cofre.
|
||||||
|
- [x] Criação do registro `gotchas-e-bugs.md` no cofre.
|
||||||
|
- [x] Documentação de Architecture Decision Records (ADR-001, ADR-002, ADR-003).
|
||||||
|
- [x] Criação do arquivo de ponte para agentes `AGENTS.md` na raiz do repositório.
|
||||||
|
- [x] Especificação de escopo em `docs/escopo.md` e roadmap em `docs/fases.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 Fase 10: Próximas Evoluções (Backlog Futuro)
|
||||||
|
- [ ] Módulo opcional de OCR com suporte a Tesseract para PDFs digitalizados.
|
||||||
|
- [ ] Processamento assíncrono em segundo plano com Celery / Redis para lotes maciços (> 50 arquivos).
|
||||||
|
- [ ] Suporte a Linearização ("Fast Web View") via integração opcional com binário `qpdf`.
|
||||||
|
- [ ] Endpoint para assinatura de carimbo d'água personalizado com transparência.
|
||||||
Reference in New Issue
Block a user