|
|
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Fast PDF Viewer</title> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> |
| <style> |
| body { margin: 0; background: #222; color: white; font-family: sans-serif; height: 100vh; display: flex; flex-direction: column; overflow: hidden; } |
| .controls { background: #333; padding: 10px; display: flex; justify-content: center; gap: 15px; box-shadow: 0 2px 10px rgba(0,0,0,0.5); z-index: 10; } |
| button { padding: 8px 16px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } |
| button:disabled { background: #555; } |
| #page-info { font-weight: bold; min-width: 120px; text-align: center; display: flex; align-items: center; } |
| #viewer-container { flex: 1; overflow: auto; display: flex; justify-content: center; padding: 20px; background: #525659; } |
| canvas { box-shadow: 0 4px 15px rgba(0,0,0,0.6); max-width: 100%; height: auto; background: white; } |
| |
| |
| #error-msg { |
| display: none; position: absolute; top: 60px; left: 50%; transform: translateX(-50%); |
| background: red; color: white; padding: 15px; border-radius: 5px; z-index: 1000; font-weight: bold; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="error-msg"></div> |
| <div class="controls"> |
| <button id="prev">Previous</button> |
| <span id="page-info">Loading...</span> |
| <button id="next">Next</button> |
| <a href="book.pdf" download style="color:#aaa; text-decoration:none; margin-left:10px; font-size:12px; display:flex; align-items:center;">(Download)</a> |
| </div> |
| <div id="viewer-container"> |
| <canvas id="the-canvas"></canvas> |
| </div> |
| <script> |
| const url = 'book.pdf'; |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; |
| let pdfDoc = null, pageNum = 1, pageRendering = false, pageNumPending = null; |
| const scale = 1.5, canvas = document.getElementById('the-canvas'), ctx = canvas.getContext('2d'); |
| |
| function showError(msg) { |
| const el = document.getElementById('error-msg'); |
| el.style.display = 'block'; |
| el.innerHTML = msg; |
| } |
| |
| function renderPage(num) { |
| pageRendering = true; |
| pdfDoc.getPage(num).then(page => { |
| const viewport = page.getViewport({scale: scale}); |
| canvas.height = viewport.height; canvas.width = viewport.width; |
| const renderContext = { canvasContext: ctx, viewport: viewport }; |
| page.render(renderContext).promise.then(() => { |
| pageRendering = false; |
| if (pageNumPending !== null) { renderPage(pageNumPending); pageNumPending = null; } |
| }); |
| }).catch(err => showError("Render Error: " + err.message)); |
| |
| document.getElementById('page-info').textContent = `Page ${num} / ${pdfDoc.numPages}`; |
| document.getElementById('prev').disabled = num <= 1; |
| document.getElementById('next').disabled = num >= pdfDoc.numPages; |
| document.getElementById('viewer-container').scrollTop = 0; |
| } |
| |
| function queueRenderPage(num) { if(pageRendering) pageNumPending = num; else renderPage(num); } |
| document.getElementById('prev').addEventListener('click', () => { if(pageNum > 1) { pageNum--; queueRenderPage(pageNum); } }); |
| document.getElementById('next').addEventListener('click', () => { if(pageNum < pdfDoc.numPages) { pageNum++; queueRenderPage(pageNum); } }); |
| |
| |
| const loadingTask = pdfjsLib.getDocument({ |
| url: url, |
| rangeChunkSize: 65536 * 16, |
| disableAutoFetch: true, |
| disableStream: true |
| }); |
| |
| loadingTask.promise.then(pdf => { |
| pdfDoc = pdf; |
| document.getElementById('page-info').textContent = `Page 1 / ${pdf.numPages}`; |
| renderPage(pageNum); |
| }).catch(err => { |
| console.error(err); |
| showError("Load Error: " + err.message + "<br><small>If you just uploaded this, wait 2 minutes and refresh.</small>"); |
| }); |
| </script> |
| </body> |
| </html> |
| |