| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>PDF Viewer Hub</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script> |
| <style> |
| .pdf-thumbnail:hover .pdf-overlay { |
| opacity: 1; |
| } |
| .pdf-overlay { |
| transition: opacity 0.3s ease; |
| } |
| .pdf-container { |
| height: 600px; |
| overflow: auto; |
| position: relative; |
| } |
| .pdf-fullscreen { |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| z-index: 9999; |
| background: white; |
| overflow: auto; |
| } |
| .search-highlight { |
| background-color: rgba(255, 255, 0, 0.5); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100"> |
| <div class="container mx-auto px-4 py-8"> |
| <header class="mb-10 text-center"> |
| <h1 class="text-4xl font-bold text-indigo-700 mb-2">PDF Viewer Hub</h1> |
| <p class="text-gray-600 text-lg">View, search and download PDF documents</p> |
| </header> |
|
|
| <div class="flex flex-wrap -mx-2"> |
| |
| <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4"> |
| <div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative"> |
| <img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+1" alt="Sample PDF 1" class="w-full h-64 object-cover"> |
| <div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center"> |
| <button onclick="openPdfModal('sample1.pdf', 'Sample Document 1')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-eye mr-1"></i> View |
| </button> |
| <a href="sample1.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-download mr-1"></i> Download |
| </a> |
| </div> |
| <div class="p-4"> |
| <h3 class="font-semibold text-lg mb-1">Sample Document 1</h3> |
| <p class="text-gray-500 text-sm">2.4 MB • 12 pages</p> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4"> |
| <div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative"> |
| <img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+2" alt="Sample PDF 2" class="w-full h-64 object-cover"> |
| <div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center"> |
| <button onclick="openPdfModal('sample2.pdf', 'Sample Document 2')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-eye mr-1"></i> View |
| </button> |
| <a href="sample2.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-download mr-1"></i> Download |
| </a> |
| </div> |
| <div class="p-4"> |
| <h3 class="font-semibold text-lg mb-1">Sample Document 2</h3> |
| <p class="text-gray-500 text-sm">3.1 MB • 18 pages</p> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4"> |
| <div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative"> |
| <img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+3" alt="Sample PDF 3" class="w-full h-64 object-cover"> |
| <div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center"> |
| <button onclick="openPdfModal('sample3.pdf', 'Sample Document 3')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-eye mr-1"></i> View |
| </button> |
| <a href="sample3.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-download mr-1"></i> Download |
| </a> |
| </div> |
| <div class="p-4"> |
| <h3 class="font-semibold text-lg mb-1">Sample Document 3</h3> |
| <p class="text-gray-500 text-sm">1.8 MB • 8 pages</p> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 px-2 mb-4"> |
| <div class="pdf-thumbnail bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 relative"> |
| <img src="https://via.placeholder.com/300x400/4F46E5/FFFFFF?text=Sample+PDF+4" alt="Sample PDF 4" class="w-full h-64 object-cover"> |
| <div class="pdf-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 flex items-center justify-center"> |
| <button onclick="openPdfModal('sample4.pdf', 'Sample Document 4')" class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-eye mr-1"></i> View |
| </button> |
| <a href="sample4.pdf" download class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium mx-1 hover:bg-indigo-100 transition"> |
| <i class="fas fa-download mr-1"></i> Download |
| </a> |
| </div> |
| <div class="p-4"> |
| <h3 class="font-semibold text-lg mb-1">Sample Document 4</h3> |
| <p class="text-gray-500 text-sm">5.2 MB • 24 pages</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="pdfModal" class="fixed inset-0 z-50 overflow-y-auto hidden"> |
| <div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> |
| <div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
| <div class="absolute inset-0 bg-gray-500 opacity-75"></div> |
| </div> |
| <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> |
| <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full"> |
| <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
| <div class="sm:flex sm:items-start justify-between"> |
| <h3 id="pdfTitle" class="text-lg leading-6 font-medium text-gray-900"></h3> |
| <button type="button" onclick="closePdfModal()" class="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none"> |
| <span class="sr-only">Close</span> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div class="mt-4"> |
| <div class="flex justify-between items-center mb-4"> |
| <div class="flex space-x-2"> |
| <button id="zoomIn" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded"> |
| <i class="fas fa-search-plus"></i> |
| </button> |
| <button id="zoomOut" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded"> |
| <i class="fas fa-search-minus"></i> |
| </button> |
| <button id="fullscreen" class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded"> |
| <i class="fas fa-expand"></i> |
| </button> |
| <a id="downloadBtn" download class="bg-gray-100 hover:bg-gray-200 text-gray-800 py-1 px-3 rounded inline-flex items-center"> |
| <i class="fas fa-download mr-1"></i> Download |
| </a> |
| </div> |
| <div class="relative"> |
| <div class="flex"> |
| <input type="text" id="searchText" placeholder="Search in document..." class="border rounded-l py-1 px-3 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500"> |
| <button id="searchBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white py-1 px-3 rounded-r"> |
| <i class="fas fa-search"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| <div id="pdfContainer" class="pdf-container border"> |
| <canvas id="pdfCanvas"></canvas> |
| </div> |
| <div class="flex justify-between items-center mt-4"> |
| <div> |
| <button id="prevPage" class="bg-indigo-600 hover:bg-indigo-700 text-white py-1 px-4 rounded mr-2"> |
| <i class="fas fa-chevron-left"></i> Prev |
| </button> |
| <button id="nextPage" class="bg-indigo-600 hover:bg-indigo-700 text-white py-1 px-4 rounded"> |
| Next <i class="fas fa-chevron-right"></i> |
| </button> |
| </div> |
| <div class="text-gray-600 text-sm"> |
| Page <span id="currentPage">1</span> of <span id="totalPages">1</span> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js'; |
| |
| |
| let pdfModal = document.getElementById('pdfModal'); |
| let currentPdf = null; |
| let currentPage = 1; |
| let scale = 1; |
| let fullscreen = false; |
| let searchText = ''; |
| let searchResults = []; |
| let currentSearchResult = 0; |
| |
| |
| function openPdfModal(pdfUrl, pdfTitle) { |
| document.getElementById('pdfTitle').textContent = pdfTitle; |
| document.getElementById('downloadBtn').href = pdfUrl; |
| |
| |
| pdfModal.classList.remove('hidden'); |
| |
| |
| loadPdf(pdfUrl); |
| |
| |
| document.getElementById('searchText').value = ''; |
| searchText = ''; |
| searchResults = []; |
| currentSearchResult = 0; |
| } |
| |
| |
| function closePdfModal() { |
| if (fullscreen) { |
| toggleFullscreen(); |
| } |
| pdfModal.classList.add('hidden'); |
| currentPdf = null; |
| } |
| |
| |
| function loadPdf(pdfUrl) { |
| |
| currentPage = 1; |
| scale = 1; |
| |
| |
| pdfjsLib.getDocument(pdfUrl).promise.then(function(pdf) { |
| currentPdf = pdf; |
| |
| |
| document.getElementById('totalPages').textContent = pdf.numPages; |
| |
| |
| renderPage(currentPage); |
| }).catch(function(error) { |
| alert('Error loading PDF: ' + error.message); |
| }); |
| } |
| |
| |
| function renderPage(pageNum) { |
| if (!currentPdf) return; |
| |
| currentPdf.getPage(pageNum).then(function(page) { |
| let viewport = page.getViewport({ scale: scale }); |
| let canvas = document.getElementById('pdfCanvas'); |
| let context = canvas.getContext('2d'); |
| |
| |
| canvas.height = viewport.height; |
| canvas.width = viewport.width; |
| |
| |
| document.querySelectorAll('.search-highlight').forEach(el => el.remove()); |
| |
| |
| document.getElementById('currentPage').textContent = pageNum; |
| |
| |
| document.getElementById('prevPage').disabled = (pageNum <= 1); |
| document.getElementById('nextPage').disabled = (pageNum >= currentPdf.numPages); |
| |
| |
| let renderContext = { |
| canvasContext: context, |
| viewport: viewport |
| }; |
| |
| page.render(renderContext); |
| |
| |
| if (searchText) { |
| searchInPage(page, viewport); |
| } |
| }); |
| } |
| |
| |
| function searchInPage(page, viewport) { |
| page.getTextContent().then(function(textContent) { |
| |
| searchResults = []; |
| let text = textContent.items.map(item => item.str).join(' '); |
| |
| let regex = new RegExp(searchText, 'gi'); |
| let match; |
| while ((match = regex.exec(text)) !== null) { |
| searchResults.push(match.index); |
| } |
| |
| |
| if (searchResults.length > 0) { |
| highlightMatches(page, textContent, viewport); |
| document.getElementById('searchText').classList.remove('border-gray-300'); |
| document.getElementById('searchText').classList.add('border-green-500'); |
| } else { |
| document.getElementById('searchText').classList.remove('border-green-500'); |
| document.getElementById('searchText').classList.add('border-gray-300'); |
| } |
| }); |
| } |
| |
| |
| function highlightMatches(page, textContent, viewport) { |
| let textItems = textContent.items; |
| let canvas = document.getElementById('pdfCanvas'); |
| let searchTermLength = searchText.length; |
| |
| |
| document.querySelectorAll('.search-highlight').forEach(el => el.remove()); |
| |
| let foundChars = 0; |
| let currentResult = 0; |
| |
| for (let i = 0; i < textItems.length; i++) { |
| let item = textItems[i]; |
| let itemText = item.str; |
| |
| |
| while (currentResult < searchResults.length && |
| foundChars + itemText.length >= searchResults[currentResult]) { |
| |
| let position = searchResults[currentResult] - foundChars; |
| if (position < 0) position = 0; |
| |
| |
| let textBefore = itemText.substr(0, position); |
| let measureBefore = page.getTextWidth(textBefore); |
| |
| |
| let searchTermText = itemText.substr(position, searchTermLength); |
| let searchMeasure = page.getTextWidth(searchTermText); |
| |
| |
| let highlightDiv = document.createElement('div'); |
| highlightDiv.className = 'search-highlight absolute'; |
| highlightDiv.style.top = (item.transform[5] - (item.height * (scale * 0.9))) + 'px'; |
| highlightDiv.style.left = (item.transform[4] + measureBefore) + 'px'; |
| highlightDiv.style.width = searchMeasure + 'px'; |
| highlightDiv.style.height = (item.height * scale) + 'px'; |
| |
| if (currentSearchResult === currentResult) { |
| highlightDiv.style.border = '2px solid red'; |
| highlightDiv.style.boxSizing = 'border-box'; |
| } |
| |
| document.querySelector('#pdfContainer').appendChild(highlightDiv); |
| |
| currentResult++; |
| |
| |
| if (position + searchTermLength >= itemText.length) { |
| break; |
| } |
| } |
| |
| foundChars += itemText.length; |
| } |
| } |
| |
| |
| function toggleFullscreen() { |
| let container = document.getElementById('pdfContainer'); |
| if (!fullscreen) { |
| container.classList.add('pdf-fullscreen'); |
| fullscreen = true; |
| document.getElementById('fullscreen').innerHTML = '<i class="fas fa-compress"></i>'; |
| } else { |
| container.classList.remove('pdf-fullscreen'); |
| fullscreen = false; |
| document.getElementById('fullscreen').innerHTML = '<i class="fas fa-expand"></i>'; |
| } |
| renderPage(currentPage); |
| } |
| |
| |
| document.getElementById('prevPage').addEventListener('click', function() { |
| if (currentPage > 1) { |
| currentPage--; |
| renderPage(currentPage); |
| } |
| }); |
| |
| document.getElementById('nextPage').addEventListener('click', function() { |
| if (currentPdf && currentPage < currentPdf.numPages) { |
| currentPage++; |
| renderPage(currentPage); |
| } |
| }); |
| |
| document.getElementById('zoomIn').addEventListener('click', function() { |
| scale += 0.25; |
| renderPage(currentPage); |
| }); |
| |
| document.getElementById('zoomOut').addEventListener('click', function() { |
| if (scale > 0.5) { |
| scale -= 0.25; |
| renderPage(currentPage); |
| } |
| }); |
| |
| document.getElementById('fullscreen').addEventListener('click', toggleFullscreen); |
| |
| document.getElementById('searchBtn').addEventListener('click', function() { |
| searchText = document.getElementById('searchText').value.trim(); |
| if (searchText && currentPdf) { |
| currentSearchResult = 0; |
| renderPage(currentPage); |
| } |
| }); |
| |
| |
| document.getElementById('searchText').addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') { |
| document.getElementById('searchBtn').click(); |
| } |
| }); |
| |
| |
| document.querySelector('#pdfModal > div > div').addEventListener('click', function(e) { |
| if (e.target === this) { |
| closePdfModal(); |
| } |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=bep40/pdf-list" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |