Compare commits

..

15 Commits

Author SHA1 Message Date
Developer 02
15f6ee7be0 add ExportDocumentException 2025-11-06 21:53:36 +01:00
Developer 02
1051c5356f add CreateReportException 2025-11-06 21:52:35 +01:00
Developer 02
7b13350fbf add BurnAnnotationException 2025-11-06 21:51:20 +01:00
Developer 02
2bada327d8 add MergeDocumentException 2025-11-06 21:50:17 +01:00
Developer 02
a47729ebca feat(pdf): implement annotation burning logic in BurnPdfCommandHandler
- Added full implementation of BurnElementAnnotsToPDF method
- Integrated PdfEditor for background rendering
- Added annotation handling for form fields, images, and ink types
- Included error handling for PDF loading and saving
- Removed unused DigitalData.Core.Abstractions namespace
2025-11-06 21:49:24 +01:00
Developer 02
fcfed963b7 feat(pdf): add annotation burning logic and repository dependency to BurnPdfCommandHandler
- Inject IRepository<Signature> for accessing signature and annotation data
- Implement BurnAnnotsToPDF method to process annotations from DB or JSON
- Add BurnElementAnnotsToPDF and BurnInstantJSONAnnotsToPDF stubs
- Integrate EF Core Include for loading related entities
2025-11-06 21:30:51 +01:00
Developer 02
b2ace61cd4 refactor(BurnPdfCommandHandler): inject AnnotationManager 2025-11-06 21:20:57 +01:00
Developer 02
77cdc83a4e add AnnotationManager as scoped 2025-11-06 21:19:05 +01:00
Developer 02
a611df4914 inject LicenseManager 2025-11-06 21:09:46 +01:00
Developer 02
1341f69ab1 move gdpucture parmas configuration to application layer 2025-11-06 20:57:10 +01:00
Developer 02
0480513288 add gdpicture dependencies 2025-11-06 20:10:41 +01:00
Developer 02
9dbd1b16b5 inject pdf burner params 2025-11-06 19:57:56 +01:00
Developer 02
c649c93921 move pdf burner to application layer 2025-11-06 19:51:15 +01:00
Developer 02
1843119b1b add PDFBurnerParams 2025-11-06 19:39:43 +01:00
Developer 02
bc4371cb99 feat(PDFBurner): init for only .net 2025-11-06 19:34:39 +01:00
11 changed files with 423 additions and 20 deletions

View File

@ -0,0 +1,12 @@
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///
/// </summary>
public class GdPictureParams
{
/// <summary>
///
/// </summary>
public string License { get; set; } = null!;
}

View File

@ -0,0 +1,48 @@
using System.Drawing;
using System.Diagnostics.CodeAnalysis;
namespace EnvelopeGenerator.Application.Common.Configurations;
/// <summary>
///
/// </summary>
public class PDFBurnerParams
{
/// <summary>
///
/// </summary>
public IEnumerable<string> IgnoredLabels { get; set; } = new List<string>
{
"Date", "Datum", "ZIP", "PLZ", "Place", "Ort", "Position", "Stellung"
};
/// <summary>
///
/// </summary>
public double TopMargin { get; set; } = 0.1;
/// <summary>
///
/// </summary>
public double YOffset { get; set; } = -0.3;
/// <summary>
///
/// </summary>
public string FontName { get; set; } = "Arial";
/// <summary>
///
/// </summary>
public int FontSize { get; set; } = 8;
/// <summary>
///
/// </summary>
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
public FontStyle FontStyle { get; set; } = FontStyle.Italic;
}

View File

