diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor
index b56dc33a..fdfbd7e0 100644
--- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor
+++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor
@@ -4,7 +4,6 @@
@using EnvelopeGenerator.Domain.Constants
@using EnvelopeGenerator.Server.Client.Models
@using DevExpress.Blazor
-@using DevExpress.Blazor.PdfViewer
@using EnvelopeGenerator.Server.Client.Services
@using EnvelopeGenerator.Application.Common.Dto
@using EnvelopeGenerator.Application.Common.Dto.History
@@ -13,6 +12,7 @@
@using Microsoft.Extensions.Localization
@inject EnvelopeGenerator.Server.Client.Services.EnvelopeService EnvelopeService
@inject EnvelopeGenerator.Server.Client.Services.DocumentService DocumentService
+@inject EnvelopeGenerator.Server.Client.Services.PdfRenderService PdfRenderService
@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
@inject EnvelopeGenerator.Server.Client.Services.ThemeService ThemeService
@inject EnvelopeGenerator.Server.Client.Services.UserPreferencesService UserPreferencesService
@@ -465,10 +465,15 @@
else if (_previewPdfBytes is not null && _previewViewerMounted)
{
-
+
}
@@ -542,7 +547,15 @@
private bool _previewLoading = false;
private bool _previewViewerMounted = false;
private byte[]? _previewPdfBytes = null;
+ private int _previewPageCount = 0;
+ private List _previewPagePreviews = [];
+ private string _previewFileName = "document.pdf";
private string? _previewErrorMessage = null;
+ private readonly IReadOnlyList _previewFieldOverlays = [];
+
+ private string? PreviewPdfDataUrl => _previewPdfBytes is { Length: > 0 }
+ ? $"data:application/pdf;base64,{Convert.ToBase64String(_previewPdfBytes)}"
+ : null;
// Phase 6e (step 1): UI state only. Export logic is added in step 2.
private string _selectedExportFormat = "xlsx";
@@ -960,6 +973,9 @@
// Unmount the viewer first — let the previous instance fully dispose
_previewViewerMounted = false;
_previewPdfBytes = null;
+ _previewPageCount = 0;
+ _previewPagePreviews = [];
+ _previewFileName = $"envelope-{envelope.Id}.pdf";
_previewErrorMessage = null;
_previewLoading = true;
_previewVisible = true;
@@ -968,6 +984,29 @@
try
{
_previewPdfBytes = await DocumentService.GetSenderDocumentBytesByEnvelopeIdAsync(envelope.Id);
+ if (_previewPdfBytes is null || _previewPdfBytes.Length == 0)
+ {
+ _previewErrorMessage = L("DocumentCouldNotBeLoaded", "Dokument konnte nicht geladen werden");
+ return;
+ }
+
+ var previewPdfBytes = _previewPdfBytes;
+
+ _previewPagePreviews = [];
+ try
+ {
+ _previewPagePreviews = (await PdfRenderService.RenderPagesAsync(previewPdfBytes))
+ .OrderBy(p => p.PageNumber)
+ .ToList();
+ }
+ catch (Exception renderEx)
+ {
+ Logger.LogWarning(renderEx, "Preview: page image rendering failed for envelope {EnvelopeId}. Falling back to native PDF preview.", envelope.Id);
+ }
+
+ _previewPageCount = _previewPagePreviews.Count > 0
+ ? _previewPagePreviews.Count
+ : EstimatePdfPageCount(previewPdfBytes) ?? 1;
// PDF ready — mount the viewer now
_previewViewerMounted = true;
@@ -1002,12 +1041,60 @@
{
await Task.Yield(); // wait one render cycle for the viewer to be removed from the DOM
_previewPdfBytes = null;
+ _previewPageCount = 0;
+ _previewPagePreviews = [];
+ _previewFileName = "document.pdf";
_previewErrorMessage = null;
_previewLoading = false;
await InvokeAsync(StateHasChanged);
});
}
+ Task OnPreviewEditorClickAsync(Microsoft.AspNetCore.Components.Web.MouseEventArgs _)
+ {
+ return Task.CompletedTask;
+ }
+
+ Task OnPreviewRemoveFieldAsync(int _)
+ {
+ return Task.CompletedTask;
+ }
+
+ Task OnPreviewMoveFieldAsync(SenderPdfFieldMoveRequest _)
+ {
+ return Task.CompletedTask;
+ }
+
+ static int? EstimatePdfPageCount(byte[]? documentBytes)
+ {
+ if (documentBytes is null || documentBytes.Length == 0)
+ return null;
+
+ try
+ {
+ var text = System.Text.Encoding.ASCII.GetString(documentBytes);
+ const string marker = "/Type /Page";
+ var count = 0;
+ var index = 0;
+
+ while (true)
+ {
+ index = text.IndexOf(marker, index, StringComparison.Ordinal);
+ if (index < 0)
+ break;
+
+ count++;
+ index += marker.Length;
+ }
+
+ return count > 0 ? count : null;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
// ── Grid Layout Persistence ───────────────────────────────────────────────
// LayoutAutoLoading fires when the grid initializes — restore saved layout.
// LayoutAutoSaving fires when the user changes column order/width/sort — persist it.