using DevExpress.Drawing; using DevExpress.Pdf; using DigitalData.Core.Exceptions; using EnvelopeGenerator.Application.Common.Dto; using EnvelopeGenerator.Application.Common.Interfaces.Doc; using EnvelopeGenerator.Domain.Interfaces; namespace EnvelopeGenerator.Infrastructure.Doc; public class PdfPageImageRenderer : IPdfPageImageRenderer { private const float RenderDpi = 110f; public Task> RenderPagesAsImagesAsync(byte[] pdfBytes, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(pdfBytes); if (pdfBytes.Length == 0) return Task.FromResult>(Array.Empty()); using var stream = new MemoryStream(pdfBytes, writable: false); using var processor = new PdfDocumentProcessor(); processor.LoadDocument(stream); var pages = processor.Document.Pages; if (pages.Count == 0) return Task.FromResult>(Array.Empty()); var renderOptions = PdfPageRenderingParameters.CreateWithResolution(RenderDpi); var results = new List(pages.Count); for (var i = 0; i < pages.Count; i++) { cancellationToken.ThrowIfCancellationRequested(); var pageNumber = i + 1; var page = pages[i]; var cropBox = page.CropBox; using DXBitmap bitmap = processor.CreateDXBitmap(pageNumber, renderOptions); using var imageStream = new MemoryStream(); bitmap.Save(imageStream, DXImageFormat.Png); var dataUrl = $"data:image/png;base64,{Convert.ToBase64String(imageStream.ToArray())}"; results.Add(new PdfPageImageDto { PageNumber = pageNumber, WidthPt = cropBox.Width, HeightPt = cropBox.Height, WidthPx = bitmap.Width, HeightPx = bitmap.Height, ImageDataUrl = dataUrl }); } return Task.FromResult>(results); } public byte[] ComposeSignaturePreviewPng(ISignatureInkPayload element) { 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 date = element.ChangedWhen!.Value.ToString("dd.MM.yyyy"); var position = string.IsNullOrWhiteSpace(element.Position) ? null : element.Position.Trim(); var x = bgPad; var y = bgPad; var imgH = sigH * imgRatio; var lineY = y + imgH + 1.0; var textY = lineY + 1.5; var row1Y = textY; var row2Y = row1Y + lineH; var row3Y = position is null ? row2Y : row2Y + lineH; var contentBottom = row3Y + lineH; var pageWidth = sigW + (bgPad * 2); var pageHeight = contentBottom + bgPad; var inkBytes = this.EnsureSupportedImageBytes(element.Ink!); using var processor = new PdfDocumentProcessor(); processor.CreateEmptyDocument(); var page = processor.AddNewPage(new PdfRectangle(0, 0, pageWidth, pageHeight)); using var graphics = processor.CreateGraphics(); using var bgBrush = new DXSolidBrush(System.Drawing.Color.FromArgb(255, 255, 253, 240)); using var linePen = new DXPen(System.Drawing.Color.FromArgb(180, 100, 100, 120), 0.5f); using var nameBrush = new DXSolidBrush(System.Drawing.Color.FromArgb(255, 20, 20, 20)); using var infoBrush = new DXSolidBrush(System.Drawing.Color.FromArgb(255, 80, 80, 80)); var fontBold = new DXFont("Arial", 7.5f, DXFontStyle.Bold); var fontNormal = new DXFont("Arial", 6.5f, DXFontStyle.Regular); graphics.FillRectangle(bgBrush, new System.Drawing.RectangleF(0, 0, (float)pageWidth, (float)pageHeight)); var imageArea = new System.Drawing.RectangleF((float)x, (float)y, (float)sigW, (float)imgH); var imageRect = FitImageContain(imageArea, inkBytes); graphics.DrawImage(inkBytes, imageRect); graphics.DrawLine(linePen, (float)(x + 2), (float)lineY, (float)(x + sigW - 2), (float)lineY); graphics.DrawString( element.FullName!.Trim(), fontBold, nameBrush, new System.Drawing.RectangleF((float)(x + padding), (float)row1Y, (float)(sigW - (padding * 2)), (float)lineH)); if (position is not null) { graphics.DrawString( position, fontNormal, infoBrush, new System.Drawing.RectangleF((float)(x + padding), (float)row2Y, (float)(sigW - (padding * 2)), (float)lineH)); } graphics.DrawString( $"{element.Place!.Trim()}, {date}", fontNormal, infoBrush, new System.Drawing.RectangleF((float)(x + padding), (float)row3Y, (float)(sigW - (padding * 2)), (float)lineH)); graphics.AddToPageForeground(page, 72, 72); using var pdfStream = new MemoryStream(); processor.SaveDocument(pdfStream); pdfStream.Position = 0; using var rasterProcessor = new PdfDocumentProcessor(); rasterProcessor.LoadDocument(pdfStream); var renderOptions = PdfPageRenderingParameters.CreateWithResolution(400f); using DXBitmap bitmap = rasterProcessor.CreateDXBitmap(1, renderOptions); using var pngStream = new MemoryStream(); bitmap.Save(pngStream, DXImageFormat.Png); return pngStream.ToArray(); } private static System.Drawing.RectangleF FitImageContain(System.Drawing.RectangleF area, byte[] imageBytes) { using var imageStream = new MemoryStream(imageBytes, writable: false); using var image = 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); } /// /// /// /// /// public bool IsSupportedImageFormat(byte[] bytes) { var isPng = bytes.Length >= 4 && bytes[0] == 0x89 && bytes[1] == 0x50 && bytes[2] == 0x4E && bytes[3] == 0x47; if (isPng) return true; var isJpeg = bytes.Length >= 3 && bytes[0] == 0xFF && bytes[1] == 0xD8 && bytes[2] == 0xFF; return isJpeg; } }