|
|
<!DOCTYPE html> |
|
|
<html lang="pt-BR"> |
|
|
<head> |
|
|
<meta charset="UTF-8" /> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
|
|
<title>Editor de PDF</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.14.305/pdf.min.js"></script> |
|
|
<style> |
|
|
canvas { |
|
|
border: 1px solid #ccc; |
|
|
margin: 10px auto; |
|
|
cursor: crosshair; |
|
|
display: block; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-100 text-gray-800 min-h-screen p-6"> |
|
|
|
|
|
<div class="max-w-4xl mx-auto bg-white shadow-lg rounded-lg p-6"> |
|
|
<h1 class="text-3xl font-bold mb-6 text-center">📄 Editor de PDF</h1> |
|
|
|
|
|
<input type="file" id="pdfInput" accept="application/pdf" class="mb-4 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100" /> |
|
|
|
|
|
<div class="flex flex-wrap gap-4 justify-center mb-4"> |
|
|
<button id="prevPage" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">⬅️ Página Anterior</button> |
|
|
<button id="nextPage" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">➡️ Próxima Página</button> |
|
|
<span id="pageInfo" class="text-lg self-center font-medium">Página 1</span> |
|
|
</div> |
|
|
|
|
|
<div class="flex flex-wrap gap-3 justify-center mb-4"> |
|
|
<button id="addTextBtn" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">✍️ Adicionar Texto</button> |
|
|
<button id="addRectBtn" class="bg-yellow-500 text-white px-4 py-2 rounded hover:bg-yellow-600">🟦 Adicionar Retângulo</button> |
|
|
<button id="clearBtn" class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600">🧹 Limpar Anotações</button> |
|
|
<button id="downloadBtn" class="bg-purple-500 text-white px-4 py-2 rounded hover:bg-purple-600">⬇️ Baixar como PNG</button> |
|
|
</div> |
|
|
|
|
|
<canvas id="pdfCanvas" class="bg-white shadow-md mx-auto"></canvas> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
const fileInput = document.getElementById('pdfInput'); |
|
|
const canvas = document.getElementById('pdfCanvas'); |
|
|
const ctx = canvas.getContext('2d'); |
|
|
const pageInfo = document.getElementById('pageInfo'); |
|
|
const addTextBtn = document.getElementById('addTextBtn'); |
|
|
const addRectBtn = document.getElementById('addRectBtn'); |
|
|
const clearBtn = document.getElementById('clearBtn'); |
|
|
const downloadBtn = document.getElementById('downloadBtn'); |
|
|
const prevPageBtn = document.getElementById('prevPage'); |
|
|
const nextPageBtn = document.getElementById('nextPage'); |
|
|
|
|
|
let pdfDoc = null; |
|
|
let currentPage = 1; |
|
|
let isAddingText = false; |
|
|
let isAddingRect = false; |
|
|
let tempRectStart = null; |
|
|
|
|
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.14.305/pdf.worker.min.js'; |
|
|
|
|
|
fileInput.addEventListener('change', async (e) => { |
|
|
const file = e.target.files[0]; |
|
|
if (!file) return; |
|
|
const fileReader = new FileReader(); |
|
|
fileReader.onload = async () => { |
|
|
pdfDoc = await pdfjsLib.getDocument({ data: fileReader.result }).promise; |
|
|
currentPage = 1; |
|
|
renderPage(currentPage); |
|
|
}; |
|
|
fileReader.readAsBinaryString(file); |
|
|
}); |
|
|
|
|
|
function renderPage(pageNumber) { |
|
|
if (!pdfDoc) return; |
|
|
pdfDoc.getPage(pageNumber).then(async (page) => { |
|
|
const viewport = page.getViewport({ scale: 1.5 }); |
|
|
canvas.width = viewport.width; |
|
|
canvas.height = viewport.height; |
|
|
await page.render({ canvasContext: ctx, viewport }).promise; |
|
|
pageInfo.textContent = `Página ${pageNumber} de ${pdfDoc.numPages}`; |
|
|
}); |
|
|
} |
|
|
|
|
|
prevPageBtn.addEventListener('click', () => { |
|
|
if (currentPage > 1) { |
|
|
currentPage--; |
|
|
renderPage(currentPage); |
|
|
} |
|
|
}); |
|
|
|
|
|
nextPageBtn.addEventListener('click', () => { |
|
|
if (currentPage < pdfDoc?.numPages) { |
|
|
currentPage++; |
|
|
renderPage(currentPage); |
|
|
} |
|
|
}); |
|
|
|
|
|
addTextBtn.addEventListener('click', () => { |
|
|
isAddingText = true; |
|
|
isAddingRect = false; |
|
|
alert('Clique no local onde deseja inserir o texto.'); |
|
|
}); |
|
|
|
|
|
addRectBtn.addEventListener('click', () => { |
|
|
isAddingRect = true; |
|
|
isAddingText = false; |
|
|
alert('Clique e arraste para desenhar um retângulo.'); |
|
|
}); |
|
|
|
|
|
canvas.addEventListener('click', (e) => { |
|
|
if (isAddingText) { |
|
|
const rect = canvas.getBoundingClientRect(); |
|
|
const x = e.clientX - rect.left; |
|
|
const y = e.clientY - rect.top; |
|
|
const text = prompt("Digite o texto:"); |
|
|
if (text) { |
|
|
ctx.fillStyle = 'red'; |
|
|
ctx.font = '20px Arial'; |
|
|
ctx.fillText(text, x, y); |
|
|
} |
|
|
isAddingText = false; |
|
|
} |
|
|
}); |
|
|
|
|
|
canvas.addEventListener('mousedown', (e) => { |
|
|
if (isAddingRect) { |
|
|
const rect = canvas.getBoundingClientRect(); |
|
|
tempRectStart = { |
|
|
x: e.clientX - rect.left, |
|
|
y: e.clientY - rect.top |
|
|
}; |
|
|
} |
|
|
}); |
|
|
|
|
|
canvas.addEventListener('mouseup', (e) => { |
|
|
if (isAddingRect && tempRectStart) { |
|
|
const rect = canvas.getBoundingClientRect(); |
|
|
const endX = e.clientX - rect.left; |
|
|
const endY = e.clientY - rect.top; |
|
|
const width = endX - tempRectStart.x; |
|
|
const height = endY - tempRectStart.y; |
|
|
|
|
|
ctx.strokeStyle = 'blue'; |
|
|
ctx.lineWidth = 2; |
|
|
ctx.strokeRect(tempRectStart.x, tempRectStart.y, width, height); |
|
|
|
|
|
tempRectStart = null; |
|
|
isAddingRect = false; |
|
|
} |
|
|
}); |
|
|
|
|
|
clearBtn.addEventListener('click', () => { |
|
|
renderPage(currentPage); |
|
|
}); |
|
|
|
|
|
downloadBtn.addEventListener('click', () => { |
|
|
const link = document.createElement('a'); |
|
|
link.download = `pagina_${currentPage}.png`; |
|
|
link.href = canvas.toDataURL(); |
|
|
link.click(); |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |