B4J Tutorial [BANano] How to Edit / Sign existing PDF Documents?

Hi Fam

I wanted to sign PDF documents in my product. So.. I used the PDF-LIB for this.

1. I designed a screen to enter the signature and initials.

Wizard.jpeg


2. The canvas we use stores the entered signature and initials as DataURL and I save it in the database. In actual sense this is just a collection of images that we will store in the database.

3. We read the signature signed on the canvas as a DataURL and then pass it to the class below.

4. Download the signed document.

5. Below is an example of a signature made on an existing contract pdf document.

Signature.jpeg
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
This class uses pdf-lib to read the contents of the PDF and then inserts an image at XY with width and height.

B4X:
#IgnoreWarnings: 12
Sub Class_Globals
    Private mCallBack As Object
    Private mEvent As String
    Private mFileName As String
    Private pdfObj As BANanoObject
    Private arrayBuffer As Object
    Private BANAno As BANano
    Private PDFLib As BANanoObject
    Private pdfDoc As BANanoObject
    Private pages As List
End Sub

Public Sub Initialize(Module As Object, event As String, fileName As String)
    mCallBack = Module
    mEvent = event
    mFileName = fileName
    PDFLib.Initialize("PDFLib")
End Sub

Sub OpenTemplate(tmpFile As String)
    arrayBuffer = BANAno.Await(BANAno.GetFileAsArrayBuffer(tmpFile, Null))
    pdfDoc = BANAno.Await(PDFLib.GetField("PDFDocument").RunMethod("load", arrayBuffer))
    'const pdfDoc = await PDFLib.PDFDocument.load(arrayBuffer);
    pages = pdfDoc.RunMethod("getPages", Null)
End Sub

Sub AddSignaturePng(pageNumber As Int, signURL As String, x As Int, y As Int, width As Int, height As Int)
    Dim tPage As Int = BANAno.parseInt(pageNumber) - 1
    Dim sigBuffer As Object = BANAno.Await(BANAno.GetFileAsArrayBuffer(signURL, Null))
    Dim pngIMage As Object = BANAno.Await(pdfDoc.RunMethod("embedPng", sigBuffer))
    
    Dim opt As Map = CreateMap()
    opt.Put("x", x)
    opt.Put("y", y)
    opt.Put("width", width)
    opt.Put("height", height)
    '
    Dim page As BANanoObject = pages.Get(tPage)
    page.RunMethod("drawImage", Array(pngIMage, opt))
End Sub

Sub AddSignatureJpeg(pageNumber As Int, signURL As String, x As Int, y As Int, width As Int, height As Int)
    Dim tPage As Int = BANAno.parseInt(pageNumber) - 1
    Dim sigBuffer As Object = BANAno.Await(BANAno.GetFileAsArrayBuffer(signURL, Null))
    Dim pngIMage As Object = BANAno.Await(pdfDoc.RunMethod("embedJpg", sigBuffer))
    
    Dim opt As Map = CreateMap()
    opt.Put("x", x)
    opt.Put("y", y)
    opt.Put("width", width)
    opt.Put("height", height)
    '
    Dim page As BANanoObject = pages.Get(tPage)
    page.RunMethod("drawImage", Array(pngIMage, opt))
End Sub

Sub SaveToFileObject As BANanoObject
    Dim pdfBytes As Object = BANAno.Await(pdfDoc.RunMethod("save", Null))
    Dim fc As List
    fc.Initialize
    fc.Add(pdfBytes)
    Dim blob As BANanoObject
    blob.Initialize2("Blob",Array(fc, CreateMap("type": "application/pdf")))
    ' make a new File object
    Dim f As BANanoObject
    f.Initialize2("File",Array(Array(blob), mFileName, CreateMap("type": blob.getfield("type"))))
    Return f
End Sub

Sub Download
    Dim pdfBytes As Object = BANAno.Await(pdfDoc.RunMethod("save", Null))
    Dim fc As List
    fc.Initialize
    fc.Add(pdfBytes)
    Dim blob As BANanoObject
    blob.Initialize2("Blob",Array(fc, CreateMap("type": "application/pdf")))
    BANAno.RunJavascriptMethod("saveAs",Array(blob,mFileName))
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
Usage - this is a proof of concept to read an existing "contract" from the assets folder and insert the signatures at

