diff --git a/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor b/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor
index 3228d138..fea7170e 100644
--- a/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor
+++ b/EnvelopeGenerator.ReceiverUIBlazor/Pages/Index.razor
@@ -1,66 +1,70 @@
@page "/"
+@using Microsoft.JSInterop
@inject IJSRuntime JS
+@implements IAsyncDisposable
-
+
Sign PDF (Blazor)
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ @if (!string.IsNullOrWhiteSpace(ErrorMessage))
+ {
+
@ErrorMessage
+ }
+
+ @if (!HasPdf)
+ {
+
Drop or select a PDF to start.
+ }
+
+ @if (HasPdf)
+ {
+
+
+
+ @if (ShowSignature)
+ {
+
+
+
+
+
+

+
+ }
+
+ @if (ShowText)
+ {
+
+ }
+
+
+
+
+ Page @DisplayPage / @PageCount
+
+
+ }
-@if (!string.IsNullOrWhiteSpace(ErrorMessage))
-{
-
@ErrorMessage
-}
-
-@if (!HasPdf)
-{
-
Drop or select a PDF to start.
-}
-
-@if (HasPdf)
-{
-
-
-
- @if (ShowSignature)
- {
-
-
-
-
-
-

-
- }
-
- @if (ShowText)
- {
-
- }
-
-
-
-
- Page @DisplayPage / @PageCount
-
-
-}
-
@if (ShowSignaturePadModal)
{
@@ -85,6 +89,7 @@
private string? PdfBase64;
private string? OriginalPdfBase64;
+ private DotNetObjectReference? _dotNetRef;
private int PageIndex;
private int PageCount;
private double ViewportWidthPx = 800;
@@ -120,6 +125,8 @@
if (firstRender)
{
await JS.InvokeVoidAsync("pdfInterop.ensureReady");
+ _dotNetRef ??= DotNetObjectReference.Create(this);
+ await JS.InvokeVoidAsync("pdfInterop.registerDropHandler", _dotNetRef);
}
if (ShowSignaturePadModal)
@@ -129,35 +136,25 @@
}
private async Task HandleFileSelected(InputFileChangeEventArgs e)
+ {
+ if (e.FileCount == 0)
+ {
+ return;
+ }
+
+ await LoadPdfFromBrowserFile(e.File);
+ }
+
+ private async Task LoadPdfFromBrowserFile(IBrowserFile file)
{
ErrorMessage = null;
try
{
- 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());
- OriginalPdfBase64 = PdfBase64;
-
- // 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();
+ await LoadPdfFromBase64Internal(Convert.ToBase64String(ms.ToArray()));
}
catch (Exception ex)
{
@@ -168,6 +165,32 @@
}
}
+ [JSInvokable]
+ public Task LoadPdfFromBase64(string base64)
+ {
+ return LoadPdfFromBase64Internal(base64);
+ }
+
+ private async Task LoadPdfFromBase64Internal(string base64)
+ {
+ ErrorMessage = null;
+ PdfBase64 = base64;
+ OriginalPdfBase64 = PdfBase64;
+
+ // 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();
+ }
+
private async Task RenderPage()
{
if (!HasPdf)
@@ -396,4 +419,10 @@
private record RenderResult(int Pages);
private record ViewportInfo(double Width, double Height, double PageWidth, double PageHeight);
+
+ public async ValueTask DisposeAsync()
+ {
+ _dotNetRef?.Dispose();
+ await Task.CompletedTask;
+ }
}
diff --git a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js
index 6d0ea97f..8d559304 100644
--- a/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js
+++ b/EnvelopeGenerator.ReceiverUIBlazor/wwwroot/js/pdfInterop.js
@@ -264,6 +264,39 @@
pointerPads.set(canvasId, { ctx, canvas });
},
+ registerDropHandler: (dotNetRef) => {
+ if (window.__pdfDropRegistered) return;
+ window.__pdfDropRegistered = true;
+
+ const prevent = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ ['dragenter', 'dragover', 'dragleave'].forEach(evt => {
+ document.addEventListener(evt, prevent, false);
+ });
+
+ document.addEventListener('drop', (e) => {
+ prevent(e);
+
+ const files = e.dataTransfer?.files;
+ if (!files || files.length === 0) {
+ return;
+ }
+
+ const file = files[0];
+ const reader = new FileReader();
+ reader.onload = () => {
+ const result = reader.result;
+ if (typeof result === 'string') {
+ const base64 = result.split(',')[1] || result;
+ dotNetRef?.invokeMethodAsync('LoadPdfFromBase64', base64);
+ }
+ };
+ reader.readAsDataURL(file);
+ }, false);
+ },
clearSignaturePad: (canvasId) => {
const pad = pointerPads.get(canvasId);
if (!pad) return;