refactor(BurnPdfCommand): extract GdPicture and math helpers into extension classes and simplify BurnPdfCommandHandler

- Refactored BurnPdfCommandHandler to use new extension methods for cleaner annotation handling.
- Introduced ITextStyle interface to generalize font styling for text annotations.
- Updated PDFBurnerParams to implement ITextStyle for consistent font settings reuse.
- Added MathematExtensions for coordinate and unit conversion (ToInches, ToPointF).
- Added GdPictureExtensions to encapsulate annotation-related logic (form fields, images, ink).
- Improved readability and maintainability by removing redundant helper methods.
This commit is contained in:
tekh 2025-11-07 14:46:55 +01:00
parent 5299016b43
commit 6134b58a4c
5 changed files with 260 additions and 161 deletions

View File

@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using EnvelopeGenerator.Application.Common.Interfaces.Model;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
namespace EnvelopeGenerator.Application.Common.Configurations;
@ -6,7 +7,7 @@ namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///
/// </summary>
public class PDFBurnerParams
public class PDFBurnerParams : ITextStyle
{
/// <summary>
///

View File

@ -0,0 +1,176 @@
using EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
using EnvelopeGenerator.Domain.Constants;
using GdPicture14;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using SixLabors.ImageSharp;
using System.Drawing;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Model;
using EnvelopeGenerator.Application.Common.Configurations;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///
/// </summary>
public static class GdPictureExtensions
{
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="page"></param>
/// <param name="value"></param>
/// <param name="textStyle"></param>
public static void AddFormFieldValue(this AnnotationManager manager, double x, double y, double width, double height, int page, string value, ITextStyle textStyle)
{
manager.SelectPage(page);
// Add the text annotation
var ant = manager.AddTextAnnot((float)x, (float)y, (float)width, (float)height, value);
// Set the font properties
ant.FontName = textStyle.FontName;
ant.FontSize = textStyle.FontSize;
ant.FontStyle = textStyle.FontStyle;
manager.SaveAnnotationsToPage();
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="pAnnotation"></param>
/// <param name="formFieldValue"></param>
/// <param name="options"></param>
public static void AddFormFieldValue(this AnnotationManager manager, Annotation pAnnotation, FormFieldValue formFieldValue, PDFBurnerParams options)
{
var ffIndex = options.GetAnnotationIndex(pAnnotation.EgName);
// Convert pixels to Inches
var oBounds = pAnnotation.Bbox?.Select(points => points.ToInches()).ToList();
if (oBounds is null || oBounds.Count < 4)
return;
double oX = oBounds[0];
double oY = oBounds[1] + options.YOffset * ffIndex + options.TopMargin;
double oWidth = oBounds[2];
double oHeight = oBounds[3];
manager.SelectPage(pAnnotation.PageIndex + 1);
// Add the text annotation
var ant = manager.AddTextAnnot((float)oX, (float)oY, (float)oWidth, (float)oHeight, formFieldValue.Value);
// Set the font properties
ant.FontName = options.FontName;
ant.FontSize = options.FontSize;
ant.FontStyle = options.FontStyle;
manager.SaveAnnotationsToPage();
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="page"></param>
/// <param name="base64"></param>
public static void AddImageAnnotation(this AnnotationManager manager, double x, double y, double width, double height, int page, string base64)
{
manager.SelectPage(page);
manager.AddEmbeddedImageAnnotFromBase64(base64, (float)x, (float)y, (float)width, (float)height);
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="pAnnotation"></param>
/// <param name="pAttachments"></param>
public static void AddImageAnnotation(this AnnotationManager manager, Annotation pAnnotation, Dictionary<string, Attachment> pAttachments)
{
var oAttachment = pAttachments
.Where(a => a.Key == pAnnotation.ImageAttachmentId)
.SingleOrDefault();
if (oAttachment.Value == null)
return;
// Convert pixels to Inches
var oBounds = pAnnotation.Bbox?.Select(post => post.ToInches()).ToList();
if (oBounds is null || oBounds.Count < 4)
return;
var oX = oBounds[0];
var oY = oBounds[1];
var oWidth = oBounds[2];
var oHeight = oBounds[3];
manager.SelectPage(pAnnotation.PageIndex + 1);
manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.Binary, (float)oX, (float)oY, (float)oWidth, (float)oHeight);
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="page"></param>
/// <param name="value"></param>
public static void AddInkAnnotation(this AnnotationManager manager, int page, string value)
{
var ink = JsonConvert.DeserializeObject<Ink>(value);
var oSegments = ink?.Lines.Points;
var oColor = ColorTranslator.FromHtml(ink?.StrokeColor ?? "#000000");
manager.SelectPage(page);
if (oSegments is null)
return;
foreach (var oSegment in oSegments)
{
var oPoints = oSegment
.Select(points => points.ToPointF())
.ToArray();
manager.AddFreeHandAnnot(oColor, oPoints);
}
}
/// <summary>
///
/// </summary>
/// <param name="manager"></param>
/// <param name="pAnnotation"></param>
public static void AddInkAnnotation(this AnnotationManager manager, Annotation pAnnotation)
{
var oSegments = pAnnotation.Lines?.Points;
var oColor = ColorTranslator.FromHtml(pAnnotation.StrokeColor ?? "#000000");
manager.SelectPage(pAnnotation.PageIndex + 1);
if (oSegments is null)
return;
foreach (var oSegment in oSegments)
{
var oPoints = oSegment
.Select(points => points.ToPointF())
.ToArray();
manager.AddFreeHandAnnot(oColor, oPoints);
}
}
}

View File

@ -0,0 +1,40 @@
using System.Drawing;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
///
/// </summary>
public static class MathematExtensions
{
/// <summary>
///
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
public static PointF ToPointF(this List<float> points)
{
var pointsInch = points.Select(ToInches).ToList();
return new PointF(pointsInch[0], pointsInch[1]);
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static double ToInches(this double value)
{
return value / 72.0;
}
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static float ToInches(this float value)
{
return value / 72f;
}
}

View File

@ -0,0 +1,27 @@
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
namespace EnvelopeGenerator.Application.Common.Interfaces.Model;
/// <summary>
///
/// </summary>
public interface ITextStyle
{
/// <summary>
///
/// </summary>
public string FontName { get; set; }
/// <summary>
///
/// </summary>
public int FontSize { get; set; }
/// <summary>
///
/// </summary>
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
public FontStyle FontStyle { get; set; }
}

View File

@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System.Drawing;
using EnvelopeGenerator.Application.Common.Extensions;
namespace EnvelopeGenerator.Application.Pdf;
@ -24,10 +24,7 @@ public record BurnPdfCommand(byte[] SourceBuffer, List<string> InstantJSONList,
/// </summary>
public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
{
/// <summary>
///
/// </summary>
private readonly PDFBurnerParams _pdfBurnerParams;
private readonly PDFBurnerParams _options;
private readonly AnnotationManager _manager;
@ -44,19 +41,13 @@ public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
/// <param name="logger"></param>
public BurnPdfCommandHandler(IOptions<PDFBurnerParams> pdfBurnerParams, AnnotationManager manager, IRepository<Signature> signRepo, ILogger<BurnPdfCommandHandler> logger)
{
_pdfBurnerParams = pdfBurnerParams.Value;
_options = pdfBurnerParams.Value;
_manager = manager;
_signRepo = signRepo;
_logger = logger;
}
/// <summary>
///
/// </summary>
/// <param name="pSourceBuffer"></param>
/// <param name="elements"></param>
/// <returns></returns>
public byte[] BurnElementAnnotsToPDF(byte[] pSourceBuffer, List<Signature> elements)
private byte[] BurnElementAnnotsToPDF(byte[] pSourceBuffer, List<Signature> elements)
{
// Add background
using var doc = PdfEditor.Pdf.FromMemory(pSourceBuffer);
@ -103,18 +94,19 @@ public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
if (annot.Type == AnnotationType.PSPDFKit.FormField)
{
AddFormFieldValue(
_manager.AddFormFieldValue(
(annot.X ?? default) / inchFactor,
y,
(annot.Width ?? default) / inchFactor,
(annot.Height ?? default) / inchFactor,
element.Page,
annot.Value
annot.Value,
_options
);
}
else if (annot.Type == AnnotationType.PSPDFKit.Image)
{
AddImageAnnotation(
_manager.AddImageAnnotation(
(annot.X ?? default) / inchFactor,
annot.Name == "signature" ? ((annot.Y ?? default) - frameYShift) / inchFactor : y,
(annot.Width ?? default) / inchFactor,
@ -125,7 +117,7 @@ public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
}
else if (annot.Type == AnnotationType.PSPDFKit.Ink)
{
AddInkAnnotation(element.Page, annot.Value);
_manager.AddInkAnnotation(element.Page, annot.Value);
}
}
}
@ -141,13 +133,7 @@ public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
return oNewStream.ToArray();
}
/// <summary>
///
/// </summary>
/// <param name="pSourceBuffer"></param>
/// <param name="pInstantJSONList"></param>
/// <returns></returns>
public byte[] BurnInstantJSONAnnotsToPDF(byte[] pSourceBuffer, List<string> pInstantJSONList)
private byte[] BurnInstantJSONAnnotsToPDF(byte[] pSourceBuffer, List<string> pInstantJSONList)
{
GdPictureStatus oResult;
@ -211,11 +197,11 @@ public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
{
case AnnotationType.PSPDFKit.Image:
if (annotationData?.Attachments is not null)
AddImageAnnotation(annotation, annotationData.Attachments);
_manager.AddImageAnnotation(annotation, annotationData.Attachments);
break;
case AnnotationType.PSPDFKit.Ink:
AddInkAnnotation(annotation);
_manager.AddInkAnnotation(annotation);
break;
case AnnotationType.PSPDFKit.Widget:
@ -223,146 +209,15 @@ public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand, byte[]>
var formFieldValue = annotationData?.FormFieldValues?
.FirstOrDefault(fv => fv.Name == annotation.Id);
if (formFieldValue != null && !_pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.Value))
if (formFieldValue != null && !_options.IgnoredLabels.Contains(formFieldValue.Value))
{
AddFormFieldValue(annotation, formFieldValue);
_manager.AddFormFieldValue(annotation, formFieldValue, _options);
}
break;
}
}
}
private void AddFormFieldValue(double x, double y, double width, double height, int page, string value)
{
_manager.SelectPage(page);
// Add the text annotation
var ant = _manager.AddTextAnnot((float)x, (float)y, (float)width, (float)height, value);
// Set the font properties
ant.FontName = _pdfBurnerParams.FontName;
ant.FontSize = _pdfBurnerParams.FontSize;
ant.FontStyle = _pdfBurnerParams.FontStyle;
_manager.SaveAnnotationsToPage();
}
private void AddFormFieldValue(Annotation pAnnotation, FormFieldValue formFieldValue)
{
var ffIndex = _pdfBurnerParams.GetAnnotationIndex(pAnnotation.EgName);
// Convert pixels to Inches
var oBounds = pAnnotation.Bbox?.Select(ToInches).ToList();
if (oBounds is null || oBounds.Count < 4)
return;
double oX = oBounds[0];
double oY = oBounds[1] + _pdfBurnerParams.YOffset * ffIndex + _pdfBurnerParams.TopMargin;
double oWidth = oBounds[2];
double oHeight = oBounds[3];
_manager.SelectPage(pAnnotation.PageIndex + 1);
// Add the text annotation
var ant = _manager.AddTextAnnot((float)oX, (float)oY, (float)oWidth, (float)oHeight, formFieldValue.Value);
// Set the font properties
ant.FontName = _pdfBurnerParams.FontName;
ant.FontSize = _pdfBurnerParams.FontSize;
ant.FontStyle = _pdfBurnerParams.FontStyle;
_manager.SaveAnnotationsToPage();
}
private void AddImageAnnotation(double x, double y, double width, double height, int page, string base64)
{
_manager.SelectPage(page);
_manager.AddEmbeddedImageAnnotFromBase64(base64, (float)x, (float)y, (float)width, (float)height);
}
private void AddImageAnnotation(Annotation pAnnotation, Dictionary<string, Attachment> pAttachments)
{
var oAttachment = pAttachments
.Where(a => a.Key == pAnnotation.ImageAttachmentId)
.SingleOrDefault();
if (oAttachment.Value == null)
return;
// Convert pixels to Inches
var oBounds = pAnnotation.Bbox?.Select(ToInches).ToList();
if (oBounds is null || oBounds.Count < 4)
return;
var oX = oBounds[0];
var oY = oBounds[1];
var oWidth = oBounds[2];
var oHeight = oBounds[3];
_manager.SelectPage(pAnnotation.PageIndex + 1);
_manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.Binary, (float)oX, (float)oY, (float)oWidth, (float)oHeight);
}
private void AddInkAnnotation(int page, string value)
{
var ink = JsonConvert.DeserializeObject<Ink>(value);
var oSegments = ink?.Lines.Points;
var oColor = ColorTranslator.FromHtml(ink?.StrokeColor ?? "#000000");
_manager.SelectPage(page);
if (oSegments is null)
return;
foreach (var oSegment in oSegments)
{
var oPoints = oSegment
.Select(ToPointF)
.ToArray();
_manager.AddFreeHandAnnot(oColor, oPoints);
}
}
private void AddInkAnnotation(Annotation pAnnotation)
{
var oSegments = pAnnotation.Lines?.Points;
var oColor = ColorTranslator.FromHtml(pAnnotation.StrokeColor ?? "#000000");
_manager.SelectPage(pAnnotation.PageIndex + 1);
if (oSegments is null)
return;
foreach (var oSegment in oSegments)
{
var oPoints = oSegment
.Select(ToPointF)
.ToArray();
_manager.AddFreeHandAnnot(oColor, oPoints);
}
}
#region Helpers
private PointF ToPointF(List<float> pPoints)
{
var oPoints = pPoints.Select(ToInches).ToList();
return new PointF(oPoints[0], oPoints[1]);
}
private double ToInches(double pValue)
{
return pValue / 72.0;
}
private float ToInches(float pValue)
{
return pValue / 72f;
}
#endregion
/// <summary>
///
/// </summary>