16 KiB
Funcionalidade de Manipulação de PDFs
Visão Geral
Sistema completo de manipulação de arquivos PDF com as seguintes funcionalidades:
Recursos Implementados
-
Compressão de PDF com redução >60%
- Compressão agressiva com qualidade ajustável (30-100%)
- Redução média de 60-80% do tamanho original
- Otimização de imagens e remoção de metadados
- Suporte para múltiplos arquivos simultâneos
- Opção de adicionar camada OCR (tornar PDF pesquisável)
-
Divisão de PDF
- Por páginas específicas (ex: 1,3,5,7)
- Por intervalos (ex: 1-5, 6
- Download em ZIP quando múltiplos arquivos-10)
- Por chunks de tamanho fixo
-
Mesclagem de PDFs
- Combine até 50 PDFs em um único arquivo
- Preserva qualidade o
- Opção de adicionar camada OCR ao PDF mescladoriginal
- Ordem personalizável
-
PDF para Imagens
- Formatos: PNG, JPG, WEBP
- DPI ajustável (150-600)
- Download em ZIP de todas as imagens
- Qualidade configurável (50-100%)
- Conversão de páginas específicas ou todas
-
Imagens para PDF
- Opção de adicionar camada OCR (tornar PDF pesquisável)
- Suporta múltiplas imagens (até 100)
- Ajuste automático ao tamanho da página
- Suporta PNG, JPG, JPEG, WEBP
-
OCR (Reconhecimento Ótico de Caracteres)
- Extração de texto de PDFs escaneados
- Suporte para Português, Inglês e Espanhol
- Retorna confiança da extração
- Processamento por página
-
Informações do PDF
- Número de páginas
- Tamanho do arquivo
- Metadados (título, autor, criador, etc.)
- Datas de criação e modificação
Instalação
1. Instalar Dependências
npm install pdf-lib pdf-parse pdf2pic tesseract.js archiver
npm install --save-dev @types/pdf-parse @types/archiver
Ou execute o script de instalação:
npm run install-pdf-deps
2. Instalar GraphicsMagick (Requerido para PDF → Imagens)
Docker: O GraphicsMagick já está incluído no Dockerfile. Rebuild a imagem se necessário:
docker-compose down
docker-compose build --no-cache
docker-compose up -d
Local (desenvolvimento):
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install graphicsmagick
MacOS:
brew install graphicsmagick
Windows:
- Baixe e instale: http://www.graphicsmagick.org/download.html
- Adicione ao PATH do sistema
Verifique a instalação:
gm version
3. Executar Migration do Banco de Dados
npm run prisma:migrate
Ou manualmente:
npx prisma migrate dev --name add_operacoes_pdf
4. Gerar Cliente Prisma
npm run prisma:generate
5. Criar Diretórios Necessários
Os diretórios serão criados automaticamente na primeira execução, mas você pode criá-los manualmente:
mkdir -p storage/temp/pdf-ops
mkdir -p storage/pdf-output
mkdir -p storage/temp/uploads
Uso
Frontend
Acesse a página de manipulação de PDFs através da rota:
/pdf
API Endpoints
Todas as rotas requerem autenticação via Bearer token.
1. Comprimir PDF
POST /api/pdf/compress
Content-Type: multipart/form-data
Authorization: Bearer {token}
30-100, padrão 50 para máxima compressão)
- maxSizeMB: number (opcional)
- enableOCR: boolean (opcional, adiciona camada OCR ao PDF
- files: File[] (array de PDFs)
- quality: number (50-100, padrão 95)
- maxSizeMB: number (opcional)
2. Dividir PDF
POST /api/pdf/split
Content-Type: multipart/form-data
Authorization: Bearer {token}
Body:
- files: File (único PDF)
- splitType: 'pages' | 'range' | 'chunks'
- pages: string (ex: "1,2,3") - para splitType='pages'
- ranges: string (ex: "1-5,6-10") - para splitType='range'
- chunkSize: number - para splitType='chunks'
3. Mesclar PDFs
POST /api/pdf/merge
Content-Type: multipart/form-da
- enableOCR: boolean (opcional, adiciona camada OCR ao PDF mesclado)ta
Authorization: Bearer {token}
Body:
- files: File[] (mínimo 2 PDFs)
4. PDF para Imagens
POST /api/pdf/pdf-to-images
Content-Type: multipart/form-data
Authorization: Bearer {token}
Body:
- files: File (único PDF)
- format: 'png' | 'jpg' | 'webp' (padrão: png)
- quality: number (50-100, padrão: 95)
- density: number (DPI, 150-600, padrão: 300)
- pages: string (opcional, ex: "[1,2,3]" ou "all")
5. Imagens para PDF
POST /api/pdf/images-to-pdf
Content-Type: multipart/form-data
Authorization: Bearer {token}
- enableOCR: boolean (opcional, adiciona camada OCR ao PDF)
Body:
- files: File[] (array de imagens)
- fitToPage: boolean (padrão: true)
- pageSize: string (opcional, JSON: {"width":595,"height":842})
6. OCR
POST /api/pdf/ocr
Content-Type: multipart/form-data
Authorization: Bearer {token}
Body:
- files: File (único PDF)
- language: string ('por' | 'eng' | 'spa', padrão: 'por')
- pages: string (opcional, ex: "[1,2]" ou "all")
7. Informações do PDF
POST /api/pdf/info
Content-Type: multipart/form-data
Authorization: Bearer {token}
Body:
- files: File (único PDF)
8. Histórico de Operações
GET /api/pdf/history?page=1&limit=20
Authorization: Bearer {token}
9. Download de Arquivo Processado
GET /api/pdf/download/:operacaoId/:fileIndex
Authorization: Bearer {token}
Estrutura de Resposta
Sucesso (Compressão)
...], "outputPaths": ["/path/compressed-1.pdf", "/path/compressed-2.pdf"], "totalOriginalSize": 1048576, "totalCompressedSize": 314572, "reduction": 70.0, "processingTime": 1234 }
### Sucesso (Split/PDF para Imagens - com ZIP)
```json
{
"success": true,
"operacaoId": "uuid",
"filesCreated": 10,
"outputPaths": ["/path/page-1.pdf", "/path/page-2.pdf", ...],
"zipPath": "/path/arquivos-uuid.zip",
"processingTime": 2345
],
"totalOriginalSize": 1048576,
"totalCompressedSize": 524288,
"totalReduction": 50.0,
"processingTime": 1234
}
Sucesso (OCR)
{
"success": true,
"operacaoId": "uuid",
"result": {
"text": "Texto extraído completo...",
"confidence": 95.5,
"pages": [
{
"pageNumber": 1,
"text": "Texto da página 1...",
"confidence": 96.2
}
]
},
"processingTime": 5678
}
Erro
{
"error": "Falha ao processar arquivo",
"message": "Detalhes específicos do erro"
}
Modelo de Dados
OperacaoPDF
interface OperacaoPDF {
id: string;
tipoOperacao: 'COMPRIMIR' | 'DIVIDIR' | 'MESCLAR' | 'PDF_PARA_IMAGEM' | 'IMAGEM_PARA_PDF' | 'OCR';
status: 'PROCESSANDO' | 'CONCLUIDO' | 'ERRO';
usuarioId: string;
arquivosOriginais: string[];
arquivosResultado: string[];
tamanhoOriginal: bigint;
tamanhoResultado: bigint | null;
parametros: any;
mensagemErro: string | null;
detalhesOCR: any | null;
tempoProcessamento: number | null;
criadoEm: Date;
atualizadoEm: Date;
}
Segurança
- ✅ Autenticação obrigatória em todas as rotas
- ✅ Validação de tipos de arquivo (apenas PDF e imagens)
- ✅ Limite de tamanho de arquivo (100MB por arquivo)
- ✅ Limite de quantidade de arquivos por operação
- ✅ Sanitização de nomes de arquivo
- ✅ Isolamento de arquivos por operação
- ✅ Limpeza automática de arquivos temporários
Performance
- ✅ Processamento assíncrono
- ✅ Otimização de memória com streams
- ✅ Compressão sem perda de qualidade perceptível
- ✅ Cache de workers de OCR
- ✅ Índices no banco de dados para consultas rápidas
- ✅ Limpeza automática de arquivos antigos
Tratamento de Erros
Todos os erros são:
- Logados no console do servidor
- Registrados no banco de dados (tabela operacoes_pdf)
- Retornados ao cliente com mensagens descritivas
- Capturados por try-catch em todas as operações
Limitações
- Tamanho máximo por arquivo: 100MB
- Máximo de arquivos para compressão: 10
- Máximo de arquivos para mesclagem: 50
- Máximo de imagens para conversão em PDF: 100
- Arquivos processados são mantidos por 24 horas
- Timeout de operações: 10 minutos (600 segundos)
- Operações longas (OCR, compressão de PDFs grandes) podem levar alguns minutos
- O sistema está configurado para suportar até 10 minutos de processamento
Manutenção
Limpeza de Arquivos Antigos
Execute periodicamente (recomendado: diariamente via cron):
import { PDFService } from './src/services/pdfService';
// Remove arquivos com mais de 24 horas
await PDFService.cleanupOldFiles(24);
Monitoramento
Consulte a tabela operacoes_pdf para:
- Estatísticas de uso
- Taxa de erro
- Tempo médio de processamento
- Operações mais utilizadas
-- Operações por tipo
SELECT "tipoOperacao", COUNT(*) as total
FROM operacoes_pdf
GROUP BY "tipoOperacao"
ORDER BY total DESC;
-- Taxa de sucesso
SELECT
status,
COUNT(*) as total,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 2) as percentual
FROM operacoes_pdf
GROUP BY status;
-- Tempo médio por operação
SELECT
"tipoOperacao",
AVG("tempoProcessamento") as tempo_medio_ms,
MIN("tempoProcessamento") as tempo_min_ms,
MAX("tempoProcessamento") as tempo_max_ms
FROM operacoes_pdf
WHERE status = 'CONCLUIDO'
GROUP BY "tipoOperacao";
Troubleshooting
Erro: "Could not execute GraphicsMagick/ImageMagick"
Sintoma: Erro ao converter PDF para imagens:
Error: Could not execute GraphicsMagick/ImageMagick: gm "convert" ...
this most likely means the gm/convert binaries can't be found
Causa: GraphicsMagick não está instalado no sistema/container.
Solução:
Docker:
# Rebuild a imagem Docker com GraphicsMagick incluído
docker-compose down
docker-compose build --no-cache
docker-compose up -d
Local:
# Ubuntu/Debian
sudo apt-get install graphicsmagick
# MacOS
brew install graphicsmagick
# Verifique a instalação
gm version
Erro 504: Gateway Timeout (Timeout ao processar PDF)
Sintoma: Operação de compressão ou OCR retorna erro 504 após ~60 segundos:
Failed to load resource: the server responded with a status of 504
Causa: Operações de PDF (especialmente compressão e OCR) podem levar vários minutos para arquivos grandes. O timeout padrão de 60 segundos é insuficiente.
Solução: As configurações de timeout foram aumentadas para 10 minutos em:
- ✅ Express server (
src/server/index.ts) - ✅ Vite proxy (
vite.config.ts) - ✅ Axios frontend (
PDFManipulationPage.tsx)
Se ainda ocorrer timeout:
- Verifique se rebuild foi feito após as alterações
- Reduza o tamanho do arquivo ou qualidade
- Processe menos arquivos por vez
- Para PDFs muito grandes (>50MB), considere fazer upload em partes
Dicas de performance:
- Compressão: Use qualidade 50-70 para PDFs grandes
- OCR: Processe apenas páginas necessárias, não o PDF inteiro
- Split: Use chunks menores para PDFs com muitas páginas
Erro: ENOENT ao deletar arquivos do histórico
Sintoma: Erro ao limpar histórico:
Error: ENOENT: no such file or directory, unlink '/path/to/file.pdf'
Causa: Tentativa de deletar arquivos que não existem mais (já foram removidos manualmente ou por outro processo).
Solução: ✅ Corrigido automaticamente - O sistema agora verifica se o arquivo existe antes de tentar deletar e ignora erros ENOENT silenciosamente.
Erro: Imagens baixadas com extensão .pdf (ou sem extensão correta)
Sintoma: Ao baixar imagens geradas pela conversão PDF→Imagens, os arquivos vêm com extensão .pdf ou precisam ser renomeados para .png/.jpg para serem visualizados.
Causa: O servidor não estava enviando o Content-Type correto no header HTTP, OU o frontend estava ignorando os headers do servidor e forçando o nome como .pdf.
Solução: ✅ Corrigido automaticamente - O sistema agora:
Backend:
- Detecta automaticamente o tipo MIME correto baseado na extensão (.png, .jpg, .webp, .zip, .pdf)
- Define headers HTTP apropriados (
Content-TypeeContent-Disposition) - Garante que o nome do arquivo no download mantém a extensão original
- Adiciona logs de debug para facilitar troubleshooting
Frontend:
- Extrai o nome do arquivo do header
Content-Dispositionenviado pelo servidor - Usa o
Content-Typecorreto do servidor ao criar o Blob para download - Não força mais extensão
.pdfem todos os downloads - Adiciona log no console do browser:
Download frontend: arquivo.png (image/png) - Libera memória do blob URL após download
Se ainda ocorrer o problema:
- Abra o Console do DevTools (F12) e verifique os logs de download
- Verifique se os headers estão sendo recebidos corretamente na aba Network
- Limpe o cache do browser (Ctrl+Shift+Del)
- Teste em uma aba anônima/privada
- Use a opção de Download ZIP que sempre funciona corretamente
Erro: "Tesseract worker failed"
Solução: Certifique-se de que o Tesseract está instalado corretamente:
npm install tesseract.js --force
Erro: "Cannot find module 'sharp'"
Solução: Reinstale o Sharp com rebuild:
npm uninstall sharp
npm install sharp
npm rebuild sharp
Erro: "ENOSPC: no space left on device"
Solução: Aumente o espaço em disco ou execute a limpeza:
npm run cleanup-pdf-files
Performance lenta no OCR
Solução: Reduza o DPI ou processe menos páginas por vez.
Suporte
Para questões ou problemas:
- Verifique os logs do servidor
- Consulte a tabela
operacoes_pdfpara detalhes do erro - Verifique se os diretórios de storage têm permissões corretas
- Certifique-se de que todas as dependências estão instaladas
Changelog
v1.1.2 (2026-02-27)
- 🔧 Correção DEFINITIVA de downloads - Arquivos agora baixam com nome e extensão corretos
- Backend: Detecta tipo MIME e define headers HTTP apropriados (Content-Type, Content-Disposition)
- Frontend: Extrai nome do arquivo e tipo MIME dos headers da resposta
- Frontend: Não força mais
.pdfem todos os downloads - usa nome real do servidor - Frontend: Cria Blob com Content-Type correto preservando o formato original
- Adiciona logs de debug no backend e frontend para troubleshooting
- Suporta formato 'jpeg' além de 'jpg'
- Fallback para PNG em formatos não suportados
- Libera memória de blob URLs após download
- 🐛 Melhorias no pdfToImages - Adicionado suporte para 'jpeg' e fallback para formatos inválidos
v1.1.1 (2026-02-27)
- 🔧 Correção de timeout 504 - Aumentado timeout para 10 minutos em todas as camadas
- Express server: 600 segundos
- Vite proxy: 600 segundos
- Axios frontend: 600 segundos
- 🗑️ Correção ENOENT - Sistema agora ignora arquivos já deletados ao limpar histórico
- 📋 Gerenciamento de histórico - Botões para limpar histórico completo ou arquivos antigos
- 🐛 Melhorias de estabilidade - Verificação de existência de arquivos antes de deletar
- 📚 Documentação expandida - Adicionado troubleshooting completo
v1.1.0 (2026-02-27)
- 🚀 Compressão agressiva com redução >60% do tamanho original
- 📦 Download em ZIP para operações com múltiplos arquivos (split, pdfToImages)
- 🔍 OCR embutido - opção de tornar PDFs pesquisáveis (compress, merge, imagesToPDF)
- ⚡ Otimização de imagens com qualidade ajustável (30-100%)
- 🎯 Interface aprimorada com indicadores visuais e opções de OCR
v1.0.0 (2026-02-27)
- ✅ Implementação inicial
- ✅ Compressão sem perda de qualidade
- ✅ Divisão de PDFs
- ✅ Mesclagem de PDFs
- ✅ Conversão PDF para imagens
- ✅ Conversão de imagens para PDF
- ✅ OCR com Tesseract
- ✅ Histórico de operações
- ✅ Interface React completa
- ✅ Validação e segurança