Compare commits

...

6 Commits

Author SHA1 Message Date
3f116ce11a refactor(annotation): generate deterministic annotation IDs based on envelopeId, receiverId, and element index 2025-10-07 09:41:02 +02:00
41738bb36c refactor(annotation): add envelope id and receiver id input parameters 2025-10-06 17:12:06 +02:00
320b2ecc77 refactor(AnnotationData): add SignatureAnnotations-getter method to split signatures 2025-10-06 17:08:44 +02:00
b02cc3d7a4 refactor(PDFBurner): convert to Sub 2025-10-06 14:39:55 +02:00
14f2d9b6af refactor(PDFBurner): remove sigAnnotType parameter from AddInstantJSONAnnotationToPDF method 2025-10-06 13:54:56 +02:00
df74267616 refactor(PDFBurner): Vereinfachung der PDFBurner-Annotationsmethoden und Verbesserung der Fehlerbehandlung
- Methoden für Annotationen (AddInstantJSONAnnotationToPDF, AddImageAnnotation, AddInkAnnotation, AddFormFieldValue) auf Rückgabe-Typ Void umgestellt.
- Interne Try/Catch-Blöcke und das Unterdrücken von Ausnahmen in Hilfsmethoden entfernt.
- Fehlerbehandlung in BurnInstantJSONAnnotationsToPDF zentralisiert, mit Try/Catch für das Hinzufügen von Annotationen.
- Exit Select-Anweisungen für bessere Lesbarkeit in Select Case-Blöcken hinzugefügt.
- Import von DevExpress.DataProcessing ergänzt.
2025-10-06 10:29:29 +02:00
5 changed files with 91 additions and 95 deletions

View File

