B4J Library PDFBox (beta). Editing PDF AcroForm Fieldvalues, creating PDFs with new AcroForm

DonManfred

Expert
Licensed User
This is a partial wrap for Apache PDFBox.

In this early stage i am focusing on manipulating PDF AcroForms and building new PDF with AcroForm.
It is also possible to create some javascript which can run when you open the PDF on acrobat.

The Examplecode and the needed Objects i got my inspiration from one of the Examples.


Example Code (just my test code)
B4X:
    'Building a new PDF with AcroForm

    'PDDocument pdDocument = new PDDocument();
    Dim r As PDRectangle
    Dim hlp As FontandColorHelper

    Dim doc As PDDocument
    doc.Initialize("")
    Dim cat As PDDocumentCatalog = doc.DocumentCatalog
    Dim json As PDActionJavaScript
    json.Initialize("",$"var now = util.printd('yyyy-mm-dd', new Date());
  var oField = this.getField('Field1');
  oField.value = now;"$)
    cat.OpenAction =  json ' the Field "Field1" is used here...

    Dim page As PDPage
    page.Initialize(r.A4)

    doc.addPage(page)
    '
    Dim acroform As PDAcroForm
    acroform.Initialize(doc)

    cat.AcroForm = acroform
    '
    Dim res As PDResources
    res.Initialize
    res.putFont(hlp.COURIER,"Cour")
    acroform.DefaultResources = res
    '
    Dim textfield As PDTextField
    textfield.Initialize("",acroform)
    textfield.PartialName = "Field1" ' This is the Field we are refering in the javascript above.
    textfield.DefaultValue = "B4X rules"
    textfield.DefaultAppearance = "/Cour 12 Tf 0 0 1 rg"

    acroform.addField(textfield)
    textfield.Value = ""

    '
    Dim widget As PDAnnotationWidget = textfield.Widget

    Dim rect As PDRectangle
    rect.Initialize(50,750,300,20)
    widget.Rectangle = rect
    widget.Page = page

    Dim fieldAppearance As PDAppearanceCharacteristicsDictionary
    fieldAppearance.Initialize
    fieldAppearance.BorderColour = hlp.createColor(Array As Float(8,16,32))
    fieldAppearance.Background = hlp.createColor(Array As Float(32,64,8))

    widget.AppearanceCharacteristics = fieldAppearance
    widget.Printed = True

    page.addAnnotation(widget)
    '
    '
    doc.save("d:/sample_form.pdf")
    doc.close()
The attached sample_form.pdf is the PDF created by this Code ;-)

The new Example i added does the following:
- It exports a XFDF File from a given pdf with acroform.
- It inports a sample XFDF and fill the PDF Acroform with the data from the XFDF.
- Now working with another PDF the Example uses a Template PDF and adds a AcroForm onto this PDF. The newly generated PDF if saved.

If you want to add Javascript for the generated AcroForm you can use this code for example.
B4X:
        Dim js As PDActionJavaScript
        js.Initialize($"var anzahl1 = this.getField("Pos1Anzahl");
var preis1 = this.getField("Pos1Summe");

var anzahl2 = this.getField("Pos2Anzahl");
var preis2 = this.getField("Pos2Summe");

var anzahl3 = this.getField("Pos3Anzahl");
var preis3 = this.getField("Pos3Summe");

var anzahl4 = this.getField("Pos4Anzahl");
var preis4 = this.getField("Pos4Summe");

var gesamt = this.getField("GesamtNetto");
var mwst = this.getField("MWST");

var gesamtbrutto = this.getField("EndPreis");

var netto = (anzahl1.value*preis1.value)+(anzahl2.value*preis2.value)+(anzahl3.value*preis3.value)+(anzahl4.value*preis4.value)
gesamt.value = netto;
gesamtbrutto.value = netto+((netto/100)*mwst.value);"$)
        widget.Action = js
What it does:
It get the fields for pos1amount, pos1sum, pos2amount, pos2sum, pos3amount, pos3sum and pos4amount, pos4sum.
It then calculate the tax (in german MWSt) and a endprice including Tax.
The generated values are set into the fields.

There are much more nice gimmicks you can do with the Acrobat internal Javascript. :D:cool:

Download the PDFBox jar here:
https://www.dropbox.com/s/1wh9ldpumahqks5/AddLibsPDFBox.zip?dl=0

PDFBox
<link>...|http://www.b4x.com</link>
Author: DonManfred
Version: 0.29
 

Attachments

Last edited:

DonManfred

Expert
Licensed User
In development... My First PDF Attachment ;-)