B4X:
Dim pdfWriter As SDUIPDFLib
    pdfWriter.Initialize(Me, "pdf", "73....pdf")
    BANano.Await(pdfWriter.OpenTemplate("./assets/contract.pdf"))
    '
    Dim signatureURL As String = $"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCADIAV4DASIAAhEBAxEB/8QAGgABAAMBAQEAAAAAAAAAAAAAAAcICQYKBP/EADgQAAEEAQQCAgIBAgMECwAAAAIAAQMEBQYHCBESEwkhFCIxFTIjJEEXOEK1GSUzNVRxc3eBhcH/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A1TREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREFMOQHObV/HrmdoXZjXOC0vj9rNVUa0s2fOSUrsRznPA00kskkNetFHYCL2C7S+MDPJ5+RtFHc9Z0fNTtNldU7NaM3bxjzyxaEytinkII4WcI6uRaIfyDPvsWGatXiZmZ+3st9t4/cj/E/vi+7HFmjpLLZD8jO7c2zwFhpbjSzyUXb20pXDpnjjaMirgz99/hk7P/LCF0EREBfLlcri8Fi7mbzeSq4/HY+vJauXLUwxQV4IxcjkkMnYQARZ3cndmZmd3X1LKv5juU88ZUeKukbpAJDBmNWSxm7eTP8AvVpP9/x9DOTO3/h3Z/omQXH26+QriTuvuPjdrNCbo/n5vM9hQeXE3atexOzGTwNLPEDNJ4g7t30JO4iLkTsKsavOVwo0Tn9WcwtpdO4iuL3qWraGVnjlL1+EFCRrlln8uv2GKvL+v8uTdN9uvRqgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIg5jc7brTG7m3uodstZVPyMNqXHzY62wiDyRjIPTSx+YkIygXiYE4v4mAk32yyr+OK7d4kc3dw+Me6GqauKDM13xVRrEPrjy2TgnA8cYG/fr91WxZOMSJmJ5gD7kcBfXtZW/M3tdkdLai225P6MO1jMlXsf0G9k6lpoJa9uF3tY2WPx6kaVvG53Kzv16oW/Xoew1SRRHxT39xPJfYnTG7OPevFev1/xs1ThIeqWTi/SzF4MZkAObeyNjLzeGSInZvJS4gfwvN7nsve5Pb17k7w6rtyY3HenK6xyL2Jisfi1QfxpUGP9XdjmkpUQJmbxeUH66bpb5cp7U9LjFu9dqylHNX0Hn5YzH+RIcfO7O3/AJOy83FDP5PGYnJ4ehalrwZgYYrvqkIHnhjNpGhPp+jjeUYpHEmdvOGIv5FnQaE/DJs5a1bvPqjfbO447FPSOPKhj7c4yfeVufRyRyf2mYVmnExft2a0Duzdi62OVeOA+wtbjzxg0lpSanYr53N1x1JqEbERwyjkbcYEcRxkZeBQxjDXfx8WL0eTiJESsOgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgLgt99oNP7+bP6r2g1Mfqpamx5VRseJE9WwLtJXsMIkPm8UwRSsDkzE4eL/Tuu9RBhfsru1vP8WfJHK7e7paftX9MZAgHM46vI7xZCk5O0GWxxE4gZswl0xdMTNLDJ6pB8otsNv9f6O3T0ZiNwtv8AP1c1p/O1htUbtd38ZAd3Z2dn6IDEmIDAmYgMSEmYhdmjblZxT245Z7cS6J1rC1LK02kmwGfghY7WJtEzfsLO7eyI/EWlhcmaQRb7ExjkDJLS+43Nj4tNT5/R13TFWTAZmwQRBl6lm5p6/ZDx6u0ZozidpXiYWJmIScCFpo/KMGjDQP5ZN4NKaE4o53b25m3h1LryWpRxNOEheU4orcM1mUx7Z2haKMo3Jmf9pQHr9nds2fjY4zWORXIrGXcxjmn0boSSHO58pB7jmISd6lR2cDA/dMDeQF4sUMVjomJmZ+D11jeTPJ/TutuWG4c2RzWC0qNStezd8vVWj91sIYaNGPphdhOw5lHE3jGxORuxSD7NNvhU/wB1jVP/ALgXv+XY5Bf9ERAXnn1LzS3cwvLzUfJTb3WeYKc81ejxlbMG8kUmCKyZw4yxCJuPoaPwZ4wL9TZjAhkEZG9DC85uhNsrvIzVO/WocjlSfPac0pm9wY7FichCeatkq0lr2P4mRkdaW14t23crx+RM3k6D0B7Qbm6f3m2v0vuppeaI8bqfGQZGMI7ATPXMxb2VzIHcfZFJ5xG38icZC/Ts7Lr1m58J25+f1FtXr7arKSyz4/RmUp5DGSSzmbwx5AZ/ZXAXfoIxkqHKzD12diV3+3WkaAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiIKr83+eWheI2nZMFUgHObl5SiFrCYOSKVq4RSHLGNy1KzMPpA4ZG9YF7ZCYRbwFyljz0p87PlH3knuaz2ow+o5MFLM0IwaT28DI4+rIACxRhLLWsSdv/c7HKTs5/XTdM0O7s7lxblc48rqbluWWhweL1hNj85iajjfLH46nZMGxcTxHAJRCwekpo3En8pJ2GSR3Y919oN1NktyNMUQ2S1rpXL4ejQrNXo4SzD/1fVeNmhiOsHRVfEWYWiMAcPHxcWdnZgxQ5DchvkZzG1eQ0jyVo6vpaJ1NJXqTPm9DQYqGSeOYbMQDOFWImk8q3kweXbiJfTt9q4nwt7p7c1Nn9SbRXdZYqrrOzq+zlauFsTtFat1ZMfWZpIBLr39fiWHNo/JwYGI2FiFyn/5Rv9xTc3/6b/nFJYmYnbnbibYjI7qZLe6hjNa1s4eNxeiP6bLNavwRhVM7f5EZu1cf8yfj7QETevIwm5M4sHpdXKbl7q7cbN6Vsa13R1pitM4Wv5D+VkLDR+6QYzk9MIf3zTOEcjjFGxSH4uwi7/SwH/6QXmY2hW28bkBqX+mMLg9ryi/qbi8ns/7x8Py+2L/i9vfi/j349Cvi1Lxy3Itcew5k7l6+x54vV2VlpYsbdqxezGayPukGQpH8XAB/wLZlJLL5f4Dt4u5j2GiWtvmw2hwuvIcJorabUOpdKxWWhu56W9HRmKNpiE5qtMgJ5QeJhkBppK5E5eJjF15Ks/DevBvNzr3k01gZf6Npzc7Ea2xlsRhYTr427KTgIRyCL+QG8D+Liz/o7OzN2zRbwj2GwGr8vnuQ+8lO1BtHtDWfPZaUq9c4M3kICjkr4YWsk0chTO7eYeJ9i4RP63sRmpy+GPSOa1VyQ1tuhfpFbpYPTcsNm8VhhePI3rUbxM4eTEfnFXuv34uLeP30Th2D4TNRZeryB1xpKG9IGLyWjjyNms3XhJYrXa0cJv8A69iNudm/9QlsssT+GVCHbP5Ycjt9ol7GI0/BqbWOACjDZlcCx1eK8cEEjkTvKAlWgJvNy/aMCfsmZ1tggIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiDPv5A/ja0Zuxi85vdtDHjdMa3rjNlMxXlmCrjswLM5zSyETsFew/Tm8ruwG/k8n2TyNkHthuXrPZzX2G3K2/wAweMz+BsfkVLAsxN9s4mBj/BAYEQEL/RCRN/qtvPle3KyW3nDzN0sRkJ6VnWWTp6a90BmBvDL5zTx+Qu3QyQ1pYyZ+2IDIXb9lQ7gt8dWjeWuwmrdw89rrM4DPQZw8JhHr145KkLwwQTHNMBdFMx/keDCJx+Prd+y76YIh3a51cv8AkZo3V2kdX6se/ou40V/MYrHaeqBVoVhuwlB3OMLzxxDYesAkcruREAkROfRS38fHx16L5ZaJv7q6+3DzGNw+Kz1nBSYXFVIwnnKOtWmGX8uRzEBd7Hi4el36D6Nu/qtPI3i5vBxa1ZDpbdXAhXjv+0sVlKkvuo5OON2Yzhk6Z+x8g8gNhMfMHIWYhd9Kvhk3g2rfavNbF0rlynriPLWtSW61yYCivxSBDD7anQi4tGEMInGTkXk7mxOJeEYWey3ADiPkdp72z1LZjB4rFXIjGPJU4WLMVpnMjCeO/N5zvIBE/j7DMfH/AAyEoneN8GN9tr49lN4NW7TRauoamDS2Ulx39Togccc5B15M4F9xyC/YSB2TDIBixmzMZeljVeqMFofS2Z1pqi/+FhsBj7GUyNr1nJ6KsEZSSyeAM5F4gJP0LO79dMzv9LA/YKHbPc/P76ck+UlGpfwdPEZC9+NBkHoS3dV5aaQ6VeqIsZeZ+u8Qv4SBF62kkbwB3QSFy/5N7KxcYtsuKXFPJZAtIjSizeqJ7MRw2Z7Pk5BVsux+JzPYeWeYGF4hMavpPxHxbSb47eOJ8ceNmExWcxn4mrdU9ag1C0gOMsM0wt6apsQiYPDC0YFG/bDL7nZ+i7WHvHXW2iNrt69Fbk7n6Hs6p0rg8sNq5joZPWUpRj2Bh27DIUMhRTeknYJPBozdhN3Xog2d372d3/wB6l2e3BxWpqcPTWQrGUdmo7mYD760jDNB5PFI4+wB8xHyHsenQZGc+qmS4ufI3jd78bh6r4+/dw2t6lLGmVNrIxOEV2CWRo/EZZ5a1gpCZj7GyxF5OZCtp8TlsVn8VSzuCydTI43I147dO5UmGaCzBILFHLHILuJgQuxMTO7Ozs7Kn3ysbE4zdni3ltbV8RZt6m22f+t4ySt4+Q1COMb4n2z9..."$
    '
    BANano.Await(pdfWriter.AddSignatureJpeg(1, signatureURL, 100, 100, 150, 150))
    BANano.Await(pdfWriter.AddSignatureJpeg(2, signatureURL, 100, 100, 150, 150))
    BANano.Await(pdfWriter.Download)
 

