feat(sender-editor): use rendered page metadata for field placement

This commit is contained in:
2026-09-29 19:22:01 +02:00
parent 7a114f47a6
commit 776700b247
2 changed files with 39 additions and 28 deletions

View File

@@ -1,4 +1,5 @@
@using EnvelopeGenerator.Application.Resources @using EnvelopeGenerator.Application.Resources
@using EnvelopeGenerator.Application.Common.Dto
@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Localization
@inject IStringLocalizer<Resource> UiLocalizer @inject IStringLocalizer<Resource> UiLocalizer
@@ -11,7 +12,7 @@
} }
else else
{ {
@if (PageImageDataUrls.Count == 0 && !string.IsNullOrWhiteSpace(PdfDataUrl)) @if (Pages.Count == 0 && !string.IsNullOrWhiteSpace(PdfDataUrl))
{ {
<div class="sender-image-editor__native-preview"> <div class="sender-image-editor__native-preview">
<iframe class="sender-image-editor__native-frame" <iframe class="sender-image-editor__native-frame"
@@ -21,17 +22,22 @@
} }
<div class="sender-image-editor__pages"> <div class="sender-image-editor__pages">
@for (var pageNumber = 1; pageNumber <= PageCount; pageNumber++) @for (var pageIndex = 0; pageIndex < PageCount; pageIndex++)
{ {
var pagePreview = pageIndex < Pages.Count ? Pages[pageIndex] : null;
var pageNumber = pagePreview?.PageNumber ?? (pageIndex + 1);
var widthPt = pagePreview?.WidthPt ?? 595.0;
var heightPt = pagePreview?.HeightPt ?? 842.0;
<div class="sender-image-editor__page-block" <div class="sender-image-editor__page-block"
data-editor-page="@pageNumber" data-editor-page="@pageNumber"
data-page-width-pt="595" data-page-width-pt="@widthPt"
data-page-height-pt="842"> data-page-height-pt="@heightPt">
<div class="sender-image-editor__page-canvas"> <div class="sender-image-editor__page-canvas">
@if (PageImageDataUrls.TryGetValue(pageNumber, out var imageDataUrl) && !string.IsNullOrWhiteSpace(imageDataUrl)) @if (!string.IsNullOrWhiteSpace(pagePreview?.ImageDataUrl))
{ {
<img class="sender-image-editor__page-image" <img class="sender-image-editor__page-image"
src="@imageDataUrl" src="@(pagePreview!.ImageDataUrl)"
alt="PDF Page @pageNumber" alt="PDF Page @pageNumber"
draggable="false" /> draggable="false" />
} }
@@ -44,7 +50,7 @@
{ {
<button type="button" <button type="button"
class="sender-image-editor__field" class="sender-image-editor__field"
style="@GetFieldStyle(field)" style="@GetFieldStyle(field, widthPt, heightPt)"
title="@field.ReceiverName" title="@field.ReceiverName"
@onclick:stopPropagation="true" @onclick:stopPropagation="true"
@onclick="() => RemoveField.InvokeAsync(field.Index)"> @onclick="() => RemoveField.InvokeAsync(field.Index)">
@@ -66,7 +72,7 @@
public int PageCount { get; set; } public int PageCount { get; set; }
[Parameter] [Parameter]
public IReadOnlyDictionary<int, string> PageImageDataUrls { get; set; } = new Dictionary<int, string>(); public IReadOnlyList<PdfPageImageDto> Pages { get; set; } = Array.Empty<PdfPageImageDto>();
[Parameter] [Parameter]
public string? PdfDataUrl { get; set; } public string? PdfDataUrl { get; set; }
@@ -80,12 +86,12 @@
[Parameter] [Parameter]
public EventCallback<int> RemoveField { get; set; } public EventCallback<int> RemoveField { get; set; }
private static string GetFieldStyle(SenderPdfFieldOverlay field) private static string GetFieldStyle(SenderPdfFieldOverlay field, double pageWidthPt, double pageHeightPt)
{ {
var leftPercent = (field.XPt / 595.0) * 100.0; var leftPercent = (field.XPt / pageWidthPt) * 100.0;
var topPercent = (field.YPt / 842.0) * 100.0; var topPercent = (field.YPt / pageHeightPt) * 100.0;
var widthPercent = (127.44 / 595.0) * 100.0; var widthPercent = (127.44 / pageWidthPt) * 100.0;
var heightPercent = (141.12 / 842.0) * 100.0; var heightPercent = (141.12 / pageHeightPt) * 100.0;
return string.Join(' ', return string.Join(' ',
$"left:{leftPercent:F4}%;", $"left:{leftPercent:F4}%;",

View File

@@ -14,6 +14,7 @@
@inject AppVersionService AppVersion @inject AppVersionService AppVersion
@inject ILogger<SenderEditorPage> Logger @inject ILogger<SenderEditorPage> Logger
@inject DocumentService DocumentService @inject DocumentService DocumentService
@inject PdfRenderService PdfRenderService
@inject ReceiverService ReceiverService @inject ReceiverService ReceiverService
@inject EnvelopeService EnvelopeService @inject EnvelopeService EnvelopeService
@inject IStringLocalizer<Resource> UiLocalizer @inject IStringLocalizer<Resource> UiLocalizer
@@ -278,7 +279,7 @@
{ {
<SenderPdfImageEditor IsPlacementMode="@(_pendingReceiverForPlacement is not null)" <SenderPdfImageEditor IsPlacementMode="@(_pendingReceiverForPlacement is not null)"
PageCount="@_pageCount" PageCount="@_pageCount"
PageImageDataUrls="@_pageImageDataUrls" Pages="@_pagePreviews"
PdfDataUrl="@PdfDataUrl" PdfDataUrl="@PdfDataUrl"
Fields="@FieldOverlays" Fields="@FieldOverlays"
EditorClick="OnPdfAreaClickAsync" EditorClick="OnPdfAreaClickAsync"
@@ -471,7 +472,7 @@
string? _errorMessage; string? _errorMessage;
byte[]? _originalPdfBytes; // Pristine upload — never modified, used as base for redraw byte[]? _originalPdfBytes; // Pristine upload — never modified, used as base for redraw
int _pageCount; int _pageCount;
Dictionary<int, string> _pageImageDataUrls = []; List<PdfPageImageDto> _pagePreviews = [];
int? _loadedEnvelopeId; int? _loadedEnvelopeId;
int? _editingEnvelopeId; int? _editingEnvelopeId;
bool _isLoadingEnvelopeForEdit; bool _isLoadingEnvelopeForEdit;
@@ -594,9 +595,11 @@
_originalPdfBytes = document?.ByteData; _originalPdfBytes = document?.ByteData;
_pdfLoaded = _originalPdfBytes is { Length: > 0 }; _pdfLoaded = _originalPdfBytes is { Length: > 0 };
_pageCount = EstimatePdfPageCount(_originalPdfBytes) ?? _pagePreviews = [];
Math.Max(1, _signatureFields.Select(f => f.Page).DefaultIfEmpty(1).Max()); _pageCount = _pagePreviews.Count > 0
_pageImageDataUrls.Clear(); ? _pagePreviews.Count
: EstimatePdfPageCount(_originalPdfBytes)
?? Math.Max(1, _signatureFields.Select(f => f.Page).DefaultIfEmpty(1).Max());
_fileName = $"Envelope-{envelopeId}.pdf"; _fileName = $"Envelope-{envelopeId}.pdf";
_pendingReceiverForPlacement = null; _pendingReceiverForPlacement = null;
_loadedEnvelopeId = envelopeId; _loadedEnvelopeId = envelopeId;
@@ -638,8 +641,12 @@
_originalPdfBytes = ms.ToArray(); _originalPdfBytes = ms.ToArray();
_fileName = file.Name; _fileName = file.Name;
_pdfLoaded = true; _pdfLoaded = true;
_pageCount = EstimatePdfPageCount(_originalPdfBytes) ?? 1; _pagePreviews = (await PdfRenderService.RenderPagesAsync(_originalPdfBytes))
_pageImageDataUrls.Clear(); .OrderBy(p => p.PageNumber)
.ToList();
_pageCount = _pagePreviews.Count > 0
? _pagePreviews.Count
: EstimatePdfPageCount(_originalPdfBytes) ?? 1;
_signatureFields.Clear(); _signatureFields.Clear();
_pendingReceiverForPlacement = null; _pendingReceiverForPlacement = null;
@@ -682,10 +689,10 @@
return; return;
} }
// Approximate points based on A4. This is temporary until the image-based viewer int page1Based = coords.PageIndex + 1;
// pipeline provides precise per-page dimensions. var pagePreview = _pagePreviews.FirstOrDefault(p => p.PageNumber == page1Based);
const double pageWidthPt = 595.0; var pageWidthPt = pagePreview?.WidthPt ?? 595.0;
const double pageHeightPt = 842.0; var pageHeightPt = pagePreview?.HeightPt ?? 842.0;
// Convert normalised [0,1] → PDF points; clamp so box stays inside page // Convert normalised [0,1] → PDF points; clamp so box stays inside page
double xPt = coords.NormX * pageWidthPt; double xPt = coords.NormX * pageWidthPt;
@@ -694,8 +701,6 @@
xPt = Math.Max(0, Math.Min(xPt, pageWidthPt - SigWidthPt)); xPt = Math.Max(0, Math.Min(xPt, pageWidthPt - SigWidthPt));
yPt = Math.Max(0, Math.Min(yPt, pageHeightPt - SigHeightPt)); yPt = Math.Max(0, Math.Min(yPt, pageHeightPt - SigHeightPt));
int page1Based = coords.PageIndex + 1;
var field = new SignatureFieldDraft( var field = new SignatureFieldDraft(
XPt: xPt, XPt: xPt,
YPt: yPt, YPt: yPt,