@@ -563,6 +563,10 @@
<Project>{63e32615-0eca-42dc-96e3-91037324b7c7}</Project>
<Name>EnvelopeGenerator.Infrastructure</Name>
</ProjectReference>
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj">
<Project>{211619f5-ae25-4ba5-a552-bacafe0632d3}</Project>
<Name>EnvelopeGenerator.PdfEditor</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@@ -5,6 +5,7 @@ Imports DigitalData.Modules.Logging
Imports GdPicture14
Imports Newtonsoft.Json
Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument.FinalizeDocumentExceptions
Imports DevExpress.DataProcessing
Namespace Jobs.FinalizeDocument
Public Class PDFBurner
@@ -40,11 +41,13 @@ Namespace Jobs.FinalizeDocument
' Add annotation to PDF
For Each oJSON In pInstantJSONList
If AddInstantJSONAnnotationToPDF(oJSON) = False Then
Try
AddInstantJSONAnnotationToPDF(oJSON)
Catch ex As Exception
Logger.Warn($"Error in AddInstantJSONAnnotationToPDF - oJson: ")
Logger.Warn(oJSON)
Throw New BurnAnnotationException($"Adding Annotation failed")
End If
Throw New BurnAnnotationException($"Adding Annotation failed", ex)
End Try
Next
oResult = Manager.BurnAnnotationsToPage(RemoveInitialAnnots:=True, VectorMode:=True)
If oResult <> GdPictureStatus.OK Then
@@ -65,16 +68,14 @@ Namespace Jobs.FinalizeDocument
End Using
End Function
Private Function AddInstantJSONAnnotationToPDF(pInstantJSON As String) As Boolean
Try
Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON)
oAnnotationData.annotations.Reverse()
Private Sub AddInstantJSONAnnotationToPDF(pInstantJSON As String)
Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON)
oAnnotationData.annotations.Reverse()
Dim sigAnnotType = oAnnotationData.annotations.ElementAt(1).type
Dim yPosOfSigAnnot = oAnnotationData.annotations.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
Dim yPosOfSigAnnot = oAnnotationData.annotations.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
Dim formFieldIndex = 0
Dim formFieldIndex = 0
For Each oAnnotation In oAnnotationData.annotations
Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
@@ -86,9 +87,11 @@ Namespace Jobs.FinalizeDocument
End If
AddImageAnnotation(oAnnotation, oAnnotationData.attachments)
Exit Select
Case ANNOTATION_TYPE_INK
AddInkAnnotation(oAnnotation)
Exit Select
Case ANNOTATION_TYPE_WIDGET
'Add form field values
@@ -97,93 +100,61 @@ Namespace Jobs.FinalizeDocument
AddFormFieldValue(oAnnotation, formFieldValue, formFieldIndex)
formFieldIndex += 1
End If
Exit Select
End Select
isSeal = False
Next
End Sub
Return True
Catch ex As Exception
Logger.Warn("Could not create annotation from InstantJSON")
Logger.Error(ex)
Return False
End Try
Private Function AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment)) As Void
Dim oAttachment = pAttachments.Where(Function(a) a.Key = pAnnotation.imageAttachmentId).
SingleOrDefault()
' Convert pixels to Inches
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1)
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Manager.SelectPage(pAnnotation.pageIndex + 1)
Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, oX, oY, oWidth, oHeight)
End Function
Private Function AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment), Optional yOffset As Double = 0) As Boolean
Try
Dim oAttachment = pAttachments.Where(Function(a) a.Key = pAnnotation.imageAttachmentId).
SingleOrDefault()
Private Function AddInkAnnotation(pAnnotation As Annotation) As Void
Dim oSegments = pAnnotation.lines.points
Dim oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor)
Manager.SelectPage(pAnnotation.pageIndex + 1)
' Convert pixels to Inches
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
For Each oSegment As List(Of List(Of Single)) In oSegments
Dim oPoints = oSegment.
Select(AddressOf ToPointF).
ToArray()
Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1) + yOffset
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Manager.SelectPage(pAnnotation.pageIndex + 1)
Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, oX, oY, oWidth, oHeight)
Return True
Catch ex As Exception
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
Return False
End Try
Manager.AddFreeHandAnnot(oColor, oPoints)
Next
End Function
Private Function AddInkAnnotation(pAnnotation As Annotation) As Boolean
Try
Dim oSegments = pAnnotation.lines.points
Dim oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor)
Manager.SelectPage(pAnnotation.pageIndex + 1)
Private Function AddFormFieldValue(pAnnotation As Annotation, formFieldValue As FormFieldValue, index As Integer) As Void
' Convert pixels to Inches
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
For Each oSegment As List(Of List(Of Single)) In oSegments
Dim oPoints = oSegment.
Select(AddressOf ToPointF).
ToArray()
Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1) + _pdfBurnerParams.YOffset * index + _pdfBurnerParams.TopMargin
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Manager.AddFreeHandAnnot(oColor, oPoints)
Next
Manager.SelectPage(pAnnotation.pageIndex + 1)
' Add the text annotation
Dim ant = Manager.AddTextAnnot(oX, oY, oWidth, oHeight, formFieldValue.value)
Return True
Catch ex As Exception
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
Return False
End Try
End Function
Private Function AddFormFieldValue(pAnnotation As Annotation, formFieldValue As FormFieldValue, index As Integer) As Boolean
Try
' Convert pixels to Inches
Dim oBounds = pAnnotation.bbox.Select(AddressOf ToInches).ToList()
Dim oX = oBounds.Item(0)
Dim oY = oBounds.Item(1) + _pdfBurnerParams.YOffset * index + _pdfBurnerParams.TopMargin
Dim oWidth = oBounds.Item(2)
Dim oHeight = oBounds.Item(3)
Manager.SelectPage(pAnnotation.pageIndex + 1)
' Add the text annotation
Dim ant = Manager.AddTextAnnot(oX, oY, oWidth, oHeight, formFieldValue.value)
' Set the font properties
ant.FontName = _pdfBurnerParams.FontName
ant.FontSize = _pdfBurnerParams.FontSize
ant.FontStyle = _pdfBurnerParams.FontStyle
Manager.SaveAnnotationsToPage()
Return True
Catch ex As Exception
Logger.Warn("Could not add image annotation!")
Logger.Error(ex)
Return False
End Try
' Set the font properties
ant.FontName = _pdfBurnerParams.FontName
ant.FontSize = _pdfBurnerParams.FontSize
ant.FontStyle = _pdfBurnerParams.FontStyle
Manager.SaveAnnotationsToPage()
End Function
Private Function ToPointF(pPoints As List(Of Single)) As PointF
@@ -201,6 +172,22 @@ Namespace Jobs.FinalizeDocument
Friend Class AnnotationData
Public Property annotations As List(Of Annotation)
Public ReadOnly Property SignatureAnnotations As List(Of List(Of Annotation))
Get
Dim result As New List(Of List(Of Annotation))()
If annotations IsNot Nothing AndAlso annotations.Count > 0 Then
For i As Integer = 0 To annotations.Count - 1 Step 6
Dim group As List(Of Annotation) = annotations.Skip(i).Take(6).ToList()
result.Add(group)
Next
End If
Return result
End Get
End Property
Public Property attachments As Dictionary(Of String, Attachment)
Public Property formFieldValues As List(Of FormFieldValue)
End Class

