Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', function() { | |
| // DOM elements | |
| const fileInput = document.getElementById('file-input'); | |
| const uploadBtn = document.getElementById('upload-btn'); | |
| const dropZone = document.getElementById('drop-zone'); | |
| const imagePreview = document.getElementById('image-preview'); | |
| const imagePreviewContainer = document.getElementById('image-preview-container'); | |
| const emptyState = document.getElementById('empty-state'); | |
| const pageCountInput = document.getElementById('page-count'); | |
| const pageCountValue = document.getElementById('page-count-value'); | |
| const portraitBtn = document.getElementById('portrait'); | |
| const landscapeBtn = document.getElementById('landscape'); | |
| const previewControls = document.getElementById('preview-controls'); | |
| const generateBtn = document.getElementById('generate-btn'); | |
| const resultSection = document.getElementById('result-section'); | |
| const posterPages = document.getElementById('poster-pages'); | |
| const downloadAllBtn = document.getElementById('download-all'); | |
| const printAllBtn = document.getElementById('print-all'); | |
| // Current image and settings | |
| let currentImage = null; | |
| let currentOrientation = 'portrait'; | |
| let currentPageCount = 4; | |
| // Event listeners | |
| uploadBtn.addEventListener('click', () => fileInput.click()); | |
| fileInput.addEventListener('change', handleFileSelect); | |
| dropZone.addEventListener('dragover', handleDragOver); | |
| dropZone.addEventListener('dragleave', handleDragLeave); | |
| dropZone.addEventListener('drop', handleDrop); | |
| pageCountInput.addEventListener('input', updatePageCount); | |
| portraitBtn.addEventListener('click', () => setOrientation('portrait')); | |
| landscapeBtn.addEventListener('click', () => setOrientation('landscape')); | |
| generateBtn.addEventListener('click', generatePosterPages); | |
| downloadAllBtn.addEventListener('click', downloadAllPages); | |
| printAllBtn.addEventListener('click', printAllPages); | |
| document.getElementById('pdf-btn').addEventListener('click', generatePDF); | |
| // Update page count display | |
| function updatePageCount() { | |
| currentPageCount = parseInt(pageCountInput.value); | |
| pageCountValue.textContent = currentPageCount; | |
| } | |
| // Set orientation | |
| function setOrientation(orientation) { | |
| currentOrientation = orientation; | |
| if (orientation === 'portrait') { | |
| portraitBtn.classList.add('active', 'bg-indigo-100', 'text-indigo-700'); | |
| portraitBtn.classList.remove('border', 'border-gray-300'); | |
| landscapeBtn.classList.remove('active', 'bg-indigo-100', 'text-indigo-700'); | |
| landscapeBtn.classList.add('border', 'border-gray-300'); | |
| } else { | |
| landscapeBtn.classList.add('active', 'bg-indigo-100', 'text-indigo-700'); | |
| landscapeBtn.classList.remove('border', 'border-gray-300'); | |
| portraitBtn.classList.remove('active', 'bg-indigo-100', 'text-indigo-700'); | |
| portraitBtn.classList.add('border', 'border-gray-300'); | |
| } | |
| } | |
| // Handle file selection | |
| function handleFileSelect(e) { | |
| const file = e.target.files[0]; | |
| if (file && file.type.match('image.*')) { | |
| processImageFile(file); | |
| } | |
| } | |
| // Handle drag over | |
| function handleDragOver(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| dropZone.classList.add('highlight'); | |
| } | |
| // Handle drag leave | |
| function handleDragLeave(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| dropZone.classList.remove('highlight'); | |
| } | |
| // Handle drop | |
| function handleDrop(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| dropZone.classList.remove('highlight'); | |
| const file = e.dataTransfer.files[0]; | |
| if (file && file.type.match('image.*')) { | |
| processImageFile(file); | |
| } | |
| } | |
| // Process image file | |
| function processImageFile(file) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| currentImage = new Image(); | |
| currentImage.onload = function() { | |
| showPreview(); | |
| }; | |
| currentImage.src = e.target.result; | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| // Show preview | |
| function showPreview() { | |
| imagePreview.src = currentImage.src; | |
| imagePreviewContainer.classList.remove('hidden'); | |
| emptyState.classList.add('hidden'); | |
| previewControls.classList.remove('hidden'); | |
| } | |
| // Generate poster pages | |
| function generatePosterPages() { | |
| if (!currentImage) return; | |
| // Clear previous results | |
| posterPages.innerHTML = ''; | |
| resultSection.classList.remove('hidden'); | |
| // Calculate dimensions for each page | |
| const aspectRatio = currentImage.width / currentImage.height; | |
| let cols, rows; | |
| if (currentOrientation === 'portrait') { | |
| // For portrait orientation, we'll split vertically first | |
| cols = Math.ceil(Math.sqrt(currentPageCount * (210 / 297))); // A4 ratio | |
| rows = Math.ceil(currentPageCount / cols); | |
| } else { | |
| // For landscape orientation, we'll split horizontally first | |
| rows = Math.ceil(Math.sqrt(currentPageCount * (297 / 210))); // A4 ratio | |
| cols = Math.ceil(currentPageCount / rows); | |
| } | |
| // Create canvas for the entire image | |
| const canvas = document.createElement('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| // Size canvas to maintain aspect ratio | |
| if (currentOrientation === 'portrait') { | |
| canvas.width = cols * 210; // A4 width in mm (approximate) | |
| canvas.height = (canvas.width / aspectRatio); | |
| // If height is less than needed for rows, adjust | |
| const neededHeight = rows * 297; | |
| if (canvas.height < neededHeight) { | |
| canvas.height = neededHeight; | |
| canvas.width = canvas.height * aspectRatio; | |
| } | |
| } else { | |
| canvas.height = rows * 210; // A4 height in mm (approximate) | |
| canvas.width = canvas.height * aspectRatio; | |
| // If width is less than needed for cols, adjust | |
| const neededWidth = cols * 297; | |
| if (canvas.width < neededWidth) { | |
| canvas.width = neededWidth; | |
| canvas.height = canvas.width / aspectRatio; | |
| } | |
| } | |
| // Draw the image on canvas | |
| ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height); | |
| // Calculate each page's dimensions | |
| const pageWidth = canvas.width / cols; | |
| const pageHeight = canvas.height / rows; | |
| // Create individual pages | |
| for (let row = 0; row < rows; row++) { | |
| for (let col = 0; col < cols; col++) { | |
| const pageIndex = row * cols + col; | |
| if (pageIndex >= currentPageCount) break; | |
| // Create page canvas | |
| const pageCanvas = document.createElement('canvas'); | |
| pageCanvas.width = 2480; // A4 at 300dpi | |
| pageCanvas.height = 3508; | |
| const pageCtx = pageCanvas.getContext('2d'); | |
| // Calculate source dimensions | |
| const sx = col * pageWidth; | |
| const sy = row * pageHeight; | |
| const sw = pageWidth; | |
| const sh = pageHeight; | |
| // Draw the portion of the image on the page | |
| pageCtx.drawImage( | |
| canvas, | |
| sx, sy, sw, sh, | |
| 0, 0, pageCanvas.width, pageCanvas.height | |
| ); | |
| // Add cut lines and page number | |
| addPageMarkings(pageCtx, pageCanvas.width, pageCanvas.height, pageIndex + 1); | |
| // Create page element | |
| const pageElement = document.createElement('div'); | |
| pageElement.className = 'poster-page relative'; | |
| // Create download button | |
| const downloadBtn = document.createElement('button'); | |
| downloadBtn.className = 'absolute top-2 left-2 bg-white bg-opacity-90 p-2 rounded-full shadow hover:bg-opacity-100 transition'; | |
| downloadBtn.innerHTML = '<i data-feather="download" class="w-4 h-4"></i>'; | |
| downloadBtn.onclick = () => downloadPage(pageCanvas, pageIndex + 1); | |
| // Add image and button to page | |
| const img = document.createElement('img'); | |
| img.src = pageCanvas.toDataURL('image/jpeg', 0.9); | |
| img.className = 'w-full h-auto'; | |
| pageElement.appendChild(img); | |
| pageElement.appendChild(downloadBtn); | |
| posterPages.appendChild(pageElement); | |
| } | |
| } | |
| feather.replace(); | |
| } | |
| // Add page markings (cut lines and page numbers) | |
| function addPageMarkings(ctx, width, height, pageNumber) { | |
| // Cut lines (5mm from edges) | |
| const cutMargin = 59; // 5mm at 300dpi (5/25.4*300) | |
| ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)'; | |
| ctx.lineWidth = 2; | |
| ctx.setLineDash([5, 5]); | |
| // Top and bottom lines | |
| ctx.beginPath(); | |
| ctx.moveTo(cutMargin, cutMargin); | |
| ctx.lineTo(width - cutMargin, cutMargin); | |
| ctx.moveTo(cutMargin, height - cutMargin); | |
| ctx.lineTo(width - cutMargin, height - cutMargin); | |
| ctx.stroke(); | |
| // Left and right lines | |
| ctx.beginPath(); | |
| ctx.moveTo(cutMargin, cutMargin); | |
| ctx.lineTo(cutMargin, height - cutMargin); | |
| ctx.moveTo(width - cutMargin, cutMargin); | |
| ctx.lineTo(width - cutMargin, height - cutMargin); | |
| ctx.stroke(); | |
| // Page number | |
| ctx.font = 'bold 40px Arial'; | |
| ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; | |
| ctx.textAlign = 'right'; | |
| ctx.textBaseline = 'bottom'; | |
| ctx.fillText(pageNumber.toString(), width - 20, height - 20); | |
| } | |
| // Download a single page | |
| function downloadPage(canvas, pageNumber) { | |
| const link = document.createElement('a'); | |
| link.download = `poster-page-${pageNumber}.jpg`; | |
| link.href = canvas.toDataURL('image/jpeg', 0.9); | |
| link.click(); | |
| } | |
| // Download all pages | |
| function downloadAllPages() { | |
| const pages = posterPages.querySelectorAll('.poster-page img'); | |
| pages.forEach((img, index) => { | |
| const link = document.createElement('a'); | |
| link.download = `poster-page-${index + 1}.jpg`; | |
| link.href = img.src; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| }); | |
| } | |
| // Generate PDF of all pages | |
| function generatePDF() { | |
| if (posterPages.children.length === 0) return; | |
| // Include jsPDF library dynamically | |
| const script = document.createElement('script'); | |
| script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js'; | |
| script.onload = function() { | |
| const { jsPDF } = window.jspdf; | |
| const pdf = new jsPDF({ | |
| orientation: currentOrientation, | |
| unit: 'mm', | |
| format: 'a4' | |
| }); | |
| const pages = posterPages.querySelectorAll('.poster-page img'); | |
| const promises = []; | |
| // Convert all images to Image objects to get dimensions | |
| pages.forEach((img, index) => { | |
| const promise = new Promise((resolve) => { | |
| const tempImg = new Image(); | |
| tempImg.onload = function() { | |
| resolve({ | |
| src: img.src, | |
| width: tempImg.width, | |
| height: tempImg.height, | |
| index: index + 1 | |
| }); | |
| }; | |
| tempImg.src = img.src; | |
| }); | |
| promises.push(promise); | |
| }); | |
| Promise.all(promises).then((images) => { | |
| images.forEach((img, i) => { | |
| if (i > 0) { | |
| pdf.addPage('a4', currentOrientation); | |
| } | |
| // Calculate dimensions to fit A4 page (210x297mm or 297x210mm) | |
| const pageWidth = currentOrientation === 'portrait' ? 210 : 297; | |
| const pageHeight = currentOrientation === 'portrait' ? 297 : 210; | |
| // Maintain aspect ratio | |
| const imgAspect = img.width / img.height; | |
| const pageAspect = pageWidth / pageHeight; | |
| let width, height; | |
| if (imgAspect > pageAspect) { | |
| width = pageWidth; | |
| height = width / imgAspect; | |
| } else { | |
| height = pageHeight; | |
| width = height * imgAspect; | |
| } | |
| // Center the image | |
| const x = (pageWidth - width) / 2; | |
| const y = (pageHeight - height) / 2; | |
| pdf.addImage(img.src, 'JPEG', x, y, width, height); | |
| // Add page number | |
| pdf.setFontSize(12); | |
| pdf.setTextColor(150); | |
| pdf.text( | |
| `Page ${img.index}`, | |
| currentOrientation === 'portrait' ? 200 : 287, | |
| currentOrientation === 'portrait' ? 290 : 200, | |
| { align: 'right' } | |
| ); | |
| }); | |
| pdf.save('poster-pages.pdf'); | |
| }); | |
| }; | |
| document.head.appendChild(script); | |
| } | |
| // Print all pages | |
| function printAllPages() { | |
| if (posterPages.children.length === 0) return; | |
| // Create a print stylesheet | |
| const printStyle = document.createElement('style'); | |
| printStyle.innerHTML = ` | |
| @media print { | |
| body * { | |
| visibility: hidden; | |
| } | |
| .print-container, .print-container * { | |
| visibility: visible; | |
| } | |
| .print-container { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| width: 100%; | |
| height: 100%; | |
| margin: 0; | |
| padding: 0; | |
| page-break-after: always; | |
| } | |
| .print-page { | |
| width: 100%; | |
| height: 100%; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .print-page img { | |
| max-width: 100%; | |
| max-height: 100%; | |
| } | |
| } | |
| `; | |
| // Create a print container | |
| const printContainer = document.createElement('div'); | |
| printContainer.className = 'print-container'; | |
| // Clone all pages into print container | |
| const pages = posterPages.querySelectorAll('.poster-page'); | |
| pages.forEach(page => { | |
| const clone = page.cloneNode(true); | |
| const pageDiv = document.createElement('div'); | |
| pageDiv.className = 'print-page'; | |
| pageDiv.appendChild(clone); | |
| printContainer.appendChild(pageDiv); | |
| }); | |
| // Create print window | |
| const printWindow = window.open('', '', 'width=800,height=600'); | |
| printWindow.document.body.appendChild(printStyle); | |
| printWindow.document.body.appendChild(printContainer); | |
| printWindow.document.close(); | |
| // Trigger print after content loads | |
| printWindow.onload = function() { | |
| printWindow.print(); | |
| }; | |
| } | |
| }); | |