B4X:
    '// embedded files are stored in a named tree
    'PDEmbeddedFilesNameTreeNode efTree = new PDEmbeddedFilesNameTreeNode();
    Dim efTree As PDEmbeddedFilesNameTreeNode
    efTree.Initialize("")
    
    '// first create the File specification, which holds the embedded File
    'PDComplexFileSpecification fs = new PDComplexFileSpecification();
    Dim fs As PDComplexFileSpecification
    fs.Initialize
    fs.File = "donmanfred.png"
    Wait For (File.CopyAsync(File.DirAssets, "donmanfred.png", File.DirData("PDFBox"), "donmanfred.png")) Complete (Success As Boolean)
    
    '
    '// create a dummy File stream, this would probably normally be a FileInputStream
    'byte[] data = "This is the contents of the embedded file".getBytes("ISO-8859-1");
    'ByteArrayInputStream fakeFile = new ByteArrayInputStream(data);
    '
    '// now lets some of the optional parameters
    'PDEmbeddedFile ef = new PDEmbeddedFile(doc, fakeFile);
    Dim ef As PDEmbeddedFile
    ef.Initialize(doc,File.OpenInput(File.DirData("PDFBox"),"donmanfred.png"))
    ef.Subtype = "image/png"
    ef.Size = File.Size(File.DirData("PDFBox"),"donmanfred.png")
    'ef.setSize(data.length);
    ef.CreationDate = DateTime.Now
    ef.ModDate = DateTime.Now
    'ef.setCreationDate(Calendar.getInstance());
    fs.EmbeddedFile = ef
    '
    '// create a new tree node And add the embedded File
    'PDEmbeddedFilesNameTreeNode treeNode = new PDEmbeddedFilesNameTreeNode();
    Dim treenode As PDEmbeddedFilesNameTreeNode
    treenode.Initialize("")
    Dim kids As Map
    kids.Initialize
    kids.Put("",fs)
    treenode.setNames("My first Attachment", fs)
    
    '// add the tree To the document catalog
    'PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
    Dim names As PDDocumentNameDictionary
    names.Initialize(cat)
    'names.EmbeddedFiles = efTree
    names.EmbeddedFiles = treenode
    'names.setEmbeddedFiles(efTree);
    cat.Names = names
    
    
    
    doc.save("d:/sample_form.pdf")
    doc.close()
Find the generated PDF attached ;-)
 

Attachments

DonManfred

Expert
Licensed User

DonManfred

Expert
Licensed User
Adding Text and Image to PDF

B4X:
Sub AddTextandImagetopdf()
    Private box As PDFBox
    box.Initialize("",File.Combine(File.DirData("PDFBox"),"acroformtest.pdf")) ' PDF to start with
    Dim r As PDRectangle
    Dim hlp As FontandColorHelper

    Dim doc As PDDocument = box.Document                                 ' Get Document from PDF
    Dim cat As PDDocumentCatalog = doc.DocumentCatalog  ' Get DocumentCatalog
    Dim p1 As PDPage = doc.GetPage(0)                                        ' Get Page1
    Dim acroform As PDAcroForm = cat.AcroForm                     ' Get Acroform
    Dim res As PDResources
    If acroform.DefaultResources <> Null Then
        res = acroform.DefaultResources
    Else
        res.Initialize
        res.putFont(hlp.COURIER,"Cour")
        acroform.DefaultResources = res
    End If
    
    Dim contentStream As PDPageContentStream                         ' Create a PageContentStream
    contentStream.Initialize("",doc,p1,"APPEND",False,False) ' Initialize it with Document and Page
    
    contentStream.beginText()
    contentStream.setFont(hlp.COURIER,20) ' needed before writing Text
    contentStream.setNonStrokingColor(0,50,150)
    contentStream.setStrokingColor(60,100,150)
    contentStream.newLineAtOffset(60, 720) ' Position of "next Text" to add
    contentStream.showText("Bla bla bla bla bla ;-)") ' Output the Text
    contentStream.endText()
    
    contentStream.addLine(60,715,330,715)         ' Create a Line under the text
    contentStream.closeAndStroke
    
    Dim img As PDImageXObject                                    ' Create PDImageXObject which holds an Image to add to the PDF
    img.Initialize(doc,"d:\\donmanfred_avatar.bmp") ' The Image
    contentStream.drawImage2(img,435,435,64,64)         ' Draw Image on Page at x/y=435 with a size of 64x64
    
    contentStream.close()                                                     ' Close the ContentStream

    doc.save("d:/sample.pdf")    ' Save PDF to Disc
    doc.close()    ' Close PDF
    