View File

@@ -1,5 +1,4 @@
async function createAnnotations(document) {
async function createAnnotations(document, envelopeId, receiverId) {
const signatures = [];
for (let element of document.elements) {
@@ -7,12 +6,18 @@ async function createAnnotations(document) {
const page = element.page - 1
}
let elementIndex = 0;
function generateId(annotationType) {
return `${envelopeId}_${receiverId}_${elementIndex++}_${annotationType}`;
}
for (let element of document.elements) {
const annotParams = await getAnnotationParams(element.left, element.top);
const page = element.page - 1
//#region signatures
const id = PSPDFKit.generateInstantId()
const id = generateId('signature');
const annotation = new PSPDFKit.Annotations.WidgetAnnotation({
id: id,
pageIndex: page,
@@ -29,7 +34,7 @@ async function createAnnotations(document) {
//#endregion
//#region position
const id_position = PSPDFKit.generateInstantId()
const id_position = generateId('position');
const annotation_position = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position,
pageIndex: page,
@@ -49,7 +54,7 @@ async function createAnnotations(document) {
//#endregion
//#region city
const id_city = PSPDFKit.generateInstantId()
const id_city = generateId('city');
const annotation_city = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city,
pageIndex: page,
@@ -69,7 +74,7 @@ async function createAnnotations(document) {
//#endregion
//#region date
const id_date = PSPDFKit.generateInstantId()
const id_date = generateId('date');
const annotation_date = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date,
pageIndex: page,
@@ -97,7 +102,7 @@ async function createAnnotations(document) {
this.markFieldAsCity(formFieldCity);
//#region date label
const id_date_label = PSPDFKit.generateInstantId()
const id_date_label = generateId('date_label');
const annotation_date_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date_label,
pageIndex: page,
@@ -120,7 +125,7 @@ async function createAnnotations(document) {
//#endregion
//#region city label
const id_city_label = PSPDFKit.generateInstantId()
const id_city_label = generateId('city_label');
const annotation_city_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city_label,
pageIndex: page,
@@ -143,7 +148,7 @@ async function createAnnotations(document) {
//#endregion
//#region position label
const id_position_label = PSPDFKit.generateInstantId()
const id_position_label = generateId('position_label');
const annotation_position_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position_label,
pageIndex: page,

View File

@@ -36,7 +36,7 @@ class App {
// Load annotations into PSPDFKit
try {
let signatures = await createAnnotations(this.currentDocument);
let signatures = await createAnnotations(this.currentDocument, this.envelopeReceiver.envelopeId, this.envelopeReceiver.receiverId);
await this.pdfKit.create(signatures);
} catch (e) {
console.error("Error loading annotations:", e);

View File

@@ -1,3 +1,3 @@
class App{constructor(n,t,i,r,u,f){this.container=f??`#${this.constructor.name.toLowerCase()}`;this.envelopeKey=n;this.pdfKit=null;this.currentDocument=t.envelope.documents[0];this.currentReceiver=t.receiver;this.signatureCount=t.envelope.documents[0].elements.length;this.envelopeReceiver=t;this.documentBytes=i;this.licenseKey=r;this.locale=u}async init(){this.pdfKit=await loadPSPDFKit(this.documentBytes,this.container,this.licenseKey,this.locale);addToolbarItems(this.pdfKit,this.handleClick.bind(this));this.pdfKit.addEventListener("annotations.load",this.handleAnnotationsLoad.bind(this));this.pdfKit.addEventListener("annotations.change",this.handleAnnotationsChange.bind(this));this.pdfKit.addEventListener("annotations.create",this.handleAnnotationsCreate.bind(this));this.pdfKit.addEventListener("annotations.willChange",()=>{Comp.ActPanel.Toggle()});try{let n=await createAnnotations(this.currentDocument);await this.pdfKit.create(n)}catch(n){console.error("Error loading annotations:",n)}[...document.getElementsByClassName("btn_refresh")].forEach(n=>n.addEventListener("click",()=>this.handleClick("RESET")));[...document.getElementsByClassName("btn_complete")].forEach(n=>n.addEventListener("click",()=>this.handleClick("FINISH")));[...document.getElementsByClassName("btn_reject")].forEach(n=>n.addEventListener("click",()=>this.handleClick("REJECT")))}handleAnnotationsLoad(n){n.toJS()}handleAnnotationsChange(){}async handleAnnotationsCreate(n){const t=n.toJS()[0],i=!!t.formFieldName,r=!!t.isSignature;if(i===!1&&r===!0){const r=t.boundingBox.left-20,u=t.boundingBox.top-20,n=150,i=75,f=new Date,e=await createAnnotationFrameBlob(this.envelopeReceiver.name,this.currentReceiver.signature,f,n,i),o=await fetch(e),s=await o.blob(),h=await this.pdfKit.createAttachment(s),c=createImageAnnotation(new PSPDFKit.Geometry.Rect({left:r,top:u,width:n,height:i}),t.pageIndex,h);this.pdfKit.create(c)}}async handleClick(n){let t=!1;switch(n){case"RESET":t=await this.handleReset(null);Comp.SignatureProgress.SignedCount=0;t.isConfirmed&&Swal.fire({title:"Erfolg",text:"Dokument wurde zurückgesetzt",icon:"info"});break;case"FINISH":t=await this.handleFinish(null);t==!0&&(window.location.href=`/Envelope/${this.envelopeKey}`);break;case"REJECT":Swal.fire({title:localized.rejection,html:`<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,icon:"question",input:"text",inputAttributes:{autocapitalize:"off"},showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.complete,cancelButtonText:localized.back,showLoaderOnConfirm:!0,preConfirm:async n=>{try{return await rejectEnvelope(n)}catch(t){Swal.showValidationMessage(`
class App{constructor(n,t,i,r,u,f){this.container=f??`#${this.constructor.name.toLowerCase()}`;this.envelopeKey=n;this.pdfKit=null;this.currentDocument=t.envelope.documents[0];this.currentReceiver=t.receiver;this.signatureCount=t.envelope.documents[0].elements.length;this.envelopeReceiver=t;this.documentBytes=i;this.licenseKey=r;this.locale=u}async init(){this.pdfKit=await loadPSPDFKit(this.documentBytes,this.container,this.licenseKey,this.locale);addToolbarItems(this.pdfKit,this.handleClick.bind(this));this.pdfKit.addEventListener("annotations.load",this.handleAnnotationsLoad.bind(this));this.pdfKit.addEventListener("annotations.change",this.handleAnnotationsChange.bind(this));this.pdfKit.addEventListener("annotations.create",this.handleAnnotationsCreate.bind(this));this.pdfKit.addEventListener("annotations.willChange",()=>{Comp.ActPanel.Toggle()});try{let n=await createAnnotations(this.currentDocument,this.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId);await this.pdfKit.create(n)}catch(n){console.error("Error loading annotations:",n)}[...document.getElementsByClassName("btn_refresh")].forEach(n=>n.addEventListener("click",()=>this.handleClick("RESET")));[...document.getElementsByClassName("btn_complete")].forEach(n=>n.addEventListener("click",()=>this.handleClick("FINISH")));[...document.getElementsByClassName("btn_reject")].forEach(n=>n.addEventListener("click",()=>this.handleClick("REJECT")))}handleAnnotationsLoad(n){n.toJS()}handleAnnotationsChange(){}async handleAnnotationsCreate(n){const t=n.toJS()[0],i=!!t.formFieldName,r=!!t.isSignature;if(i===!1&&r===!0){const r=t.boundingBox.left-20,u=t.boundingBox.top-20,n=150,i=75,f=new Date,e=await createAnnotationFrameBlob(this.envelopeReceiver.name,this.currentReceiver.signature,f,n,i),o=await fetch(e),s=await o.blob(),h=await this.pdfKit.createAttachment(s),c=createImageAnnotation(new PSPDFKit.Geometry.Rect({left:r,top:u,width:n,height:i}),t.pageIndex,h);this.pdfKit.create(c)}}async handleClick(n){let t=!1;switch(n){case"RESET":t=await this.handleReset(null);Comp.SignatureProgress.SignedCount=0;t.isConfirmed&&Swal.fire({title:"Erfolg",text:"Dokument wurde zurückgesetzt",icon:"info"});break;case"FINISH":t=await this.handleFinish(null);t==!0&&(window.location.href=`/Envelope/${this.envelopeKey}`);break;case"REJECT":Swal.fire({title:localized.rejection,html:`<div class="text-start fs-6 p-0 m-0">${localized.rejectionReasonQ}</div>`,icon:"question",input:"text",inputAttributes:{autocapitalize:"off"},showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.complete,cancelButtonText:localized.back,showLoaderOnConfirm:!0,preConfirm:async n=>{try{return await rejectEnvelope(n)}catch(t){Swal.showValidationMessage(`
Request failed: ${t}
`)}},allowOutsideClick:()=>!Swal.isLoading()}).then(n=>{if(n.isConfirmed){const t=n.value;t.ok?reload():Swal.showValidationMessage(`Request failed: ${t.message}`)}});break;case"COPY_URL":const n=window.location.href.replace(/\/readonly/gi,"");navigator.clipboard.writeText(n).then(function(){bsNotify("Kopiert",{alert_type:"success",delay:4,icon_name:"check_circle"})}).catch(function(){bsNotify("Unerwarteter Fehler",{alert_type:"danger",delay:4,icon_name:"error"})});break;case"SHARE":Comp.ShareBackdrop.show();break;case"LOGOUT":await logout()}}async handleFinish(){const n=await this.pdfKit.exportInstantJSON(),t=await n.formFieldValues,r=t.filter(n=>isFieldRequired(n)),u=r.some(n=>n.value===undefined||n.value===null||n.value==="");if(u)return Swal.fire({title:"Warnung",text:"Bitte füllen Sie alle Standortinformationen vollständig aus!",icon:"warning"}),!1;const f=new RegExp("^[a-zA-Z\\u0080-\\u024F]+(?:([\\ \\-\\']|(\\.\\ ))[a-zA-Z\\u0080-\\u024F]+)*$"),e=t.filter(n=>isCityField(n));for(var i of e)if(!IS_MOBILE_DEVICE&&!f.test(i.value))return Swal.fire({title:"Warnung",text:`Bitte überprüfen Sie die eingegebene Ortsangabe "${i.value}" auf korrekte Formatierung. Beispiele für richtige Formate sind: München, Île-de-France, Sauðárkrókur, San Francisco, St. Catharines usw.`,icon:"warning"}),!1;const o=await this.validateAnnotations(this.signatureCount);return o===!1?(Swal.fire({title:"Warnung",text:"Es wurden nicht alle Signaturfelder ausgefüllt!",icon:"warning"}),!1):Swal.fire({title:localized.confirmation,html:`<div class="text-start fs-6 p-0 m-0">${localized.sigAgree}</div>`,icon:"question",showCancelButton:!0,confirmButtonColor:"#3085d6",cancelButtonColor:"#d33",confirmButtonText:localized.finalize,cancelButtonText:localized.back}).then(async t=>{if(t.isConfirmed){try{await this.pdfKit.save()}catch(i){return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1}try{const t=await signEnvelope(await n);if(t.ok)return!0;if(t.status===403)return Swal.fire({title:"Warnung",text:"Umschlag ist nicht mehr verfügbar.",icon:"warning"}),!1;throw new Error;}catch(i){return Swal.fire({title:"Fehler",text:"Umschlag konnte nicht signiert werden!",icon:"error"}),!1}}else return!1})}async validateAnnotations(n){const t=await getAnnotations(this.pdfKit),i=t.map(n=>n.toJS()).filter(n=>n.isSignature);return n>i.length?!1:!0}async handleReset(){const n=await Swal.fire({title:"Sind sie sicher?",text:"Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?",icon:"question",showCancelButton:!0});if(n.isConfirmed){const n=await deleteAnnotations(this.pdfKit)}return n}}