@ -2,11 +2,15 @@
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
using EnvelopeGenerator.Application.Services;
using EnvelopeGenerator.Application.ThirdPartyModules.Queries;
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using QRCoder;
using System.Reflection;
using GdPicture14;
namespace EnvelopeGenerator.Application;
@ -46,6 +50,28 @@ public static class DependencyInjection
services.Configure<MailParams>(config.GetSection(nameof(MailParams)));
services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams)));
services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams)));
services.Configure<PDFBurnerParams>(config.GetSection(nameof(PDFBurnerParams)));
services.AddOptions<GdPictureParams>()
.Configure((GdPictureParams opt, IServiceProvider sp) =>
{
var licenseKey = "GDPICTURE";
using var scope = sp.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
opt.License = config["GdPictureLicenseKey"]
?? mediator.ReadThirdPartyModuleLicenseAsync(licenseKey).GetAwaiter().GetResult()
?? throw new InvalidOperationException($"License record not found for key: {licenseKey}");
});
services.AddSingleton(provider => {
var license = provider.GetRequiredService<IOptions<GdPictureParams>>().Value.License;
var licenseManager = new LicenseManager();
licenseManager.RegisterKEY(license);
return licenseManager;
});
services.AddScoped(provider => {
// Ensure LicenseManager is resolved so that its constructor is called
_ = provider.GetRequiredService<LicenseManager>();
return new AnnotationManager();
});
services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams)));
services.TryAddSingleton<ISmsSender, GTXSmsSender>();

View File

