From 4bd178d856f16dec1bc41c95ae598b23c605a8f8 Mon Sep 17 00:00:00 2001 From: Jonathan Jenne Date: Fri, 17 Nov 2023 13:48:39 +0100 Subject: [PATCH] Handle vector based signatures --- .../EnvelopeGenerator.Test.vbproj | 5 + EnvelopeGenerator.Test/PDFBurner.vb | 162 ++++++++++++++++++ .../frmFinalizePDF.Designer.vb | 52 +++--- EnvelopeGenerator.Test/frmFinalizePDF.vb | 77 ++++----- 4 files changed, 230 insertions(+), 66 deletions(-) create mode 100644 EnvelopeGenerator.Test/PDFBurner.vb diff --git a/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj b/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj index aa8039d5..264b041f 100644 --- a/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj +++ b/EnvelopeGenerator.Test/EnvelopeGenerator.Test.vbproj @@ -61,6 +61,10 @@ ..\..\DDMonorepo\Controls.DocumentViewer\bin\Debug\DigitalData.Controls.DocumentViewer.dll + + False + ..\..\DDModules\Base\bin\Debug\DigitalData.Modules.Base.dll + ..\..\DDModules\Database\bin\Debug\DigitalData.Modules.Database.dll @@ -119,6 +123,7 @@ True + frmFieldEditor.vb diff --git a/EnvelopeGenerator.Test/PDFBurner.vb b/EnvelopeGenerator.Test/PDFBurner.vb new file mode 100644 index 00000000..76e88872 --- /dev/null +++ b/EnvelopeGenerator.Test/PDFBurner.vb @@ -0,0 +1,162 @@ +Imports DevExpress.Pdf +Imports DigitalData.Modules.Base +Imports DigitalData.Modules.Logging +Imports EnvelopeGenerator.PDFBurner +Imports GdPicture14 +Imports Newtonsoft.Json + +Public Class PDFBurner + Inherits BaseClass + + Private ReadOnly LicenseKey As String + Private ReadOnly Manager As AnnotationManager + Private ReadOnly LicenseManager As LicenseManager + + Private Const ANNOTATION_TYPE_IMAGE = "pspdfkit/image" + Private Const ANNOTATION_TYPE_INK = "pspdfkit/ink" + + Public Sub New(pLogConfig As LogConfig, pGDPictureLicenseKey As String) + MyBase.New(pLogConfig) + + LicenseKey = pGDPictureLicenseKey + LicenseManager = New LicenseManager() + LicenseManager.RegisterKEY(pGDPictureLicenseKey) + + Manager = New AnnotationManager() + End Sub + + Public Function BurnInstantJSONAnnotationsToPDF(pSourcePath As String, pInstantJSONList As List(Of String), pDestinationPath As String) As Boolean + If Manager.InitFromFile(pSourcePath) <> GdPictureStatus.OK Then + Logger.Warn("Could not open file [{0}] for burning.", pSourcePath) + Return False + End If + + For Each oJSON In pInstantJSONList + If AddInstantJSONAnnotationToPDF(oJSON) = False Then + Logger.Warn("Adding Annotation failed. Exiting") + Return False + End If + Next + + Try + Manager.BurnAnnotationsToPage(RemoveInitialAnnots:=True, VectorMode:=True) + Manager.SaveDocumentToPDF(pDestinationPath) + Manager.Close() + + Return True + Catch ex As Exception + Logger.Warn("Could not burn and save annotations to file [{0}]!", pDestinationPath) + Logger.Error(ex) + + Return False + End Try + End Function + + Private Function AddInstantJSONAnnotationToPDF(pInstantJSON As String) As Boolean + Try + Dim oAnnotationData = JsonConvert.DeserializeObject(Of AnnotationData)(pInstantJSON) + + For Each oAnnotation In oAnnotationData.annotations + Select Case oAnnotation.type + Case ANNOTATION_TYPE_IMAGE + AddImageAnnotation(oAnnotation, oAnnotationData.attachments) + Case ANNOTATION_TYPE_INK + AddInkAnnotation(oAnnotation) + End Select + Next + + Return True + Catch ex As Exception + Logger.Warn("Could not create annotation from InstantJSON") + Logger.Error(ex) + Return False + End Try + End Function + + Private Function AddImageAnnotation(pAnnotation As Annotation, pAttachments As Dictionary(Of String, Attachment)) As Boolean + Try + 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.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 + 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) + + For Each oSegment As List(Of List(Of Single)) In oSegments + Dim oPoints = oSegment. + Select(AddressOf ToPointF). + ToArray() + + + Manager.AddFreeHandAnnot(oColor, oPoints) + Next + + Return True + Catch ex As Exception + Logger.Warn("Could not add image annotation!") + Logger.Error(ex) + + Return False + End Try + + End Function + + Private Function ToPointF(pPoints As List(Of Single)) As PointF + Dim oPoints = pPoints.Select(AddressOf ToInches).ToList() + Return New PointF(oPoints.Item(0), oPoints.Item(1)) + End Function + + Private Function ToInches(pValue As Double) As Double + Return pValue / 72 + End Function + + Private Function ToInches(pValue As Single) As Single + Return pValue / 72 + End Function + + Friend Class AnnotationData + Public Property annotations As List(Of Annotation) + Public Property attachments As Dictionary(Of String, Attachment) + End Class + + Friend Class Annotation + Public Property id As String + Public Property bbox As List(Of Double) + Public Property type As String + Public Property isSignature As Boolean + Public Property imageAttachmentId As String + Public Property lines As Lines + Public Property pageIndex As Integer + Public Property strokeColor As String + End Class + + Friend Class Lines + Public Property points As List(Of List(Of List(Of Single))) + End Class + + Friend Class Attachment + Public Property binary As String + Public Property contentType As String + End Class +End Class diff --git a/EnvelopeGenerator.Test/frmFinalizePDF.Designer.vb b/EnvelopeGenerator.Test/frmFinalizePDF.Designer.vb index 6d430222..47177a45 100644 --- a/EnvelopeGenerator.Test/frmFinalizePDF.Designer.vb +++ b/EnvelopeGenerator.Test/frmFinalizePDF.Designer.vb @@ -1,9 +1,9 @@ - _ + Partial Class frmFinalizePDF Inherits System.Windows.Forms.Form 'Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen. - _ + Protected Overrides Sub Dispose(ByVal disposing As Boolean) Try If disposing AndAlso components IsNot Nothing Then @@ -20,20 +20,20 @@ Partial Class frmFinalizePDF 'Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich. 'Das Bearbeiten ist mit dem Windows Form-Designer möglich. 'Das Bearbeiten mit dem Code-Editor ist nicht möglich. - _ + Private Sub InitializeComponent() Me.txtReceiver = New System.Windows.Forms.TextBox() Me.txtEnvelope = New System.Windows.Forms.TextBox() Me.Label1 = New System.Windows.Forms.Label() Me.Label2 = New System.Windows.Forms.Label() Me.Button1 = New System.Windows.Forms.Button() - Me.PictureBox1 = New System.Windows.Forms.PictureBox() - CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).BeginInit() + Me.Button2 = New System.Windows.Forms.Button() + Me.txtResult = New System.Windows.Forms.TextBox() Me.SuspendLayout() ' 'txtReceiver ' - Me.txtReceiver.Location = New System.Drawing.Point(57, 65) + Me.txtReceiver.Location = New System.Drawing.Point(15, 65) Me.txtReceiver.Name = "txtReceiver" Me.txtReceiver.Size = New System.Drawing.Size(100, 20) Me.txtReceiver.TabIndex = 0 @@ -41,7 +41,7 @@ Partial Class frmFinalizePDF ' 'txtEnvelope ' - Me.txtEnvelope.Location = New System.Drawing.Point(57, 23) + Me.txtEnvelope.Location = New System.Drawing.Point(15, 23) Me.txtEnvelope.Name = "txtEnvelope" Me.txtEnvelope.Size = New System.Drawing.Size(100, 20) Me.txtEnvelope.TabIndex = 0 @@ -50,7 +50,7 @@ Partial Class frmFinalizePDF 'Label1 ' Me.Label1.AutoSize = True - Me.Label1.Location = New System.Drawing.Point(54, 9) + Me.Label1.Location = New System.Drawing.Point(12, 9) Me.Label1.Name = "Label1" Me.Label1.Size = New System.Drawing.Size(61, 13) Me.Label1.TabIndex = 1 @@ -59,7 +59,7 @@ Partial Class frmFinalizePDF 'Label2 ' Me.Label2.AutoSize = True - Me.Label2.Location = New System.Drawing.Point(54, 46) + Me.Label2.Location = New System.Drawing.Point(12, 46) Me.Label2.Name = "Label2" Me.Label2.Size = New System.Drawing.Size(59, 13) Me.Label2.TabIndex = 1 @@ -67,27 +67,37 @@ Partial Class frmFinalizePDF ' 'Button1 ' - Me.Button1.Location = New System.Drawing.Point(57, 102) + Me.Button1.Location = New System.Drawing.Point(15, 102) Me.Button1.Name = "Button1" - Me.Button1.Size = New System.Drawing.Size(75, 23) + Me.Button1.Size = New System.Drawing.Size(166, 23) Me.Button1.TabIndex = 2 - Me.Button1.Text = "Energie!" + Me.Button1.Text = "Write Annotations to PDF" Me.Button1.UseVisualStyleBackColor = True ' - 'PictureBox1 + 'Button2 ' - Me.PictureBox1.Location = New System.Drawing.Point(384, 92) - Me.PictureBox1.Name = "PictureBox1" - Me.PictureBox1.Size = New System.Drawing.Size(341, 170) - Me.PictureBox1.TabIndex = 3 - Me.PictureBox1.TabStop = False + Me.Button2.Location = New System.Drawing.Point(15, 131) + Me.Button2.Name = "Button2" + Me.Button2.Size = New System.Drawing.Size(166, 23) + Me.Button2.TabIndex = 3 + Me.Button2.Text = "Merge Json" + Me.Button2.UseVisualStyleBackColor = True + ' + 'TextBox1 + ' + Me.txtResult.Location = New System.Drawing.Point(333, 12) + Me.txtResult.Multiline = True + Me.txtResult.Name = "TextBox1" + Me.txtResult.Size = New System.Drawing.Size(455, 426) + Me.txtResult.TabIndex = 4 ' 'frmFinalizePDF ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.ClientSize = New System.Drawing.Size(800, 450) - Me.Controls.Add(Me.PictureBox1) + Me.Controls.Add(Me.txtResult) + Me.Controls.Add(Me.Button2) Me.Controls.Add(Me.Button1) Me.Controls.Add(Me.Label2) Me.Controls.Add(Me.Label1) @@ -95,7 +105,6 @@ Partial Class frmFinalizePDF Me.Controls.Add(Me.txtReceiver) Me.Name = "frmFinalizePDF" Me.Text = "frmFinalizePDF" - CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).EndInit() Me.ResumeLayout(False) Me.PerformLayout() @@ -106,5 +115,6 @@ Partial Class frmFinalizePDF Friend WithEvents Label1 As Label Friend WithEvents Label2 As Label Friend WithEvents Button1 As Button - Friend WithEvents PictureBox1 As PictureBox + Friend WithEvents Button2 As Button + Friend WithEvents txtResult As TextBox End Class diff --git a/EnvelopeGenerator.Test/frmFinalizePDF.vb b/EnvelopeGenerator.Test/frmFinalizePDF.vb index 3c09e208..b817bb55 100644 --- a/EnvelopeGenerator.Test/frmFinalizePDF.vb +++ b/EnvelopeGenerator.Test/frmFinalizePDF.vb @@ -4,6 +4,7 @@ Imports DigitalData.Modules.Database Imports DigitalData.Modules.Logging Imports GdPicture14 Imports Newtonsoft.Json +Imports Newtonsoft.Json.Linq Public Class frmFinalizePDF Private Const CONNECTIONSTRING = "Server=sDD-VMP04-SQL17\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=+bk8oAbbQP1AzoHtvZUbd+Mbok2f8Fl4miEx1qssJ5yEaEWoQJ9prg4L14fURpPnqi1WMNs9fE4=;" @@ -13,84 +14,70 @@ Public Class frmFinalizePDF Private Viewer As GdPicture14.GdViewer Private Manager As AnnotationManager - + Private PDFBurner As PDFBurner Private Sub frmFinalizePDF_Load(sender As Object, e As EventArgs) Handles MyBase.Load LogConfig = New LogConfig(LogConfig.PathType.CustomPath, Application.StartupPath) Database = New MSSQLServer(LogConfig, MSSQLServer.DecryptConnectionString(CONNECTIONSTRING)) + PDFBurner = New PDFBurner(LogConfig, "21182889975216572111813147150675976632") + Manager = New AnnotationManager() Dim oLicense = New LicenseManager() oLicense.RegisterKEY("21182889975216572111813147150675976632") End Sub - Private Function LoadAnnotationData() As String + Private Function LoadAnnotationDataForReceiver() As String Dim oSql = $"SELECT VALUE FROM [TBSIG_DOCUMENT_STATUS] WHERE ENVELOPE_ID = {txtEnvelope.Text} AND RECEIVER_ID = {txtReceiver.Text}" Return Database.GetScalarValue(oSql) End Function + Private Function LoadAnnotationDataForEnvelope() As DataTable + Dim oSql = $"SELECT VALUE FROM [TBSIG_DOCUMENT_STATUS] WHERE ENVELOPE_ID = {txtEnvelope.Text}" + Return Database.GetDatatable(oSql) + + End Function + Private Function LoadEnvelopeDocument() As String Dim oSql = $"SELECT FILEPATH FROM [TBSIG_ENVELOPE_DOCUMENT] WHERE ENVELOPE_ID = {txtEnvelope.Text}" Return Database.GetScalarValue(oSql) End Function - Public Class AnnotationData - - Public Property annotations As List(Of Annotation) - Public Property attachments As Dictionary(Of String, Attachment) - End Class - - Public Class Annotation - Public Property id As String - Public Property bbox As List(Of Double) - Public Property type As String - Public Property isSignature As Boolean - Public Property imageAttachmentId As String - End Class - - Public Class Attachment - Public Property binary As String - Public Property contentType As String - End Class - Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click - Dim oAnnotationData = LoadAnnotationData() Dim oDocumentPath = LoadEnvelopeDocument() + Dim oTable = LoadAnnotationDataForEnvelope() + Dim oJsonList = oTable.Rows. + Cast(Of DataRow). + Select(Function(r As DataRow) r.Item("VALUE").ToString()). + ToList() - Dim oConverter = JsonConvert.DeserializeObject(Of AnnotationData)(oAnnotationData) - - Dim oImages = oConverter.annotations.Where(Function(a) a.type = "pspdfkit/image").ToList() - - Manager.InitFromFile(Application.StartupPath & "/source.pdf") - - For Each oImage In oImages - Dim oAttachment = oConverter.attachments.Where(Function(a) a.Key = oImage.imageAttachmentId).Single() - Dim oBytes = Convert.FromBase64String(oAttachment.Value.binary) + PDFBurner.BurnInstantJSONAnnotationsToPDF(oDocumentPath, oJsonList, $"{oDocumentPath}.burned.pdf") + End Sub + Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click + Try + Dim oTable = LoadAnnotationDataForEnvelope() + Dim oJsonList = oTable.Rows. + Cast(Of DataRow). + Select(Function(r As DataRow) r.Item("VALUE").ToString()). + Select(Function(s As String) JObject.Parse(s)). + ToList() - 'Using oStream As New MemoryStream(oBytes) - ' Using oFileStream As New FileStream($"{oImage.imageAttachmentId}.png", FileMode.OpenOrCreate) - ' oStream.CopyTo(oFileStream) - ' oStream.Flush() - ' End Using - 'End Using - Dim x = oImage.bbox.Item(0) / 72 - Dim y = oImage.bbox.Item(1) / 72 - Dim w = oImage.bbox.Item(2) / 72 - Dim h = oImage.bbox.Item(3) / 72 + Dim oJObject1 = oJsonList.First() + Dim oJObject2 = oJsonList.ElementAt(1) - Manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.binary, x, y, w, h) + oJObject1.Merge(oJObject2) + txtResult.Text = oJObject1.ToString() - Next + Catch ex As Exception - Manager.SaveDocumentToPDF("pdf.pdf") - Manager.Close() + End Try End Sub End Class \ No newline at end of file