Compare commits

...

9 Commits

Author SHA1 Message Date
510f5e9ddd fix: update to set to hold the value of id in a varable to be able to use form fields 2025-10-08 00:47:17 +02:00
44b204ca68 add structred id to image annotations 2025-10-08 00:36:20 +02:00
b72ac68daf refactor(annotations.js): move generateId method to outsite of createAnnotations method 2025-10-08 00:29:37 +02:00
7d85d59ace add hasStructuredID property isntead of throwing exception and filter it 2025-10-08 00:19:28 +02:00
4fad41bd0b update to use # as seperator instead of _ 2025-10-07 22:19:01 +02:00
39936792aa update to use annotations-by-receiver for iteration 2025-10-07 22:16:52 +02:00
74f444a8d6 feat(PDFBurner): add AnnotationsByReceiver method to AnnotationData 2025-10-07 21:54:18 +02:00
b67f26cc21 refactor(PDFBurner): enhance annotation ID handling and validation
- Added parsing of annotation `id` into `envelopeId`, `receiverId`, `index`, and `internalType`.
- Added validation for `id` format, ensuring it has exactly 4 parts and numeric checks for relevant fields.
- Throws `BurnAnnotationException` if `id` is null, empty, or incorrectly formatted.
- Maintains existing functionality for adding image, ink, and widget annotations.
2025-10-07 21:31:38 +02:00
a29f918125 fix(DependencyInjection): update to scan the assembly of EnvelopeReceiver instead of Config 2025-10-07 18:09:14 +02:00
5 changed files with 84 additions and 34 deletions

View File

