using DocumentService.Client.Interfaces; using DocumentService.Client.Models.Requests; using EnvelopeGenerator.Application.Common.Dto; using EnvelopeGenerator.Application.Common.Interfaces.Services; using ClientCertLevel = DocumentService.Client.Models.Requests.PdfSignatureCertificationLevel; using ClientCoordUnit = DocumentService.Client.Models.Requests.PdfSignatureCoordinateUnit; using ClientCoordOrigin = DocumentService.Client.Models.Requests.PdfSignatureCoordinateOrigin; namespace EnvelopeGenerator.Infrastructure.Crypt.Services; /// /// Proxy that forwards PDF signing to the remote DocumentService API via . /// public sealed class PdfDigitalSignatureService(IPdfSignatureClient signatureClient) : IPdfDigitalSignatureService { public async Task SignPdfAsync(Stream inputPdf, Stream outputPdf, PdfSignOptionsDto? options = null, Stream? image = null, CancellationToken cancellationToken = default) { var pdfBytes = await ReadAllBytesAsync(inputPdf, cancellationToken); var imageBytes = image is null ? null : await ReadAllBytesAsync(image, cancellationToken); using var signed = await signatureClient.SignAsync(pdfBytes, Map(options), imageBytes, cancellationToken); await signed.CopyToAsync(outputPdf, cancellationToken); } public Task CheckPdfSignatureAsync(Stream inputPdf, CancellationToken cancellationToken = default) => throw new NotSupportedException("Signature checking is handled by DocumentService and is not available through this proxy."); private static SignPdfOptions Map(PdfSignOptionsDto? o) => new() { DocumentElementId = o?.DocumentElementId ?? 0, Name = o?.Name ?? string.Empty, Reason = o?.Reason, Location = o?.Location, Email = o?.Email, Phone = o?.Phone, ApplicationName = o?.ApplicationName, CertificationLevel = (ClientCertLevel)(o?.CertificationLevel ?? EnvelopeGenerator.Application.Common.Enums.PdfSignatureCertificationLevel.NoCertification), Appearance = o?.Appearance is null ? null : MapAppearance(o.Appearance) }; private static PdfSignatureAppearance MapAppearance(PdfSignatureAppearanceDto a) => new() { PageNumber = a.PageNumber, Left = a.Left, Bottom = a.Bottom, Width = a.Width, Height = a.Height, ShowLabels = a.ShowLabels, ShowName = a.ShowName, ShowDate = a.ShowDate, ShowReason = a.ShowReason, ShowLocation = a.ShowLocation, ShowDistinguishedName = a.ShowDistinguishedName, DateTimeFormat = a.DateTimeFormat, CoordinateUnit = (ClientCoordUnit)a.CoordinateUnit, CoordinateOrigin = (ClientCoordOrigin)a.CoordinateOrigin, Font = new PdfSignatureFont { FamilyName = a.Font.FamilyName, Size = a.Font.Size, IsBold = a.Font.IsBold, IsItalic = a.Font.IsItalic, IsUnderline = a.Font.IsUnderline, Color = new PdfSignatureColor { R = a.Font.Color.R, G = a.Font.Color.G, B = a.Font.Color.B } } }; private static async Task ReadAllBytesAsync(Stream stream, CancellationToken cancellationToken) { if (stream is MemoryStream memory && memory.TryGetBuffer(out var buffer)) return buffer.AsSpan(0, (int)memory.Length).ToArray(); using var copy = new MemoryStream(); await stream.CopyToAsync(copy, cancellationToken); return copy.ToArray(); } }