This commit is contained in:
Jonathan Jenne 2023-12-05 12:51:14 +01:00
parent 0f41312a01
commit 0455795165
3 changed files with 370 additions and 46 deletions

View File

@ -137,6 +137,7 @@
<Compile Include="Entities\User.vb" /> <Compile Include="Entities\User.vb" />
<Compile Include="Helpers.vb" /> <Compile Include="Helpers.vb" />
<Compile Include="Jobs\CertificateDocumentJob.vb" /> <Compile Include="Jobs\CertificateDocumentJob.vb" />
<Compile Include="Jobs\PDFBurner.vb" />
<Compile Include="Models\BaseModel.vb" /> <Compile Include="Models\BaseModel.vb" />
<Compile Include="Models\CertificateModel.vb" /> <Compile Include="Models\CertificateModel.vb" />
<Compile Include="Models\ConfigModel.vb" /> <Compile Include="Models\ConfigModel.vb" />
@ -267,5 +268,6 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project> </Project>

View File

@ -1,72 +1,231 @@
Imports DigitalData.Modules.Database Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Base
Imports GdPicture14 Imports GdPicture14
Imports Quartz Imports Quartz
Imports Quartz.Util Imports Quartz.Util
Imports DevExpress.XtraRichEdit.Keyboard
Imports DevExpress.Pdf
Imports System.Security.Cryptography
Imports DevExpress
Imports System.IO
Public Class CertificateDocumentJob Namespace Jobs
Implements IJob Public Class CertificateDocumentJob
Implements IJob
Private LicenseManager As New LicenseManager() Private LicenseManager As New LicenseManager()
Private GdViewer As GdViewer Private GdViewer As GdViewer
Private LogConfig As LogConfig Private ConfigModel As ConfigModel
Private Logger As Logger Private EnvelopeModel As EnvelopeModel
Private Database As MSSQLServer
Private Config As DbConfig
Public Function Execute(pContext As IJobExecutionContext) As Task Implements IJob.Execute Private LogConfig As LogConfig
Dim oGdPictureKey As String = pContext.MergedJobDataMap.Item(Constants.GDPICTURE) Private Logger As Logger
LogConfig = pContext.MergedJobDataMap.Item(Constants.LOGCONFIG) Private Database As MSSQLServer
Logger = LogConfig.GetLogger() Private Config As DbConfig
Try Private PDFBurner As PDFBurner
Logger.Info("Loading GdViewer..")
GdViewer = New GdViewer()
LicenseManager.RegisterKEY(oGdPictureKey)
Logger.Info("Loading Database..") Private Class EnvelopeData
Database = GetDatabase(pContext, LogConfig) Public EnvelopeId As Integer
Public DocumentPath As String
Public AnnotationData As List(Of String)
Logger.Info("Loading Configuration..") End Class
Config = GetDbConfig(Database)
Dim JobId = pContext.JobDetail.Key Public Async Function Execute(pContext As IJobExecutionContext) As Task Implements IJob.Execute
Logger.Info("Starting job {0}", JobId) Dim oGdPictureKey As String = pContext.MergedJobDataMap.Item(Constants.GDPICTURE)
LogConfig = pContext.MergedJobDataMap.Item(Constants.LOGCONFIG)
Logger = LogConfig.GetLogger()
Dim oCompleteStatus As Integer = Constants.EnvelopeStatus.EnvelopeCompletelySigned Try
Dim oSql = $"SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = {oCompleteStatus}" Logger.Debug("Loading GdViewer..")
Dim oTable = Database.GetDatatable(oSql) GdViewer = New GdViewer()
LicenseManager.RegisterKEY(oGdPictureKey)
Logger.Info("Found [{0}] completed envelopes.", oTable.Rows.Count) Logger.Debug("Loading PDFBurner..")
PDFBurner = New PDFBurner(LogConfig, oGdPictureKey)
' Do important work... Logger.Debug("Loading Database..")
Database = GetDatabase(pContext, LogConfig)
Logger.Info("Completed job {0}", JobId) Logger.Debug("Loading Configuration..")
Return Task.FromResult(True) Config = ConfigModel.LoadConfiguration()
Catch ex As Exception Dim JobId = pContext.JobDetail.Key
Logger.Warn("Certificate Document job failed!") Logger.Debug("Starting job {0}", JobId)
Logger.Error(ex)
Return Task.FromException(ex) Dim oCompleteStatus As Integer = Constants.EnvelopeStatus.EnvelopeCompletelySigned
End Try Dim oSql = $"SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = {oCompleteStatus}"
End Function Dim oTable = Database.GetDatatable(oSql)
Dim oEnvelopeIds As List(Of Integer) = oTable.Rows.Cast(Of DataRow).
Select(Function(r) r.Item("GUID")).
Cast(Of Integer).
ToList()
Private Function GetDbConfig(pDatabase) As DbConfig Logger.Info("Found [{0}] completed envelopes.", oEnvelopeIds.Count)
Dim oState As New State With {.Database = pDatabase}
Dim oConfigModel = New ConfigModel(oState)
Return oConfigModel.LoadConfiguration() For Each oId In oEnvelopeIds
End Function
Logger.Info("Finalizing Envelope [{0}]", oId)
Dim oEnvelopeData = GetEnvelopeData(oId)
If oEnvelopeData Is Nothing Then
Logger.Warn("EnvelopeData could not be loaded for Envelope [{0}]!", oId)
End If
GenerateFinalPDF(oEnvelopeData)
Await GenerateReportPdf()
Next
Logger.Debug("Completed job {0}", JobId)
Catch ex As Exception
Logger.Warn("Certificate Document job failed!")
Logger.Error(ex)
End Try
End Function
Private Function MergeDocuments(pDocumentPath As String, pReport As Byte())
Using oGdPicturePDF As New GdPicturePDF()
Using oGdPicturePDFReport As New GdPicturePDF()
Using oStream As New MemoryStream(pReport)
' Load the source file into memory
If oGdPicturePDF.LoadFromFile(pDocumentPath, True) <> GdPictureStatus.OK Then
Throw New ApplicationException("Document could not be loaded!")
End If
' Load the report file into memory
If oGdPicturePDFReport.LoadFromStream(oStream, True) <> GdPictureStatus.OK Then
Throw New ApplicationException("Report could not be loaded!")
End If
If oGdPicturePDF.ClonePages(oGdPicturePDFReport, "*") = GdPictureStatus.OK Then
Throw New ApplicationException("Report could not be loaded!")
End If
End Using
End Using
End Using
End Function
Private Function GenerateFinalPDF(pData As EnvelopeData) As Boolean
Dim pEnvelopeId = pData.EnvelopeId
Logger.Info("Burning [{0}] signatures", pData.AnnotationData.Count)
Dim oAnnotations = pData.AnnotationData
Dim oInputPath = pData.DocumentPath
Dim oOutputPath = Config.ExportPath
Logger.Info("Input path: [{0}]", oInputPath)
Logger.Info("Output path: [{0}]", oOutputPath)
Dim oBurnResult = PDFBurner.BurnInstantJSONAnnotationsToPDF(oInputPath, oAnnotations, oOutputPath)
If oBurnResult = False Then
Logger.Warn("PDF Could not be burned for Envelope [{0}]!", pEnvelopeId)
Return False
End If
Return True
End Function
Private Async Function GenerateReportPdf(pEnvelopeId As Integer) As Task(Of Boolean)
Dim oSql As String = $"SELECT * FROM VWSIG_ENVELOPE_REPORT WHERE ENVELOPE_ID = {pEnvelopeId}"
Dim oTable As DataTable = Database.GetDatatable(oSql)
Dim oItems = GetReportSource(oTable)
If oItems.Count = 0 Then
Return False
End If
Dim oState As New State() With {
.Database = Database,
.LogConfig = LogConfig
}
EnvelopeModel = New EnvelopeModel(oState)
Dim oEnvelope = EnvelopeModel.GetById(pEnvelopeId)
Dim oCreator As New ReportCreator(oEnvelope)
Dim oBuffer = Await oCreator.CreateReport(oItems)
Return
End Function
Private Function GetReportSource(pDataTable As DataTable) As List(Of ReportItem)
Return pDataTable.Rows.
Cast(Of DataRow).
Select(AddressOf ToReportItem).
OrderByDescending(Function(r) r.ItemDate).
ToList()
End Function
Private Function GetEnvelopeData(pEnvelopeId As Integer) As EnvelopeData
Dim oSql = $"SELECT T.GUID, T2.FILEPATH FROM [dbo].[TBSIG_ENVELOPE] T
JOIN TBSIG_ENVELOPE_DOCUMENT T2 ON T.GUID = T2.ENVELOPE_ID
WHERE T.GUID = {pEnvelopeId}"
Dim oTable As DataTable = Database.GetDatatable(oSql)
Dim oRow As DataRow = oTable.Rows.Cast(Of DataRow).SingleOrDefault()
If oRow Is Nothing Then
Return Nothing
End If
Dim oAnnotationData = GetAnnotationData(pEnvelopeId)
Dim oData As New EnvelopeData With {
.EnvelopeId = pEnvelopeId,
.DocumentPath = oRow.ItemEx("FILEPATH", ""),
.AnnotationData = oAnnotationData
}
Return oData
End Function
Private Function GetAnnotationData(pEnvelopeId As Integer) As List(Of String)
Dim oSql = $"SELECT VALUE FROM TBSIG_DOCUMENT_STATUS WHERE ENVELOPE_ID = {pEnvelopeId}"
Dim oTable As DataTable = Database.GetDatatable(oSql)
Return oTable.Rows.Cast(Of DataRow).
Select(Function(r) r.ItemEx("VALUE", String.Empty)).
Cast(Of String).
ToList()
End Function
Private Function ToReportItem(pRow As DataRow) As ReportItem
Return New ReportItem() With {
.EnvelopeId = pRow.Item("ENVELOPE_ID"),
.EnvelopeTitle = pRow.ItemEx("HEAD_TITLE", String.Empty),
.EnvelopeSubject = pRow.ItemEx("HEAD_SUBJECT", String.Empty),
.ItemDate = pRow.ItemEx(Of Date)("POS_WHEN", Nothing),
.ItemStatus = pRow.ItemEx("POS_STATUS", 0),
.ItemUserReference = pRow.ItemEx("POS_WHO", "")
}
End Function
Private Function GetDatabase(pContext As IJobExecutionContext, pLogConfig As LogConfig) As MSSQLServer Private Sub InitializeModels()
Dim oConnectionString As String = pContext.MergedJobDataMap.Item(Constants.DATABASE) Dim oState = GetState()
Dim Database = New MSSQLServer(pLogConfig, MSSQLServer.DecryptConnectionString(oConnectionString)) ConfigModel = New ConfigModel(oState)
EnvelopeModel = New EnvelopeModel(oState)
End Sub
Return Database Private Function GetDatabase(pContext As IJobExecutionContext, pLogConfig As LogConfig) As MSSQLServer
End Function Dim oConnectionString As String = pContext.MergedJobDataMap.Item(Constants.DATABASE)
End Class Dim Database = New MSSQLServer(pLogConfig, MSSQLServer.DecryptConnectionString(oConnectionString))
Return Database
End Function
Private Function GetState() As State
Return New State() With {
.LogConfig = LogConfig,
.Database = Database
}
End Function
End Class
End Namespace

View File

@ -0,0 +1,163 @@
Imports DevExpress.Pdf
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging
Imports GdPicture14
Imports Newtonsoft.Json
Namespace Jobs
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
End Namespace