Mashiane

Expert
Licensed User
Longtime User
This is additional functionality using another library and not the main pdf generation built internally with SithasoDaisy.

This is based on this library


In AppStart, you need to add

B4X:
BANano.Header.AddJavascriptFileForLater("pdf-lib.min.js")

This is the code for the class, this was just to meet my needs at the time and I didn't explore any other functionality of this library.

B4X:
#IgnoreWarnings: 12
Sub Class_Globals
    Private mCallBack As Object            'ignore
    Private mEvent As String            'ignore
    Private mFileName As String        
    Private pdfObj As BANanoObject        'ignore
    Private arrayBuffer As Object
    Private BANAno As BANano            'ignore
    Private PDFLib As BANanoObject
    Private pdfDoc As BANanoObject
    Private pages As List
    Public PageCount As Int
End Sub

'initialize the pdf-lib with the final file name
Public Sub Initialize(Module As Object, event As String, fileName As String)
    mCallBack = Module
    mEvent = event
    mFileName = fileName
    PDFLib.Initialize("PDFLib")
    PageCount = 0
End Sub

'open an existing pdf template
Sub OpenTemplate(tmpFile As String)
    arrayBuffer = BANAno.Await(BANAno.GetFileAsArrayBuffer(tmpFile, Null))
    pdfDoc = BANAno.Await(PDFLib.GetField("PDFDocument").RunMethod("load", arrayBuffer))
    pages = pdfDoc.RunMethod("getPages", Null)
    PageCount = pages.size