End Sub
Note that the code is based on an version of the wrap not published as of now. Will do it this weekend.

Attached: acroformtest.pdf (pdf to change)
sample.pdf The resulting PDF after this Code.
 

Attachments

DonManfred

Expert
Licensed User
With the help of barcode4j and/or zxing you also can create a few Barcodeformats and add them to the PDPageContentStream.
Using zxing QRCode can be added.

B4X:
    box.addBarcode128(doc,p1,"1234567890",300,450,p1rec.Height-100,100,20)
B4X:
box.addQRCodeImage(cs,doc,"https://b4x.com",100,100,"",50,50)
Another nice feature is the use of XFDF Files.

Once you successfully created a PDF with a AcroForm you can Extract XFDF Data from the Acroform
B4X:
    Private box As PDFBox
    box.Initialize("",File.Combine(path,filename))
    Dim doc As PDDocument = box.Document
    Dim p1 As PDPage = box.getPage(0)
    Dim cat As PDDocumentCatalog = doc.DocumentCatalog
    Dim acroform As PDAcroForm = cat.AcroForm
    Dim fdfdoc As FDFDocument = acroform.exportFDF
    fdfdoc.save("D:\\acroformtest-mustermann.xfdf")
    fdfdoc.close
    doc.close()    ' Close PDF
The opposite is possible too. You have a pdf with the empty AcroForm and want to Fill this Form with the Values from a XFDF File.

B4X:
    Private box As PDFBox
    box.Initialize("",File.Combine(path,filename))
    Dim doc As PDDocument = box.Document
    Dim p1 As PDPage = box.getPage(0)
    Dim cat As PDDocumentCatalog = doc.DocumentCatalog
    Dim acroform As PDAcroForm = cat.AcroForm
    Dim fdfdoc As FDFDocument = acroform.exportFDF
    fdfdoc.loadXFDF("D:\\acroformtest-mustermann.xfdf")
    acroform.importFDF(fdfdoc)
    fdfdoc.close
    doc.save(destinationpdfFilename)    ' Save PDF to Disc
    doc.close()    ' Close PDF
acroformtest.pdf - The empty AcroForm
acroformtest-mustermann.zip contains the XFDF file correspondending to the Acroform used
acroformtest-mustermann.pdf is the resulting pdf with the filled AcroForm.
 

Attachments

DonManfred

Expert
Licensed User
PDFBox also has an PDFRendering class in a additional jar (pdfbox-tools).

B4X:
Sub Export2PNG(path As String, filename As String, destination As String, dpi As Int, pageno As Int)
    'jsapireference.pdf
    Private box As PDFBox
    box.Initialize("",File.Combine(path,filename))
    Log(box.NumberOfPages)
    box.ExportDocumenttopng(box.Document,destination,dpi,pageno)
End Sub
B4X:
    Export2PNG("D:\\Work\\","jsapireference.pdf","D:\\Work\\Temp\\jsreference300",300, 100) ' Export Page 100 (0 based)

    Export2PNG("D:\\Work\\","jsapireference.pdf","D:\\Work\\Temp\\jsreference300",300, -1) ' Export All Pages from the given pdf as single PNGs with 300dpi
 

DonManfred

