diff --git a/EnvelopeGenerator.Application/Common/Dto/PdfSignatureAppearanceDto.cs b/EnvelopeGenerator.Application/Common/Dto/PdfSignatureAppearanceDto.cs
index a3716d4e..f3afa1fc 100644
--- a/EnvelopeGenerator.Application/Common/Dto/PdfSignatureAppearanceDto.cs
+++ b/EnvelopeGenerator.Application/Common/Dto/PdfSignatureAppearanceDto.cs
@@ -3,7 +3,38 @@ namespace EnvelopeGenerator.Application.Common.Dto;
///
///
///
-///
+public enum PdfSignatureCoordinateUnit
+{
+ ///
+ ///
+ ///
+ Point = 0,
+
+ ///
+ ///
+ ///
+ Inch = 1
+}
+
+///
+///
+///
+public enum PdfSignatureCoordinateOrigin
+{
+ ///
+ ///
+ ///
+ BottomLeft = 0,
+
+ ///
+ ///
+ ///
+ TopLeft = 1
+}
+
+///
+///
+///
///
///
///
@@ -16,6 +47,8 @@ namespace EnvelopeGenerator.Application.Common.Dto;
///
///
///
+///
+///
public sealed record PdfSignatureAppearanceDto(
int PageNumber,
double Left,
@@ -28,7 +61,9 @@ public sealed record PdfSignatureAppearanceDto(
bool ShowReason = false,
bool ShowLocation = false,
bool ShowDistinguishedName = false,
- string DateTimeFormat = "dd.MM.yyyy HH:mm:ss zzz")
+ string DateTimeFormat = "dd.MM.yyyy HH:mm:ss zzz",
+ PdfSignatureCoordinateUnit CoordinateUnit = PdfSignatureCoordinateUnit.Point,
+ PdfSignatureCoordinateOrigin CoordinateOrigin = PdfSignatureCoordinateOrigin.BottomLeft)
{
///
///
diff --git a/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs b/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs
index e84280f0..6f26379d 100644
--- a/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs
+++ b/EnvelopeGenerator.Infrastructure.Crypt/Services/PdfDigitalSignatureService.cs
@@ -43,8 +43,8 @@ public sealed class PdfDigitalSignatureService(IOptions options)
var builder = request.Appearance is null ? new PdfDeferredSignatureBuilder(signerInfo) : new (signerInfo, new PdfSignatureFieldInfo(request.Appearance.PageNumber)
{
- Name = $"Sig_{DateTimeOffset.UtcNow:yyyyMMddHHmmss}",
- SignatureBounds = new PdfRectangle(request.Appearance.Left, request.Appearance.Bottom, request.Appearance.Left + request.Appearance.Width, request.Appearance.Bottom + request.Appearance.Height)
+ Name = $"Sig_{DateTimeOffset.UtcNow:yyyyMMddHHmmssfff}",
+ SignatureBounds = BuildSignatureBounds(request.Appearance, workingInput)
});
builder.Name = request.Name;
@@ -105,6 +105,61 @@ public sealed class PdfDigitalSignatureService(IOptions options)
}
}
+ private static PdfRectangle BuildSignatureBounds(PdfSignatureAppearanceDto appearance, Stream inputPdf)
+ {
+ var left = appearance.Left;
+ var bottom = appearance.Bottom;
+ var width = appearance.Width;
+ var height = appearance.Height;
+
+ if (appearance.CoordinateUnit == PdfSignatureCoordinateUnit.Inch)
+ {
+ left *= 72.0;
+ bottom *= 72.0;
+ width *= 72.0;
+ height *= 72.0;
+ }
+
+ if (width <= 0 || height <= 0)
+ throw new BadRequestException("Signature appearance width and height must be greater than zero.");
+
+ if (appearance.CoordinateOrigin == PdfSignatureCoordinateOrigin.TopLeft)
+ {
+ var pageHeight = GetPageHeightInPoints(inputPdf, appearance.PageNumber);
+ bottom = pageHeight - bottom - height;
+ }
+
+ return new PdfRectangle(left, bottom, left + width, bottom + height);
+ }
+
+ private static double GetPageHeightInPoints(Stream inputPdf, int pageNumber)
+ {
+ if (pageNumber <= 0)
+ throw new BadRequestException("Signature page number must be greater than zero.");
+
+ var previousPosition = inputPdf.CanSeek ? inputPdf.Position : 0;
+
+ try
+ {
+ if (inputPdf.CanSeek)
+ inputPdf.Position = 0;
+
+ using var processor = new PdfDocumentProcessor();
+ processor.LoadDocument(inputPdf);
+
+ var pageCount = processor.Document.Pages.Count;
+ if (pageNumber > pageCount)
+ throw new BadRequestException($"Signature page number {pageNumber} is out of range. Document has {pageCount} page(s).");
+
+ return processor.Document.Pages[pageNumber - 1].CropBox.Height;
+ }
+ finally
+ {
+ if (inputPdf.CanSeek)
+ inputPdf.Position = previousPosition;
+ }
+ }
+
//TODO: verify and optimize (remove memory stream etc.) the check algortihm
public async Task CheckPdfSignatureAsync(Stream inputPdf, CancellationToken cancellationToken = default)
{