End Sub

Sub SetTitle(s As String)
    pdfDoc.RunMethod("setTitle", Array(s))
End Sub

Sub SetAuthor(s As String)
    pdfDoc.RunMethod("setAuthor", Array(s))
End Sub

Sub SetSubject(s As String)
    pdfDoc.RunMethod("setSubject", Array(s))
End Sub

'add a signature of a png format
Sub AddSignaturePng(pg As BANanoObject, signURL As String, x As Int, y As Int, width As Int, height As Int)
    Dim sigBuffer As Object = BANAno.Await(BANAno.GetFileAsArrayBuffer(signURL, Null))
    Dim pngIMage As Object = BANAno.Await(pdfDoc.RunMethod("embedPng", sigBuffer))
    
    Dim opt As Map = CreateMap()
    opt.Put("x", x)
    opt.Put("y", y)
    opt.Put("width", width)
    opt.Put("height", height)
    '
    pg.RunMethod("drawImage", Array(pngIMage, opt))
End Sub

Sub GetPage(pgNum As Int) As BANanoObject
    Dim pg As BANanoObject = pages.Get(pgNum)
    Return pg
End Sub

Sub DrawText(pg As BANanoObject, txt As String, xPos As Int, yPos As Int, fSize As Int)
    Dim opt As Map = CreateMap()
    opt.Put("x", xPos)
    opt.Put("y", yPos)
    opt.Put("size", fSize)
    '
    pg.RunMethod("drawText", Array(txt, opt))
