diff --git a/app.css b/app.css index 56efd80..a8abce9 100644 --- a/app.css +++ b/app.css @@ -589,7 +589,7 @@ body { flex-shrink: 0; } -/* Doc-Viewer (PDF/Image inline) */ +/* Doc-Viewer (Bilder) */ .doc-viewer-modal .dv-body { flex: 1; display: flex; @@ -605,6 +605,30 @@ body { text-decoration: none; } +/* PDF-Viewer */ +.pdf-viewer-modal .fs-header { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} +.pdf-viewer-modal .fs-title { order: 4; } +.pdf-viewer-modal .icon-btn:nth-of-type(1) { order: 1; } +.pdf-viewer-modal .pdf-page-info { order: 2; font-size: 12px; color: #aaa; } +.pdf-viewer-modal .icon-btn:nth-of-type(2) { order: 3; } +.pdf-viewer-modal .icon-btn:nth-of-type(3) { order: 5; } +.pdf-viewer-modal .fs-header a { order: 6; } +.pdf-body { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + background: #f5f5f5; + overflow: auto; + padding: 8px; +} +.pdf-body canvas { max-width: 100%; height: auto; box-shadow: 0 2px 8px rgba(0,0,0,0.2); } + /* PIN-Modal */ .pin-modal .pin-body { flex: 1; diff --git a/app.js b/app.js index 424f68e..b6217eb 100644 --- a/app.js +++ b/app.js @@ -1794,9 +1794,15 @@ function formatShortDate(ts) { function openFileViewer({ url, blob, mime }, filename) { const isImage = (mime || '').startsWith('image/'); + const isPdf = (mime || '').includes('pdf') || /\.pdf$/i.test(filename); + + if (isPdf && typeof pdfjs !== 'undefined') { + openPdfViewer(blob, filename, url); + return; + } if (!isImage) { - // Alles außer Bildern: direkt Download + // Alles außer Bildern + PDFs: Download const a = document.createElement('a'); a.href = url; a.download = filename || 'download'; @@ -1827,6 +1833,60 @@ function openFileViewer({ url, blob, mime }, filename) { modal.querySelector('#dv-close').onclick = cleanup; } +async function openPdfViewer(blob, filename, blobUrl) { + const modal = document.createElement('div'); + modal.className = 'fullscreen-modal pdf-viewer-modal'; + modal.innerHTML = ` +
+ +
+ +
${escapeHtml(filename || '')}
+ + +
+
+ +
+ `; + document.body.appendChild(modal); + + const canvas = modal.querySelector('#pdf-canvas'); + const ctx = canvas.getContext('2d'); + const pageInfo = modal.querySelector('#pdf-page-info'); + let pdf, currentPage = 1; + + try { + pdf = await pdfjs.getDocument(new Uint8Array(await blob.arrayBuffer())).promise; + pageInfo.textContent = `1 / ${pdf.numPages}`; + + async function renderPage(num) { + if (num < 1 || num > pdf.numPages) return; + currentPage = num; + const page = await pdf.getPage(num); + const viewport = page.getViewport({ scale: window.devicePixelRatio }); + canvas.width = viewport.width; + canvas.height = viewport.height; + await page.render({ canvasContext: ctx, viewport }).promise; + pageInfo.textContent = `${num} / ${pdf.numPages}`; + } + + await renderPage(1); + modal.querySelector('#pdf-prev').onclick = () => renderPage(currentPage - 1); + modal.querySelector('#pdf-next').onclick = () => renderPage(currentPage + 1); + modal.querySelector('#pdf-prev').disabled = false; + modal.querySelector('#pdf-next').disabled = pdf.numPages === 1; + } catch (err) { + pageInfo.textContent = '❌ PDF-Fehler'; + console.error('PDF load error:', err); + } + + modal.querySelector('#pdf-close').onclick = () => { + URL.revokeObjectURL(blobUrl); + modal.remove(); + }; +} + /* ============================================================ * PHOTO VOLLBILD MODAL MIT ZOOM + SWIPE + SKIZZEN-EDITOR * ============================================================ */ diff --git a/index.php b/index.php index e53c89a..8e1cc86 100644 --- a/index.php +++ b/index.php @@ -62,12 +62,21 @@ header('Expires: 0');
+ +