@@ -169,7 +169,7 @@
SignatureCaptureDto? _sig;
// ----- Submit / logout state -----
- bool _isLoggingOut = false;
+ bool _isLoggingOut = false;
bool _submitConfirmVisible = false;
string? _submitErrorMessage;
@@ -262,7 +262,7 @@
}
_envelopeReceiver = await PageDataService.GetEnvelopeReceiverAsync(EnvelopeKey);
- _signatures = await PageDataService.GetSignaturesAsync(_receiverUser);
+ _signatures = await PageDataService.GetSignaturesAsync(_receiverUser);
// Burn signature image + info onto PDF via PdfSharp
if (_sig is not null && _signatures.Count > 0)
@@ -272,13 +272,13 @@
{
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4,
Landscape = false,
- Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0),
+ Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0),
};
var detail = new DetailBand();
report.Bands.Add(detail);
detail.Controls.Add(new XRPdfContent
{
- Source = pdfBytes,
+ Source = pdfBytes,
GenerateOwnPages = true,
});
_report = report;
@@ -317,11 +317,11 @@
// ----- PDF signature rendering -----
///
- /// Uses DevExpress PdfGraphics to burn the captured signature onto the PDF at each signature field.
- /// Layout per field (input coordinates are top-left origin, units = PDF points):
- /// [top 52%] signature image (aspect-ratio preserved)
+ /// Uses PdfSharp to burn the captured signature onto the PDF at each signature field.
+ /// Layout per field (top-left origin, Y down, units = PDF points):
+ /// [top 65%] signature image
/// [separator line]
- /// [bottom] FullName / Position (optional) / Place, Date
+ /// [bottom 35%] FullName (bold) / Position (optional) / Place, Date
///
static byte[] DrawSignaturesOnPdf(
byte[] pdfBytes,
@@ -333,96 +333,100 @@
using var inputMs = new System.IO.MemoryStream(pdfBytes);
using var outputMs = new System.IO.MemoryStream();
- using var processor = new DevExpress.Pdf.PdfDocumentProcessor();
- processor.LoadDocument(inputMs);
- const double sigW = 1.77 * 72;
- const double sigH = 1.96 * 72;
- const double imgRatio = 0.52;
- const double lineH = 11.5;
- const double bgPad = 3.0;
- const double padding = 3.0;
+ var document = PdfSharp.Pdf.IO.PdfReader.Open(
+ inputMs, PdfSharp.Pdf.IO.PdfDocumentOpenMode.Modify);
- using var bgBrush = new DevExpress.Drawing.DXSolidBrush(System.Drawing.Color.FromArgb(255, 255, 253, 240));
- using var linePen = new DevExpress.Drawing.DXPen(System.Drawing.Color.FromArgb(180, 100, 100, 120), 0.5f);
- using var nameBrush = new DevExpress.Drawing.DXSolidBrush(System.Drawing.Color.FromArgb(255, 20, 20, 20));
- using var infoBrush = new DevExpress.Drawing.DXSolidBrush(System.Drawing.Color.FromArgb(255, 80, 80, 80));
- var fontBold = new DevExpress.Drawing.DXFont("Arial", 7.5f, DevExpress.Drawing.DXFontStyle.Bold);
- var fontNormal = new DevExpress.Drawing.DXFont("Arial", 6.5f, DevExpress.Drawing.DXFontStyle.Regular);
+ const double sigW = 1.77 * 72; // 127.44 pt
+ const double sigH = 1.96 * 72; // 141.12 pt
+ const double imgRatio = 0.52; // top 52% = image
+ const double lineH = 11.5; // fixed row height matching font size (bold 7.5pt + normal 6.5pt)
+ const double bgPad = 3.0; // background box padding around content (pt)
+
+ var black = PdfSharp.Drawing.XColor.FromArgb(255, 20, 20, 20);
+ var darkGray = PdfSharp.Drawing.XColor.FromArgb(255, 80, 80, 80);
+ var lineColor = PdfSharp.Drawing.XColor.FromArgb(180, 100, 100, 120);
+
+ var bgColor = PdfSharp.Drawing.XColor.FromArgb(255, 255, 253, 240);
+ var bgBrush = new PdfSharp.Drawing.XSolidBrush(bgColor);
+
+ var fontBold = new PdfSharp.Drawing.XFont("Arial", 7.5, PdfSharp.Drawing.XFontStyleEx.Bold);
+ var fontNormal = new PdfSharp.Drawing.XFont("Arial", 6.5, PdfSharp.Drawing.XFontStyleEx.Regular);
+ var linePen = new PdfSharp.Drawing.XPen(lineColor, 0.5);
+
+ var fmtLeft = new PdfSharp.Drawing.XStringFormat
+ {
+ Alignment = PdfSharp.Drawing.XStringAlignment.Near,
+ LineAlignment = PdfSharp.Drawing.XLineAlignment.Near,
+ };
var date = DateTime.Now.ToString("dd.MM.yyyy");
foreach (var field in signatures)
{
- var pageIndex = field.Page - 1;
- if (pageIndex < 0 || pageIndex >= processor.Document.Pages.Count)
- continue;
+ int pageIndex = field.Page - 1;
+ if (pageIndex < 0 || pageIndex >= document.PageCount) continue;
- var page = processor.Document.Pages[pageIndex];
- var pageHeight = page.CropBox.Height;
+ var page = document.Pages[pageIndex];
- var x = field.X;
- var y = field.Y;
+ using var gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(page);
- var imgH = sigH * imgRatio;
- var lineY = y + imgH + 1.0;
- var textY = lineY + 1.5;
- var row1Y = textY;
- var row2Y = row1Y + lineH;
- var row3Y = string.IsNullOrWhiteSpace(sig.Position) ? row2Y : row2Y + lineH;
- var contentBottom = row3Y + lineH;
+ double x = field.X;
+ double y = field.Y;
- using var graphics = processor.CreateGraphics();
+ // --- Calculate layout positions first (needed for bg rect) ---
+ double imgH = sigH * imgRatio;
+ double lineY = y + imgH + 1.0; // 1pt gap between image and separator
+ double textY = lineY + 1.5; // 1.5pt gap below separator line
+ double padding = 3;
- var bgRect = ToPdfRect(x - bgPad, y - bgPad, sigW + (bgPad * 2), (contentBottom - y) + (bgPad * 2), pageHeight);
- graphics.FillRectangle(bgBrush, bgRect);
+ // Row 1: FullName
+ double row1Y = textY;
+ // Row 2: Position (optional)
+ double row2Y = row1Y + lineH;
+ // Row 3: Place, Date — immediately after row2 regardless of position
+ double row3Y = !string.IsNullOrWhiteSpace(sig.Position) ? row2Y + lineH : row2Y;
+ double contentBottom = row3Y + lineH;
- var imageArea = ToPdfRect(x, y, sigW, imgH, pageHeight);
- var imageRect = FitImageContain(imageArea, imgBytes);
- graphics.DrawImage(imgBytes, imageRect);
+ // --- Background rectangle sized to actual content (not full sigH) ---
+ var bgRect = new PdfSharp.Drawing.XRect(
+ x - bgPad,
+ y - bgPad,
+ sigW + bgPad * 2,
+ (contentBottom - y) + bgPad * 2);
+ gfx.DrawRectangle(bgBrush, bgRect);
- var lineYPdf = ToPdfY(lineY, pageHeight);
- graphics.DrawLine(linePen, (float)(x + 2), lineYPdf, (float)(x + sigW - 2), lineYPdf);
+ // --- Image area ---
+ var imgRect = new PdfSharp.Drawing.XRect(x, y, sigW, imgH);
+ using var imgStream = new System.IO.MemoryStream(imgBytes);
+ var xImg = PdfSharp.Drawing.XImage.FromStream(imgStream);
+ gfx.DrawImage(xImg, imgRect);
- graphics.DrawString(sig.FullName, fontBold, nameBrush, ToPdfRect(x + padding, row1Y, sigW - (padding * 2), lineH, pageHeight));
+ // --- Separator line ---
+ gfx.DrawLine(linePen, x + 2, lineY, x + sigW - 2, lineY);
+ // --- Text rows ---
+ // Row 1: FullName (bold)
+ var nameRect = new PdfSharp.Drawing.XRect(x + padding, row1Y, sigW - padding * 2, lineH);
+ gfx.DrawString(sig.FullName, fontBold, new PdfSharp.Drawing.XSolidBrush(black), nameRect, fmtLeft);
+
+ // Row 2: Position (optional)
if (!string.IsNullOrWhiteSpace(sig.Position))
{
- graphics.DrawString(sig.Position, fontNormal, infoBrush, ToPdfRect(x + padding, row2Y, sigW - (padding * 2), lineH, pageHeight));
+ var posRect = new PdfSharp.Drawing.XRect(x + padding, row2Y, sigW - padding * 2, lineH);
+ gfx.DrawString(sig.Position, fontNormal, new PdfSharp.Drawing.XSolidBrush(darkGray), posRect, fmtLeft);
}
- graphics.DrawString($"{sig.Place}, {date}", fontNormal, infoBrush, ToPdfRect(x + padding, row3Y, sigW - (padding * 2), lineH, pageHeight));
-
- graphics.AddToPageForeground(page, 72, 72);
+ // Row 3: Place, Date
+ var placeDate = $"{sig.Place}, {date}";
+ var dateRect = new PdfSharp.Drawing.XRect(x + padding, row3Y, sigW - padding * 2, lineH);
+ gfx.DrawString(placeDate, fontNormal, new PdfSharp.Drawing.XSolidBrush(darkGray), dateRect, fmtLeft);
}
- processor.SaveDocument(outputMs);
+ document.Save(outputMs);
return outputMs.ToArray();
}
- static System.Drawing.RectangleF FitImageContain(System.Drawing.RectangleF area, byte[] imageBytes)
- {
- using var imageStream = new System.IO.MemoryStream(imageBytes, writable: false);
- using var image = DevExpress.Drawing.DXImage.FromStream(imageStream);
-
- if (image.Width <= 0 || image.Height <= 0)
- return area;
-
- var scale = Math.Min(area.Width / image.Width, area.Height / image.Height);
- var drawW = image.Width * scale;
- var drawH = image.Height * scale;
- var drawX = area.X + ((area.Width - drawW) / 2f);
- var drawY = area.Y + ((area.Height - drawH) / 2f);
-
- return new System.Drawing.RectangleF(drawX, drawY, drawW, drawH);
- }
-
- static System.Drawing.RectangleF ToPdfRect(double x, double y, double width, double height, double pageHeight)
- => new((float)x, (float)(pageHeight - y - height), (float)width, (float)height);
-
- static float ToPdfY(double y, double pageHeight)
- => (float)(pageHeight - y);
-
static byte[]? DataUrlToBytes(string? dataUrl)
{
if (string.IsNullOrWhiteSpace(dataUrl)) return null;
diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfSignatureController.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfSignatureController.cs
index d35bbb86..bd046d93 100644
--- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfSignatureController.cs
+++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfSignatureController.cs
@@ -1,5 +1,9 @@
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
+using EnvelopeGenerator.Application.PdfSigning;
+using EnvelopeGenerator.Domain.Constants;
+using EnvelopeGenerator.Server.Extensions;
+using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -8,11 +12,37 @@ namespace EnvelopeGenerator.Server.Controllers;
///
///
///
-///
+///
+///
[ApiController]
[Route("api/[controller]")]
-public class PdfSignatureController(IPdfDigitalSignatureService signatureService) : ControllerBase
+public class PdfSignatureController(IPdfDigitalSignatureService SignatureService, IMediator Mediator) : ControllerBase
{
+ ///
+ /// Gets the document for the specified envelope key.
+ ///
+ ///
+ ///
+ ///
+ [Authorize(Policy = AuthPolicy.Receiver)]
+ [HttpPost("{envelopeKey}")]
+ public async Task
SignDocument(string envelopeKey, CancellationToken cancel)
+ {
+ int envelopeId = User.EnvelopeId();
+ int receiverId = User.ReceiverId();
+
+ var senderDoc = await Mediator.Send(new SignPdfByEnvelopeReceiverCommand
+ {
+ EnvelopeId = envelopeId,
+ ReceiverId = receiverId
+ }, cancel);
+
+ senderDoc.Position = 0;
+ var fileName = Path.GetFileNameWithoutExtension(envelopeKey);
+ return File(senderDoc, "application/pdf", $"{fileName}-signed.pdf");
+ }
+
+ #region Test Endpoints
///
///
///
@@ -57,7 +87,7 @@ public class PdfSignatureController(IPdfDigitalSignatureService signatureService
var output = new MemoryStream();
- await signatureService.SignPdfAsync(input, output, request.Options, signatureImage, cancel);
+ await SignatureService.SignPdfAsync(input, output, request.Options, signatureImage, cancel);
output.Position = 0;
var fileName = Path.GetFileNameWithoutExtension(request.File.FileName);
@@ -76,7 +106,8 @@ public class PdfSignatureController(IPdfDigitalSignatureService signatureService
public async Task IsSigned([FromForm] CheckPdfSignatureForm request, CancellationToken cancel)
{
await using var input = request.File.OpenReadStream();
- var result = await signatureService.CheckPdfSignatureAsync(input, cancel);
+ var result = await SignatureService.CheckPdfSignatureAsync(input, cancel);
return Ok(result);
}
+ #endregion
}
diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/appsettings.json b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/appsettings.json
index 51bf6274..59647559 100644
--- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/appsettings.json
+++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/appsettings.json
@@ -290,5 +290,9 @@
"MarginTopRatio": -0.1
}
]
+ },
+ "ESignParams": {
+ "ApplicationName": "signFLOW",
+ "DefaultReason": " Bestätigt"
}
}