End Sub

Sub AddSignatureJpeg(pg As BANanoObject, signURL As String, x As Int, y As Int, width As Int, height As Int)
    Dim sigBuffer As Object = BANAno.Await(BANAno.GetFileAsArrayBuffer(signURL, Null))
    Dim pngIMage As Object = BANAno.Await(pdfDoc.RunMethod("embedJpg", sigBuffer))
    
    Dim opt As Map = CreateMap()
    opt.Put("x", x)
    opt.Put("y", y)
    opt.Put("width", width)
    opt.Put("height", height)
    '
    pg.RunMethod("drawImage", Array(pngIMage, opt))
End Sub

Sub EmbedBase64Jpeg(pg As BANanoObject, base64Jpeg As String, x As Int, y As Int, width As Int, height As Int)
    Dim pngIMage As Object = BANAno.Await(pdfDoc.RunMethod("embedJpg", base64Jpeg))
    
    Dim opt As Map = CreateMap()
    opt.Put("x", x)
    opt.Put("y", y)
    opt.Put("width", width)
    opt.Put("height", height)
    '
    pg.RunMethod("drawImage", Array(pngIMage, opt))
End Sub

'save the pdf as a FileObject
Sub SaveToFileObject As BANanoObject
    Dim pdfBytes As Object = BANAno.Await(pdfDoc.RunMethod("save", Null))
    Dim fc As List
    fc.Initialize
    fc.Add(pdfBytes)
    Dim blob As BANanoObject
    blob.Initialize2("Blob",Array(fc, CreateMap("type": "application/pdf")))
    ' make a new File object
    Dim f As BANanoObject
    f.Initialize2("File",Array(Array(blob), mFileName, CreateMap("type": blob.getfield("type"))))
    Return f
End Sub

'download the PDF
Sub Download
    Dim pdfBytes As Object = BANAno.Await(pdfDoc.RunMethod("save", Null))
    Dim fc As List
    fc.Initialize
    fc.Add(pdfBytes)
    Dim blob As BANanoObject
    blob.Initialize2("Blob",Array(fc, CreateMap("type": "application/pdf")))
    BANAno.RunJavascriptMethod("saveAs",Array(blob,mFileName))
End Sub
 
Top