Expert
Licensed User
Or Splitting a PDF

B4X:
Sub SplitPDF(SourcePDF As String, DestinationPDFprefix)
    'jsapireference.pdf
    Private box As PDFBox
    box.splitpdf(SourcePDF,DestinationPDFprefix)
End Sub
    SplitPDF(File.Combine("D:\\Work\","jsapireference.pdf"),File.Combine("D:\\Work\Temp","jsreference-"))
 

Attachments

DonManfred

Expert
Licensed User
Embedding a TTF-Font

B4X:
    Dim r As PDRectangle
    Dim p1 As PDPage
    p1.Initialize(r.A4)

    Dim doc As PDDocument
    doc.Initialize("")
    doc.addPage(p1)
    Dim cat As PDDocumentCatalog = doc.DocumentCatalog
    Dim acroform As PDAcroForm
    acroform.Initialize(doc)
    cat.AcroForm = acroform
    Dim res As PDResources
    If acroform.DefaultResources <> Null Then
        res = acroform.DefaultResources
    Else
        res.Initialize
        res.putFont(hlp.HELVETICA,"Helv")
        res.putFont(hlp.COURIER,"Cour")
        res.putFont(hlp.ZAPF_DINGBATS,"Zapf")
        acroform.DefaultResources = res
    End If
    Dim p1rec As PDRectangle = p1.CropBox
    Log($"Width=${p1rec.Width}/Heigth=${p1rec.Height}"$)
    Dim contentStream As PDPageContentStream                         ' Create a PageContentStream
    contentStream.Initialize("",doc,p1,"APPEND",False,False) ' Initialize it with Document and Page

    'DrawQRCode(contentStream,doc,"https://b4x.com", 160,p1rec.Height-100,100,100)
    contentStream.beginText()
    contentStream.setFont(hlp.LoadTTFFont(doc,File.Combine(File.DirData("PDFBox"),"fontawesome-webfont.ttf")),300) ' needed before writing Text
    contentStream.setNonStrokingColor(0,50,150)
    contentStream.setStrokingColor(60,100,150)
    contentStream.newLineAtOffset(100, p1rec.Height-328) ' Position of "next Text" to add
    contentStream.showText("") ' Output the Text
    contentStream.endText()

    contentStream.close
    doc.save("D:\\Work\\fontawesome.pdf")    ' Save PDF to Disc
    doc.close()    ' Close PDF
 

Attachments

DonManfred

Expert
Licensed User
Can you add cmyk color for Text ?
Yes, there are methods in there to use CMYK Colors but i have not testet it.
I don´t know where or when i do need CMYK

B4X:
    contentStream.beginText()
    contentStream.setFont(hlp.LoadTTFFont(doc,File.Combine(File.DirData("PDFBox"),"fontawesome-webfont.ttf")),300) ' needed before writing Text

    contentStream.setNonStrokingColor5(c,m,y,k) ' Use CMYK

    contentStream.newLineAtOffset(100, p1rec.Height-328) ' Position of "next Text" to add
    contentStream.showText("") ' Output the Text
    contentStream.endText()
 
Last edited:

paragkini

Member
Licensed User
Hi, Tried running this project in B4J with all the files copied. Getting below msg. Can someone help?

B4J Version: 8.50
Java Version: 8
Parsing code. (0.12s)
Building folders structure. (0.48s)
Compiling code. (0.75s)
Compiling layouts code. (0.01s)
Organizing libraries. (0.00s)
Compiling generated Java code. Error
javac 1.8.0_201
src\b4j\example\main.java:60: error: cannot access FontHelperwrapper
public static de.donmanfred.pdfbox.pdfbox.acroform.FontHelperwrapper _hlp = null;
^
bad class file: D:\Parag\B4XAdditionalLibraries\b4j\pdfbox.jar(de/donmanfred/pdfbox/pdfbox/acroform/FontHelperwrapper.class)
class file has wrong version 55.0, should be 52.0
Please remove or make sure it appears in the correct subdirectory of the classpath.
 

Attachments

paragkini

Member
Licensed User
Thanks. I will try with version 11 or 14 and if the issue still persists, will open a new thread.
 
Top