@ -46,6 +46,73 @@
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="GdPicture.NET.14">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.barcode.1d.writer">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.barcode.1d.writer.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.barcode.2d.writer">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.barcode.2d.writer.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.CAD">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.CAD.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.CAD.DWG">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.CAD.DWG.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.Common">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.Common.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.Document">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.Document.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.Email">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.Email.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.HTML">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.HTML.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.Imaging">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.Imaging.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.Imaging.Formats">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.Imaging.Formats.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.Imaging.Formats.Conversion">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.Imaging.Formats.Conversion.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.Imaging.Rendering">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.Imaging.Rendering.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.MSOfficeBinary">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.MSOfficeBinary.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.OpenDocument">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.OpenDocument.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.OpenXML">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.OpenXML.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.OpenXML.Templating">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.OpenXML.Templating.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.PDF">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.PDF.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.RTF">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.RTF.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.SVG">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.SVG.dll</HintPath>
</Reference>
<Reference Include="GdPicture.NET.14.wia.gateway">
<HintPath>..\packages\GdPicture.14.3.3\lib\net462\GdPicture.NET.14.wia.gateway.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,19 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class BurnAnnotationException : ApplicationException
{
/// <summary>
///
/// </summary>
public BurnAnnotationException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
public BurnAnnotationException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class CreateReportException : ApplicationException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public CreateReportException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public CreateReportException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@ -0,0 +1,22 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class ExportDocumentException : ApplicationException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public ExportDocumentException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public ExportDocumentException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@ -0,0 +1,22 @@
namespace EnvelopeGenerator.Application.Exceptions;
/// <summary>
///
/// </summary>
public class MergeDocumentException : ApplicationException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public MergeDocumentException(string message)
: base(message) { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public MergeDocumentException(string message, Exception innerException)
: base(message, innerException) { }
}

View File

@ -0,0 +1,179 @@
using DigitalData.Core.Abstraction.Application.Repository;
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Exceptions;
using EnvelopeGenerator.Domain.Entities;
using EnvelopeGenerator.PdfEditor;
using GdPicture14;
using MediatR;
using Microsoft.EntityFrameworkCore;
namespace EnvelopeGenerator.Application.Pdf;
/// <summary>
///
/// </summary>
public class BurnPdfCommand : IRequest
{
}
/// <summary>
///
/// </summary>
public class BurnPdfCommandHandler : IRequestHandler<BurnPdfCommand>
{
/// <summary>
///
/// </summary>
private readonly PDFBurnerParams _pdfBurnerParams;
private readonly AnnotationManager _manager;
private readonly IRepository<Signature> _signRepo;
/// <summary>
///
/// </summary>
/// <param name="pdfBurnerParams"></param>
/// <param name="manager"></param>
/// <param name="signRepo"></param>
public BurnPdfCommandHandler(PDFBurnerParams pdfBurnerParams, AnnotationManager manager, IRepository<Signature> signRepo)
{
_pdfBurnerParams = pdfBurnerParams;
_manager = manager;
_signRepo = signRepo;
}
public byte[] BurnAnnotsToPDF(byte[] pSourceBuffer, List<string> pInstantJSONList, int envelopeId)
{
// read the elements of envelope with their annotations
var elements = _signRepo
.Where(sig => sig.Document.EnvelopeId == envelopeId)
.Include(sig => sig.Annotations)
.ToList();
return elements.Any()
? BurnElementAnnotsToPDF(pSourceBuffer, elements)
: BurnInstantJSONAnnotsToPDF(pSourceBuffer, pInstantJSONList);
}
/// <summary>
///
/// </summary>
/// <param name="pSourceBuffer"></param>
/// <param name="elements"></param>
/// <returns></returns>
public byte[] BurnElementAnnotsToPDF(byte[] pSourceBuffer, List<Signature> elements)
{
// Add background
using (var doc = PdfEditor.Pdf.FromMemory(pSourceBuffer))
{
// TODO: take the length from the largest y
pSourceBuffer = doc.Background(elements, 1.9500000000000002 * 0.93, 2.52 * 0.67)
.ExportStream()
.ToArray();
GdPictureStatus oResult;
using (var oSourceStream = new MemoryStream(pSourceBuffer))
{
// Open PDF
oResult = _manager.InitFromStream(oSourceStream);
if (oResult != GdPictureStatus.OK)
throw new BurnAnnotationException($"Could not open document for burning: [{oResult}]");
// Imported from background (add to configuration)
double margin = 0.2;
double inchFactor = 72;
// Y offset of form fields
var keys = new[] { "position", "city", "date" }; // add to configuration
double unitYOffsets = 0.2;
var yOffsetsOfFF = keys
.Select((k, i) => new { Key = k, Value = unitYOffsets * i + 1 })
.ToDictionary(x => x.Key, x => x.Value);
// Add annotations
foreach (var element in elements)
{
double frameX = element.Left - 0.7 - margin;
var frame = element.Annotations?.FirstOrDefault(a => a.Name == "frame");
double frameY = element.Top - 0.5 - margin;
double frameYShift = frame.Y - frameY * inchFactor;
double frameXShift = frame.X - frameX * inchFactor;
foreach (var annot in element.Annotations)
{
double yOffsetofFF;
if (!yOffsetsOfFF.TryGetValue(annot.Name, out yOffsetofFF))
yOffsetofFF = 0;
double y = frameY + yOffsetofFF;
if (annot.Type == AnnotationType.FormField)
{
AddFormFieldValue(
annot.X / inchFactor,
y,
annot.Width / inchFactor,
annot.Height / inchFactor,
element.Page,
annot.Value
);
}
else if (annot.Type == AnnotationType.Image)
{
AddImageAnnotation(
annot.X / inchFactor,
annot.Name == "signature" ? (annot.Y - frameYShift) / inchFactor : y,
annot.Width / inchFactor,
annot.Height / inchFactor,
element.Page,
annot.Value
);
}
else if (annot.Type == AnnotationType.Ink)
{
AddInkAnnotation(element.Page, annot.Value);
}
}
}
// Save PDF
using (var oNewStream = new MemoryStream())
{
oResult = _manager.SaveDocumentToPDF(oNewStream);
if (oResult != GdPictureStatus.OK)
throw new BurnAnnotationException($"Could not save document to stream: [{oResult}]");
_manager.Close();
return oNewStream.ToArray();
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="pSourceBuffer"></param>
/// <param name="pInstantJSONList"></param>
/// <returns></returns>
public byte[] BurnInstantJSONAnnotsToPDF(byte[] pSourceBuffer, List<string> pInstantJSONList)
{
return Enumerable.Empty<byte>().ToArray();
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public Task Handle(BurnPdfCommand request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}

View File

@ -1,9 +0,0 @@
namespace EnvelopeGenerator.Finalizer.Models;
public class GdPictureOptions
{
/// <summary>
///
/// </summary>
public string License { get; set; } = null!;
}

View File

@ -96,17 +96,6 @@ try
);
#endregion Add DB Context, EG Inf. and Services
builder.Services.AddOptions<GdPictureOptions>()
.Configure((GdPictureOptions opt, IServiceProvider sp) =>
{
var licenseKey = "GDPICTURE";
using var scope = sp.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
opt.License = config["GdPictureLicenseKey"]
?? mediator.ReadThirdPartyModuleLicenseAsync(licenseKey).GetAwaiter().GetResult()
?? throw new InvalidOperationException($"License record not found for key: {licenseKey}");
});
var app = builder.Build();
#region Web API Middleware