@@ -70,13 +70,15 @@ Namespace Jobs.FinalizeDocument
Private Sub AddInstantJSONAnnotationToPDF(pInstantJSON As String)
Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON)
oAnnotationData.annotations.Reverse()
Dim yPosOfSigAnnot = oAnnotationData.annotations.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
For Each annots In oAnnotationData.AnnotationsByReceiver
annots.Reverse()
Dim formFieldIndex = 0
For Each oAnnotation In oAnnotationData.annotations
Dim yPosOfSigAnnot = annots.ElementAt(2).bbox.ElementAt(1) - 71.84002685546875 + 7
Dim isSeal = True 'First element is signature seal
Dim formFieldIndex = 0
For Each oAnnotation In annots
Logger.Debug("Adding AnnotationID: " + oAnnotation.id)
Select Case oAnnotation.type
@@ -105,6 +107,8 @@ Namespace Jobs.FinalizeDocument
isSeal = False
Next
Next
End Sub
Private Function AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment)) As Void
@@ -173,18 +177,12 @@ Namespace Jobs.FinalizeDocument
Friend Class AnnotationData
Public Property annotations As List(Of Annotation)
Public ReadOnly Property SignatureAnnotations As List(Of List(Of Annotation))
Public ReadOnly Property AnnotationsByReceiver As IEnumerable(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
Return annotations _
.Where(Function(annot) annot.hasStructuredID).ToList() _
.GroupBy(Function(a) a.receiverId) _
.Select(Function(g) g.ToList())
End Get
End Property
@@ -193,7 +191,54 @@ Namespace Jobs.FinalizeDocument
End Class
Friend Class Annotation
Private _id As String = Nothing
Public envelopeId As Integer = Nothing
Public receiverId As Integer = Nothing
Public index As Integer = Nothing
Public internalType As String = Nothing
Public hasStructuredID As Boolean = True
Public Property id As String
Get
Return _id
End Get
Set(value As String)
_id = value
If String.IsNullOrWhiteSpace(id) Then
Throw New BurnAnnotationException("The identifier of annotation is null or empty.")
End If
Dim parts As String() = value.Split("#"c)
If (parts.Length <> 4) Then
hasStructuredID = False
Return
'Throw New BurnAnnotationException($"The identifier of annotation has more or less than 4 sub-part. Id: {_id}")
End If
If Not Integer.TryParse(parts(0), envelopeId) Then
Throw New BurnAnnotationException($"The envelope ID of annotation is not integer. Id: {_id}")
End If
If Not Integer.TryParse(parts(1), receiverId) Then
Throw New BurnAnnotationException($"The receiver ID of annotation is not integer. Id: {_id}")
End If
If Not Integer.TryParse(parts(2), index) Then
Throw New BurnAnnotationException($"The index of annotation is not integer. Id: {_id}")
End If
internalType = parts(3)
End Set
End Property
Public Property bbox As List(Of Double)
Public Property type As String
Public Property isSignature As Boolean

View File

@@ -60,7 +60,8 @@ namespace EnvelopeGenerator.Infrastructure
services.AddDbRepository(opt =>
{
// scan EnvelopeGenerator
opt.RegisterFromAssembly<EGDbContext>(typeof(Config).Assembly);
opt.RegisterFromAssembly<EGDbContext>(typeof(EnvelopeReceiver).Assembly);
// scan UserManager
opt.RegisterFromAssembly<EGDbContext>(typeof(User).Assembly);
#if NET

View File

@@ -1,4 +1,10 @@
async function createAnnotations(document, envelopeId, receiverId) {
let __elementIndex = 0;
function generateId(envelopeId, receiverId, annotationType) {
return `${envelopeId}#${receiverId}#${__elementIndex++}#${annotationType}`;
}
async function createAnnotations(document, envelopeId, receiverId) {
const signatures = [];
for (let element of document.elements) {
@@ -6,18 +12,12 @@
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 = generateId('signature');
const id = generateId(envelopeId, receiverId, 'signature');
const annotation = new PSPDFKit.Annotations.WidgetAnnotation({
id: id,
pageIndex: page,
@@ -34,7 +34,7 @@
//#endregion
//#region position
const id_position = generateId('position');
const id_position = generateId(envelopeId, receiverId, 'position');
const annotation_position = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position,
pageIndex: page,
@@ -54,7 +54,7 @@
//#endregion
//#region city
const id_city = generateId('city');
const id_city = generateId(envelopeId, receiverId, 'city');
const annotation_city = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city,
pageIndex: page,
@@ -74,7 +74,7 @@
//#endregion
//#region date
const id_date = generateId('date');
const id_date = generateId(envelopeId, receiverId, 'date');
const annotation_date = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date,
pageIndex: page,
@@ -102,7 +102,7 @@
this.markFieldAsCity(formFieldCity);
//#region date label
const id_date_label = generateId('date_label');
const id_date_label = generateId(envelopeId, receiverId, 'date_label');
const annotation_date_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_date_label,
pageIndex: page,
@@ -125,7 +125,7 @@
//#endregion
//#region city label
const id_city_label = generateId('city_label');
const id_city_label = generateId(envelopeId, receiverId, 'city_label');
const annotation_city_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_city_label,
pageIndex: page,
@@ -148,7 +148,7 @@
//#endregion
//#region position label
const id_position_label = generateId('position_label');
const id_position_label = generateId(envelopeId, receiverId, 'position_label');
const annotation_position_label = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_position_label,
pageIndex: page,
@@ -224,8 +224,9 @@ function isSignature(annotation) {
return !!annotation.isSignature || annotation.description == 'FRAME'
}
function createImageAnnotation(boundingBox, pageIndex, imageAttachmentId) {
function createImageAnnotation(boundingBox, pageIndex, imageAttachmentId, envelopeId, receiverId, annotationType) {
const frameAnnotation = new PSPDFKit.Annotations.ImageAnnotation({
id: generateId(envelopeId, receiverId, annotationType),
pageIndex: pageIndex,
isSignature: false,
readOnly: true,

View File

@@ -87,7 +87,10 @@ class App {
height: height,
}),
annotation.pageIndex,
imageAttachmentId
imageAttachmentId,
this.envelopeReceiver.envelopeId,
this.envelopeReceiver.receiverId,
'signature'
)
this.pdfKit.create(frameAnnotation)

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,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(`
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.envelopeReceiver.envelopeId,this.envelopeReceiver.receiverId,"signature");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}}