Imports System.Drawing Imports System.IO Imports DigitalData.Modules.Base Imports DigitalData.Modules.Logging Imports GdPicture14 Imports Newtonsoft.Json Imports EnvelopeGenerator.CommonServices.Jobs.FinalizeDocument.FinalizeDocumentExceptions Imports DevExpress.DataProcessing Namespace Jobs.FinalizeDocument Public Class PDFBurner Inherits BaseClass Private ReadOnly Manager As AnnotationManager Private ReadOnly LicenseManager As LicenseManager Private Const ANNOTATION_TYPE_IMAGE = "pspdfkit/image" Private Const ANNOTATION_TYPE_INK = "pspdfkit/ink" Private Const ANNOTATION_TYPE_WIDGET = "pspdfkit/widget" Private Property _pdfBurnerParams As PDFBurnerParams Public Sub New(pLogConfig As LogConfig, pGDPictureLicenseKey As String, pdfBurnerParams As PDFBurnerParams) MyBase.New(pLogConfig) LicenseManager = New LicenseManager() LicenseManager.RegisterKEY(pGDPictureLicenseKey) Manager = New AnnotationManager() _pdfBurnerParams = pdfBurnerParams End Sub Public Function BurnInstantJSONAnnotationsToPDF(pSourceBuffer As Byte(), pInstantJSONList As List(Of String)) As Byte() Dim oResult As GdPictureStatus Using oSourceStream As New MemoryStream(pSourceBuffer) ' Open PDF oResult = Manager.InitFromStream(oSourceStream) If oResult <> GdPictureStatus.OK Then Throw New BurnAnnotationException($"Could not open document for burning: [{oResult}]") End If ' Add annotation to PDF For Each oJSON In pInstantJSONList Try AddInstantJSONAnnotationToPDF(oJSON) Catch ex As Exception Logger.Warn($"Error in AddInstantJSONAnnotationToPDF - oJson: ") Logger.Warn(oJSON) Throw New BurnAnnotationException($"Adding Annotation failed", ex) End Try Next oResult = Manager.BurnAnnotationsToPage(RemoveInitialAnnots:=True, VectorMode:=True) If oResult <> GdPictureStatus.OK Then Throw New BurnAnnotationException($"Could not burn annotations to file: [{oResult}]") End If 'Save PDF Using oNewStream As New MemoryStream() oResult = Manager.SaveDocumentToPDF(oNewStream) If oResult <> GdPictureStatus.OK Then Throw New BurnAnnotationException($"Could not save document to stream: [{oResult}]") End If Manager.Close() Return oNewStream.ToArray() End Using End Using End Function 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 Dim formFieldIndex = 0 For Each oAnnotation In oAnnotationData.annotations Logger.Debug("Adding AnnotationID: " + oAnnotation.id) Select Case oAnnotation.type Case ANNOTATION_TYPE_IMAGE If (isSeal) Then oAnnotation.bbox.Item(1) = yPosOfSigAnnot End If AddImageAnnotation(oAnnotation, oAnnotationData.attachments) Exit Select Case ANNOTATION_TYPE_INK AddInkAnnotation(oAnnotation) Exit Select Case ANNOTATION_TYPE_WIDGET 'Add form field values Dim formFieldValue = oAnnotationData.formFieldValues.FirstOrDefault(Function(fv) fv.name = oAnnotation.id) If formFieldValue IsNot Nothing AndAlso Not _pdfBurnerParams.IgnoredLabels.Contains(formFieldValue.value) Then AddFormFieldValue(oAnnotation, formFieldValue, formFieldIndex) formFieldIndex += 1 End If Exit Select End Select isSeal = False Next End Sub 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 AddInkAnnotation(pAnnotation As Annotation) As Void Dim oSegments = pAnnotation.lines.points Dim oColor = ColorTranslator.FromHtml(pAnnotation.strokeColor) Manager.SelectPage(pAnnotation.pageIndex + 1) For Each oSegment As List(Of List(Of Single)) In oSegments Dim oPoints = oSegment. Select(AddressOf ToPointF). ToArray() Manager.AddFreeHandAnnot(oColor, oPoints) Next End Function 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() 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() 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 ReadOnly Property AnnotationsByReceiver As IEnumerable(Of List(Of Annotation)) Get Return annotations _ .Where(Function(annot) annot.hasStructuredID).ToList() _ .GroupBy(Function(a) a.receiverId) _ .Select(Function(g) g.ToList()) End Get End Property Public ReadOnly Property UnstructuredAnnotations As IEnumerable(Of List(Of Annotation)) Get Return annotations _ .Where(Function(annot) Not annot.hasStructuredID).ToList() _ .GroupBy(Function(a) a.receiverId) _ .Select(Function(g) g.ToList()) End Get End Property Public Property attachments As Dictionary(Of String, Attachment) Public Property formFieldValues As List(Of FormFieldValue) 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 = False 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 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) hasStructuredID = True End Set End Property 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 Friend Class FormFieldValue Public Property name As String Public Property value As String End Class End Class End Namespace