Support flexible coordinate systems for PDF signatures
Added enums `PdfSignatureCoordinateUnit` and `PdfSignatureCoordinateOrigin` to support defining signature coordinates in points or inches and relative to either the bottom-left or top-left corner of the page. Updated `PdfSignatureAppearanceDto` to include `CoordinateUnit` and `CoordinateOrigin` parameters with default values. Refactored `PdfDigitalSignatureService` to use a new `BuildSignatureBounds` method for calculating signature bounds, which handles unit conversion, origin adjustments, and validation. Introduced `GetPageHeightInPoints` to retrieve page height for top-left origin calculations. Improved signature field naming precision and added error handling for invalid dimensions and page numbers. Included a TODO for optimizing the signature check algorithm.
This commit is contained in:
@@ -43,8 +43,8 @@ public sealed class PdfDigitalSignatureService(IOptions<Pkcs11Options> 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<Pkcs11Options> 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<PdfSignatureCheckResultDto> CheckPdfSignatureAsync(Stream inputPdf, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user