Download index.html from JakeFake222/ImageToPDFTouchControl: direct link, hf CLI and curl.
- Browser
- Download file 8.3 kB
-
https://huggingface.co/spaces/JakeFake222/ImageToPDFTouchControl/resolve/main/index.html
- Command line
-
hf download hf://spaces/JakeFake222/ImageToPDFTouchControl/index.html
-
curl -L -o index.html https://huggingface.co/spaces/JakeFake222/ImageToPDFTouchControl/resolve/main/index.html
8.3 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Images to PDF</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| margin: 50px; | |
| text-align: center; | |
| touch-action: none; /* Prevent default touch behaviors like scrolling */ | |
| } | |
| #imageContainer { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 10px; | |
| margin: 20px 0; | |
| padding: 10px; | |
| border: 1px solid #ccc; | |
| } | |
| .imageWrapper { | |
| max-width: 200px; | |
| cursor: move; /* For desktop */ | |
| position: relative; | |
| user-select: none; /* Prevent text selection during drag/touch */ | |
| } | |
| .imageWrapper img { | |
| max-width: 100%; | |
| height: auto; | |
| border: 1px solid #ddd; | |
| border-radius: 5px; | |
| } | |
| .imageWrapper.dragging { | |
| opacity: 0.5; /* Visual feedback during drag/touch */ | |
| } | |
| .imageWrapper.over { | |
| border: 2px dashed #4CAF50; /* Visual feedback for drop target */ | |
| } | |
| button { | |
| padding: 10px 20px; | |
| font-size: 16px; | |
| cursor: pointer; | |
| background-color: #4CAF50; | |
| color: white; | |
| border: none; | |
| border-radius: 5px; | |
| } | |
| button:hover { | |
| background-color: #45a049; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Images to PDF Converter</h1> | |
| <input type="file" id="imageInput" accept="image/*" multiple> | |
| <p>Drag and drop (desktop) or tap and move (mobile) images to reorder them for the PDF.</p> | |
| <div id="imageContainer"></div> | |
| <button onclick="generatePDF()">Download as PDF</button> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> | |
| <script> | |
| const { jsPDF } = window.jspdf; | |
| const imageInput = document.getElementById('imageInput'); | |
| const imageContainer = document.getElementById('imageContainer'); | |
| let images = []; | |
| // Handle image uploads | |
| imageInput.addEventListener('change', (event) => { | |
| images = []; | |
| imageContainer.innerHTML = ''; | |
| const files = Array.from(event.target.files); | |
| files.forEach((file) => { | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| const img = new Image(); | |
| img.src = e.target.result; | |
| img.onload = () => { | |
| images.push({ src: e.target.result, width: img.width, height: img.height }); | |
| renderImages(); | |
| }; | |
| }; | |
| reader.readAsDataURL(file); | |
| }); | |
| }); | |
| // Render images with drag-and-drop and touch functionality | |
| function renderImages() { | |
| imageContainer.innerHTML = ''; | |
| images.forEach((image, index) => { | |
| const wrapper = document.createElement('div'); | |
| wrapper.className = 'imageWrapper'; | |
| wrapper.draggable = true; | |
| wrapper.dataset.index = index; | |
| const img = new Image(); | |
| img.src = image.src; | |
| wrapper.appendChild(img); | |
| // Desktop drag-and-drop | |
| wrapper.addEventListener('dragstart', (e) => { | |
| wrapper.classList.add('dragging'); | |
| e.dataTransfer.setData('text/plain', index); | |
| }); | |
| wrapper.addEventListener('dragend', () => { | |
| wrapper.classList.remove('dragging'); | |
| clearOverStyles(); | |
| }); | |
| wrapper.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| wrapper.classList.add('over'); | |
| }); | |
| wrapper.addEventListener('dragleave', () => { | |
| wrapper.classList.remove('over'); | |
| }); | |
| wrapper.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| const draggedIndex = parseInt(e.dataTransfer.getData('text/plain')); | |
| const dropIndex = parseInt(wrapper.dataset.index); | |
| reorderImages(draggedIndex, dropIndex); | |
| renderImages(); | |
| }); | |
| // Touch events for mobile | |
| wrapper.addEventListener('touchstart', (e) => { | |
| e.preventDefault(); | |
| wrapper.classList.add('dragging'); | |
| startTouchReorder(e, wrapper, index); | |
| }); | |
| wrapper.addEventListener('touchmove', (e) => { | |
| e.preventDefault(); | |
| handleTouchMove(e, wrapper); | |
| }); | |
| wrapper.addEventListener('touchend', (e) => { | |
| e.preventDefault(); | |
| wrapper.classList.remove('dragging'); | |
| endTouchReorder(wrapper, index); | |
| clearOverStyles(); | |
| }); | |
| imageContainer.appendChild(wrapper); | |
| }); | |
| } | |
| // Touch reordering logic | |
| let touchStartY = 0; | |
| let targetIndex = null; | |
| function startTouchReorder(event, wrapper, index) { | |
| const touch = event.touches[0]; | |
| touchStartY = touch.clientY; | |
| targetIndex = index; | |
| } | |
| function handleTouchMove(event, wrapper) { | |
| const touch = event.touches[0]; | |
| const touchY = touch.clientY; | |
| // Find the element under the touch point | |
| const elements = document.elementsFromPoint(touch.clientX, touchY); | |
| const targetWrapper = elements.find(el => el.classList.contains('imageWrapper') && el !== wrapper); | |
| // Clear previous 'over' styles | |
| clearOverStyles(); | |
| if (targetWrapper) { | |
| targetWrapper.classList.add('over'); | |
| } | |
| } | |
| function endTouchReorder(wrapper, startIndex) { | |
| const overElement = document.querySelector('.imageWrapper.over'); | |
| if (overElement) { | |
| const dropIndex = parseInt(overElement.dataset.index); | |
| reorderImages(startIndex, dropIndex); | |
| renderImages(); | |
| } | |
| } | |
| function clearOverStyles() { | |
| document.querySelectorAll('.imageWrapper.over').forEach(el => el.classList.remove('over')); | |
| } | |
| function reorderImages(draggedIndex, dropIndex) { | |
| if (draggedIndex !== dropIndex) { | |
| const [draggedImage] = images.splice(draggedIndex, 1); | |
| images.splice(dropIndex, 0, draggedImage); | |
| } | |
| } | |
| // Generate PDF from images | |
| function generatePDF() { | |
| if (images.length === 0) { | |
| alert('Please upload at least one image.'); | |
| return; | |
| } | |
| const doc = new jsPDF(); | |
| const pageWidth = doc.internal.pageSize.getWidth(); | |
| const pageHeight = doc.internal.pageSize.getHeight(); | |
| const margin = 10; | |
| const maxImgWidth = pageWidth - 2 * margin; | |
| const maxImgHeight = pageHeight - 2 * margin; | |
| images.forEach((image, index) => { | |
| if (index > 0) { | |
| doc.addPage(); | |
| } | |
| // Calculate scaling to fit image within page | |
| let imgWidth = image.width; | |
| let imgHeight = image.height; | |
| const ratio = Math.min(maxImgWidth / imgWidth, maxImgHeight / imgHeight); | |
| imgWidth = imgWidth * ratio; | |
| imgHeight = imgHeight * ratio; | |
| // Center the image on the page | |
| const x = (pageWidth - imgWidth) / 2; | |
| const y = (pageHeight - imgHeight) / 2; | |
| doc.addImage(image.src, 'JPEG', x, y, imgWidth, imgHeight); | |
| }); | |
| doc.save('images.pdf'); | |
| } | |
| </script> | |
| </body> | |
| </html> |