diff --git a/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs b/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs
index eedeb896..be38a95c 100644
--- a/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs
+++ b/EnvelopeGenerator.Application/Common/Dto/DocReceiverElementDto.cs
@@ -18,6 +18,11 @@ public class DocReceiverElementDto : IDocReceiverElement, ISignatureInkPayload
///
public int DocumentId { get; set; }
+ ///
+ ///
+ ///
+ public virtual DocumentDto? Document { get; set; }
+
///
/// Gets or sets the identifier of the receiver.
///
diff --git a/EnvelopeGenerator.Application/DocReceiverElements/Queries/ReadDocReceiverElementQuery.cs b/EnvelopeGenerator.Application/DocReceiverElements/Queries/ReadDocReceiverElementQuery.cs
index 3139051b..95dcac71 100644
--- a/EnvelopeGenerator.Application/DocReceiverElements/Queries/ReadDocReceiverElementQuery.cs
+++ b/EnvelopeGenerator.Application/DocReceiverElements/Queries/ReadDocReceiverElementQuery.cs
@@ -14,6 +14,15 @@ namespace EnvelopeGenerator.Application.DocReceiverElements.Queries;
///
public record ReadDocReceiverElementQuery : EnvelopeReceiverQueryBase, IRequest>
{
+ ///
+ ///
+ ///
+ public int? Id { get; set; } = null;
+
+ ///
+ ///
+ ///
+ public bool IncludeDocument { get; set; } = false;
}
///
@@ -47,7 +56,10 @@ public class ReadDocReceiverElementQueryHandler : IRequestHandler e.Id == request.Id);
+
+ if (request.Envelope.Id is int envelopeId)
q = q.Where(e => e.Document.EnvelopeId == envelopeId);
if (request.Envelope.Uuid is string envelopeUuid)
@@ -59,6 +71,9 @@ public class ReadDocReceiverElementQueryHandler : IRequestHandler e.Receiver.Signature == signature);
+ if( request.IncludeDocument)
+ q = q.Include(e => e.Document);
+
var elements = await q.ToListAsync(cancellationToken);
return _mapper.Map>(elements);
diff --git a/EnvelopeGenerator.Application/PdfSigning/SignPdfByElementCommand.cs b/EnvelopeGenerator.Application/PdfSigning/SignPdfByElementCommand.cs
new file mode 100644
index 00000000..988973a4
--- /dev/null
+++ b/EnvelopeGenerator.Application/PdfSigning/SignPdfByElementCommand.cs
@@ -0,0 +1,85 @@
+using DigitalData.Core.Exceptions;
+using EnvelopeGenerator.Application.Common.Dto;
+using EnvelopeGenerator.Application.Common.Interfaces.Doc;
+using EnvelopeGenerator.Application.Common.Interfaces.Services;
+using EnvelopeGenerator.Application.DocReceiverElements.Queries;
+using MediatR;
+
+namespace EnvelopeGenerator.Application.PdfSigning;
+
+///
+///
+///
+public record SignPdfByElementCommand : IRequest
+{
+ ///
+ ///
+ ///
+ public DocReceiverElementDto? Element { get; set; }
+
+ ///
+ ///
+ ///
+ public int? ElementId { get; init; }
+}
+
+///
+///
+///
+///
+public class SignPdfCommandHandler(ISender sender, IPdfDigitalSignatureService signService, IPdfPageImageRenderer imgRenderer) : IRequestHandler
+{
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task Handle(SignPdfByElementCommand request, CancellationToken cancellationToken)
+ {
+ // ensure element
+ if(request.Element is null)
+ {
+ var elements = await sender.Send(new ReadDocReceiverElementQuery()
+ {
+ Id = request.ElementId!,
+ IncludeDocument= true
+ }, cancellationToken);
+
+ if (!elements.Any())
+ throw new NotFoundException($"Element with ID {request.ElementId} not found.");
+
+ request.Element = elements.FirstOrDefault();
+ }
+
+ // ensure document
+ var doc = request.Element!.Document;
+
+ if(doc is null || doc.ByteData is null || doc.ByteData.Length == 0)
+ throw new NotFoundException("Document data is missing or empty.");
+
+ // create signature image from element
+ var img = imgRenderer.ComposeSignaturePreviewPng(request.Element);
+
+ // sign pdf
+ var inputPdf = new MemoryStream(doc.ByteData);
+ var outputPdf = new MemoryStream();
+
+ await signService.SignPdfAsync(
+ inputPdf: inputPdf,
+ outputPdf: outputPdf,
+ options: new PdfSignOptionsDto
+ {
+ Appearance = new PdfSignatureAppearanceDto(
+ PageNumber: request.Element.Page,
+ Left: request.Element.Left,
+ Bottom: request.Element.Y,
+ Width: request.Element.Width,
+ Height: request.Element.Height)
+ },
+ image: img,
+ cancellationToken: cancellationToken);
+
+ return outputPdf;
+ }
+}
\ No newline at end of file
diff --git a/EnvelopeGenerator.Application/PdfSigning/SignPdfByElementCommandValidator.cs b/EnvelopeGenerator.Application/PdfSigning/SignPdfByElementCommandValidator.cs
new file mode 100644
index 00000000..35a9019e
--- /dev/null
+++ b/EnvelopeGenerator.Application/PdfSigning/SignPdfByElementCommandValidator.cs
@@ -0,0 +1,18 @@
+using FluentValidation;
+
+namespace EnvelopeGenerator.Application.PdfSigning;
+
+public class SignPdfByElementCommandValidator : AbstractValidator
+{
+ public SignPdfByElementCommandValidator()
+ {
+ RuleFor(x => x)
+ .Must(x => (x.Element is not null) ^ x.ElementId.HasValue)
+ .WithMessage("Exactly one of Element or ElementId must be provided.");
+
+ RuleFor(x => x.ElementId)
+ .GreaterThan(0)
+ .When(x => x.ElementId.HasValue)
+ .WithMessage("ElementId must be greater than 0.");
+ }
+}