import React, { useState, useCallback } from 'react'; import { useDropzone } from 'react-dropzone'; import axios from 'axios'; import { toast } from 'react-toastify'; import { useAuth } from '../../contexts/AuthContext'; import { DocumentIcon, CloudArrowUpIcon, ArrowsPointingInIcon, ScissorsIcon, ArrowsUpDownIcon, PhotoIcon, DocumentTextIcon, InformationCircleIcon, ArrowDownTrayIcon, ClockIcon, } from '@heroicons/react/24/outline'; interface OperationResult { operacaoId: string; outputPaths?: string[]; outputPath?: string; processingTime?: number; reduction?: number; filesCreated?: number; text?: string; confidence?: number; } type OperationType = | 'compress' | 'split' | 'merge' | 'pdfToImages' | 'imagesToPDF' | 'ocr' | 'info'; export const PDFManipulationPage: React.FC = () => { const { getToken } = useAuth(); const [selectedOperation, setSelectedOperation] = useState('compress'); const [files, setFiles] = useState([]); const [processing, setProcessing] = useState(false); const [result, setResult] = useState(null); const [history, setHistory] = useState([]); // Configurações para cada operação const [compressQuality, setCompressQuality] = useState(95); const [splitType, setSplitType] = useState<'pages' | 'range' | 'chunks'>('pages'); const [splitPages, setSplitPages] = useState('1,2,3'); const [splitRanges, setSplitRanges] = useState('1-5,6-10'); const [chunkSize, setChunkSize] = useState(5); const [imageFormat, setImageFormat] = useState<'png' | 'jpg' | 'webp'>('png'); const [imageQuality, setImageQuality] = useState(95); const [imageDensity, setImageDensity] = useState(300); const [ocrLanguage, setOcrLanguage] = useState('por'); const onDrop = useCallback((acceptedFiles: File[]) => { setFiles((prev) => [...prev, ...acceptedFiles]); }, []); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, accept: { 'application/pdf': ['.pdf'], 'image/*': ['.png', '.jpg', '.jpeg', '.webp'], }, }); const removeFile = (index: number) => { setFiles((prev) => prev.filter((_, i) => i !== index)); }; const clearFiles = () => { setFiles([]); setResult(null); }; const processFiles = async () => { if (files.length === 0) { toast.error('Selecione pelo menos um arquivo'); return; } setProcessing(true); setResult(null); try { const formData = new FormData(); files.forEach((file) => { formData.append('files', file); }); const token = getToken(); const config = { headers: { 'Content-Type': 'multipart/form-data', Authorization: `Bearer ${token}`, }, }; let response; switch (selectedOperation) { case 'compress': formData.append('quality', compressQuality.toString()); response = await axios.post('/api/pdf/compress', formData, config); toast.success('PDFs comprimidos com sucesso!'); break; case 'split': formData.append('splitType', splitType); if (splitType === 'pages') { const pages = splitPages.split(',').map((p) => parseInt(p.trim())); formData.append('pages', JSON.stringify(pages)); } else if (splitType === 'range') { const ranges = splitRanges.split(',').map((r) => { const [start, end] = r.trim().split('-').map(Number); return { start, end }; }); formData.append('ranges', JSON.stringify(ranges)); } else { formData.append('chunkSize', chunkSize.toString()); } response = await axios.post('/api/pdf/split', formData, config); toast.success('PDF dividido com sucesso!'); break; case 'merge': response = await axios.post('/api/pdf/merge', formData, config); toast.success('PDFs mesclados com sucesso!'); break; case 'pdfToImages': formData.append('format', imageFormat); formData.append('quality', imageQuality.toString()); formData.append('density', imageDensity.toString()); response = await axios.post('/api/pdf/pdf-to-images', formData, config); toast.success('PDF convertido em imagens!'); break; case 'imagesToPDF': formData.append('fitToPage', 'true'); response = await axios.post('/api/pdf/images-to-pdf', formData, config); toast.success('Imagens convertidas em PDF!'); break; case 'ocr': formData.append('language', ocrLanguage); response = await axios.post('/api/pdf/ocr', formData, config); toast.success('OCR realizado com sucesso!'); break; case 'info': response = await axios.post('/api/pdf/info', formData, config); break; } setResult(response.data); loadHistory(); } catch (error: any) { console.error('Erro ao processar arquivos:', error); toast.error(error.response?.data?.message || 'Erro ao processar arquivos'); } finally { setProcessing(false); } }; const loadHistory = async () => { try { const token = getToken(); const response = await axios.get('/api/pdf/history', { headers: { Authorization: `Bearer ${token}` }, }); setHistory(response.data.operacoes || []); } catch (error) { console.error('Erro ao carregar histórico:', error); } }; const downloadFile = async (operacaoId: string, fileIndex: number) => { try { const token = getToken(); const response = await axios.get(`/api/pdf/download/${operacaoId}/${fileIndex}`, { headers: { Authorization: `Bearer ${token}` }, responseType: 'blob', }); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', `arquivo-${fileIndex}.pdf`); document.body.appendChild(link); link.click(); link.remove(); toast.success('Download iniciado!'); } catch (error) { console.error('Erro ao fazer download:', error); toast.error('Erro ao fazer download'); } }; React.useEffect(() => { loadHistory(); }, []); const operations = [ { id: 'compress' as OperationType, name: 'Comprimir PDF', icon: ArrowsPointingInIcon, description: 'Reduza o tamanho mantendo a qualidade', }, { id: 'split' as OperationType, name: 'Dividir PDF', icon: ScissorsIcon, description: 'Separe páginas específicas', }, { id: 'merge' as OperationType, name: 'Mesclar PDFs', icon: ArrowsUpDownIcon, description: 'Combine múltiplos PDFs', }, { id: 'pdfToImages' as OperationType, name: 'PDF para Imagens', icon: PhotoIcon, description: 'Converta páginas em imagens', }, { id: 'imagesToPDF' as OperationType, name: 'Imagens para PDF', icon: DocumentIcon, description: 'Crie PDF a partir de imagens', }, { id: 'ocr' as OperationType, name: 'OCR', icon: DocumentTextIcon, description: 'Extraia texto de PDFs', }, { id: 'info' as OperationType, name: 'Informações', icon: InformationCircleIcon, description: 'Obtenha detalhes do PDF', }, ]; const formatBytes = (bytes: number) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; }; return (

Manipulação de PDFs

Ferramentas completas para trabalhar com arquivos PDF

{/* Seleção de operação */}
{operations.map((op) => ( ))}
{/* Área principal */}
{/* Upload de arquivos */}

Arquivos

{isDragActive ? (

Solte os arquivos aqui...

) : (

Arraste e solte arquivos aqui, ou clique para selecionar

PDFs e imagens (PNG, JPG, WEBP)

)}
{/* Lista de arquivos */} {files.length > 0 && (

Arquivos selecionados ({files.length})

{files.map((file, index) => (

{file.name}

{formatBytes(file.size)}

))}
)}
{/* Configurações específicas */}

Configurações

{selectedOperation === 'compress' && (
setCompressQuality(parseInt(e.target.value))} className="w-full" />

95% recomendado para qualidade sem perda perceptível

)} {selectedOperation === 'split' && (
{splitType === 'pages' && (
setSplitPages(e.target.value)} placeholder="1,2,3,5,8" className="w-full border border-gray-300 rounded-lg px-3 py-2" />
)} {splitType === 'range' && (
setSplitRanges(e.target.value)} placeholder="1-5,6-10,11-15" className="w-full border border-gray-300 rounded-lg px-3 py-2" />
)} {splitType === 'chunks' && (
setChunkSize(parseInt(e.target.value))} min="1" className="w-full border border-gray-300 rounded-lg px-3 py-2" />
)}
)} {selectedOperation === 'pdfToImages' && (
setImageQuality(parseInt(e.target.value))} className="w-full" />
setImageDensity(parseInt(e.target.value))} className="w-full" />
)} {selectedOperation === 'ocr' && (
)}
{/* Resultado */} {result && (

Resultado

{selectedOperation === 'compress' && result.reduction !== undefined && (

Redução de {result.reduction.toFixed(2)}%

Tempo de processamento: {result.processingTime}ms

)} {(result.outputPaths || result.outputPath) && (

Arquivos gerados:

{result.outputPaths?.map((path, index) => ( ))} {result.outputPath && ( )}
)} {selectedOperation === 'ocr' && result.text && (

Texto extraído:

{result.text}
{result.confidence && (

Confiança: {result.confidence.toFixed(2)}%

)}
)}
)}
{/* Histórico */}

Histórico

{history.length === 0 ? (

Nenhuma operação ainda

) : (
{history.map((op) => (
{op.tipoOperacao.replace('_', ' ')} {op.status}
{new Date(op.criadoEm).toLocaleString('pt-BR')}
{op.tempoProcessamento && (

{op.tempoProcessamento}ms

)} {op.status === 'CONCLUIDO' && op.arquivosResultado && op.arquivosResultado.length > 0 && (
{op.arquivosResultado.map((arquivo: string, index: number) => ( ))}
)}
))}
)}
); };