diff --git a/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor b/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor index 74516821..83d66884 100644 --- a/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor +++ b/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor @@ -12,6 +12,11 @@ +@if (!string.IsNullOrWhiteSpace(ErrorMessage)) +{ +
@ErrorMessage
+} + @if (!HasPdf) {
Drop or select a PDF to start.
@@ -96,6 +101,7 @@ private double StartLeft; private double StartTop; private string TextValue = "Text"; + private string? ErrorMessage; private bool HasPdf => !string.IsNullOrWhiteSpace(PdfBase64); private int DisplayPage => PageIndex + 1; @@ -117,22 +123,41 @@ private async Task HandleFileSelected(InputFileChangeEventArgs e) { - if (e.FileCount == 0) + ErrorMessage = null; + + try { - return; + if (e.FileCount == 0) + { + return; + } + + var file = e.File; + await using var stream = file.OpenReadStream(maxAllowedSize: 20 * 1024 * 1024); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + PdfBase64 = Convert.ToBase64String(ms.ToArray()); + + // Show the canvas before we start rendering + await InvokeAsync(StateHasChanged); + await Task.Yield(); + + // Make sure pdf.js is ready + await JS.InvokeVoidAsync("pdfInterop.ensureReady"); + + var result = await JS.InvokeAsync("pdfInterop.loadPdf", PdfBase64); + PageCount = result.Pages; + PageIndex = 0; + + await RenderPage(); + } + catch (Exception ex) + { + ErrorMessage = $"Fehler beim Laden der PDF: {ex.Message}"; + PdfBase64 = null; + PageCount = 0; + PageIndex = 0; } - - var file = e.File; - await using var stream = file.OpenReadStream(maxAllowedSize: 20 * 1024 * 1024); - using var ms = new MemoryStream(); - await stream.CopyToAsync(ms); - PdfBase64 = Convert.ToBase64String(ms.ToArray()); - - var result = await JS.InvokeAsync("pdfInterop.loadPdf", PdfBase64); - PageCount = result.Pages; - PageIndex = 0; - - await RenderPage(); } private async Task RenderPage() @@ -142,10 +167,17 @@ return; } - var viewport = await JS.InvokeAsync("pdfInterop.renderPage", PageIndex, "pdf-canvas", ViewportWidthPx); - ViewportWidthPx = viewport.Width; - ViewportHeightPx = viewport.Height; - StateHasChanged(); + try + { + var viewport = await JS.InvokeAsync("pdfInterop.renderPage", PageIndex, "pdf-canvas", ViewportWidthPx); + ViewportWidthPx = viewport.Width; + ViewportHeightPx = viewport.Height; + StateHasChanged(); + } + catch (Exception ex) + { + ErrorMessage = $"Fehler beim Rendern: {ex.Message}"; + } } private void Reset() diff --git a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/css/app.css b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/css/app.css index 669a1de3..1386cc4d 100644 --- a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/css/app.css +++ b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/css/app.css @@ -76,6 +76,15 @@ h1 { color: rgba(226, 232, 240, 0.8); } +.error-banner { + margin-top: 8px; + padding: 10px 12px; + border-radius: 8px; + background: rgba(239, 68, 68, 0.12); + border: 1px solid rgba(239, 68, 68, 0.3); + color: #fecdd3; +} + .document-shell { position: relative; margin-top: 12px; diff --git a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/index.html b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/index.html index 3831da21..b58847a9 100644 --- a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/index.html +++ b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/index.html @@ -6,9 +6,10 @@ Receiver UI (Blazor) - - - + + + + diff --git a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js index 62c2bde5..cb5b4f4b 100644 --- a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js +++ b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js @@ -1,8 +1,13 @@ (function () { + // Stick to pdf.js 3.11 UMD + classic worker for compatibility. + const PDF_JS_SRC = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"; + const WORKER_SRC = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js"; + const state = { pdfDoc: null, pdfBytes: null, lastViewport: null, + pdfJsReady: null, }; function base64ToUint8(base64) { @@ -32,17 +37,73 @@ const pointerPads = new Map(); - window.pdfInterop = { - ensureReady: () => { - if (pdfjsLib && pdfjsLib.GlobalWorkerOptions) { - // worker is already loaded via CDN include + function loadScriptOnce(url) { + return new Promise((resolve, reject) => { + // If already present, resolve immediately + const existing = Array.from(document.getElementsByTagName('script')).find(s => s.src === url); + if (existing && existing.dataset.loaded === "true") { + resolve(); return; } + + const script = existing || document.createElement('script'); + script.src = url; + script.defer = true; + script.onload = () => { + script.dataset.loaded = "true"; + resolve(); + }; + script.onerror = (e) => reject(new Error(`Script load failed: ${url}`)); + + if (!existing) { + document.head.appendChild(script); + } + }); + } + + async function ensurePdfJsLoaded() { + if (typeof pdfjsLib !== "undefined") { + return; + } + + if (!state.pdfJsReady) { + state.pdfJsReady = loadScriptOnce(PDF_JS_SRC); + } + + await state.pdfJsReady; + + if (typeof pdfjsLib === "undefined") { + throw new Error("pdfjsLib could not be loaded"); + } + } + + window.pdfInterop = { + ensureReady: async () => { + // Ensure pdf.js is present and the worker path is set explicitly. + await ensurePdfJsLoaded(); + if (pdfjsLib && pdfjsLib.GlobalWorkerOptions) { + if (pdfjsLib.GlobalWorkerOptions.workerSrc !== WORKER_SRC) { + pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_SRC; + } + } else { + throw new Error("pdf.js not available after load"); + } }, loadPdf: async (base64) => { - return await reloadFromBase64(base64); + await ensurePdfJsLoaded(); + try { + const result = await reloadFromBase64(base64); + if (!result || !result.pages) { + throw new Error("PDF has keine Seiten erkannt"); + } + return result; + } catch (err) { + console.error("pdfInterop.loadPdf failed", err); + throw err; + } }, renderPage: async (pageIndex, canvasId, targetWidth) => { + await ensurePdfJsLoaded(); if (!state.pdfDoc) { throw new Error('PDF not loaded'); } @@ -51,8 +112,14 @@ const scale = targetWidth / rawViewport.width; const viewport = page.getViewport({ scale }); - const canvas = document.getElementById(canvasId); + let canvas = document.getElementById(canvasId); if (!canvas) { + // give the UI a tiny delay to render the canvas into the DOM + await new Promise(r => setTimeout(r, 40)); + canvas = document.getElementById(canvasId); + } + if (!canvas) { + console.error("renderPage: canvas not found", canvasId); throw new Error('Canvas not found'); } const ctx = canvas.getContext('2d');