Skip to Content

Recently one of our customers asked me about PI capability of handling PDFs. This discussion really got interesting when we started scoping requirement. Just handling PDFs turned out like PI will receive multiple images as base64 encoded string in payload. PI will merge these image file, convert it into PDF and then long list of DOs and Don’ts ultimately send base64 encoded pdf file to 3rd party application.

My experience with PDF till that point of time was restricted to moving pdf files from one folder to another using AAE so I thought of creating POC first to see what can be achieved through PI.

I started to look out for Ideas at SDN forum and found some of my ex-colleagues (not one 🙂 ) have implemented some sort of complex PDF handling in PI. Key take away for me here was iText.


iText is a very powerful open source library. Using it’s APIs we can work with PDFs/images in more way than I can count. This library exactly fitted requirement for my poc.

At SDN forum most of the suggestions are to create adapter module or JAVA mapping. Java programming is not best of my traits and I find it easy to work with UDF. Anyway I don’t have to create actual PDF file at source but to send it as base64 encoded string so UDF is best suited for this.

Scope: – PI will receive base64 encoded image as a string in payload. PI will merge these images and create a base64 encoded PDF. PI will put the base64 encoded PDF into a field in target payload which in turn will create xml file at target FTP.

Algorithm: – here I am explaining the basic algorithm.

  1. Read base64 encoded string from payload
  2. For each encode image:
    1. Decode the image
    2. Convert the image to PDF
    3. Add pdf to existing one.
  3. Convert PDF to base64 encoding
  4. Return string to payload.

Implementation:-

For this implementation itext jar “itextpdf-5.4.1.jar” has been used.

Import instructions:-

import com.itextpdf.text.Document;

import com.itextpdf.text.pdf.codec.Base64;

import com.itextpdf.text.*;

import com.itextpdf.text.Image;

import com.itextpdf.text.pdf.PdfWriter;

UDF.JPG

This is advanced UDF for type queue having one input of type string.  I have segregated the UDF code in multiple part for better understanding. I have also included the full version of UDF in last part of blog.

Part 1:Declarations:-

String merge = null; // store base64 encoded merged PDF document
Document pdfDoc = new Document();  //  Constructs a new Document with A4 page size
ByteArrayOutputStream output = new ByteArrayOutputStream(); // store pdf in byte array
PdfWriter writer = null;  // Constructs a PdfWriter
Image img = null;   // Constructs a PdfWriter


Part 2:Processing each encoded node:-

for(int i =0; i<encodedImgStr.length; i++)
{
.
// Code for merging images and creating PDF as described in part 3 and 4
.
}

Part 3:Decode the encoded image and read it into bytes:

String imgStr = encodedImgStr[i];
byte[] imgBytes = Base64.decode(imgStr);

Part 4:Convert images to PDF and merge:-

try{
             // get an instance of an image
                img = Image.getInstance(imgBytes);
            // get the plain height of the image
               float imgHeight = img.getPlainHeight() + 100; 
              // get the plain height of the image
                 float imgWidth = img.getPlainWidth() + 100; 
              // create the page for the document
               Rectangle pageSize = new Rectangle(imgWidth, imgHeight);
               // set the page size of the pdf
                 pdfDoc.setPageSize(pageSize);
   /* below code is executed only once. When first image is processed*/
                if(i ==0)
                {
   /*below method gets an instance of PdfWriter “writer”. pdfDoc  is the document that has to be written and output is the OutputStream the writer has to write which in this case is ByteArrayOutputStream. */
                      writer = PdfWriter.getInstance(pdfDoc, output);
                    writer.open();
 pdfDoc.open();
                }
// Below method convert image to PDF. At each loop image is converted to pdf and new page is appended.
pdfDoc.add(img);
       }
 catch (Exception e) {
 }

Part 5:Close Document and Writer:-

// once document is written to output stream close the document and writer
pdfDoc.close();
 writer.close();

Part 6:Convert output stream to byte:


// Create a new byte array pdfBytes and copy valid content of output stream output to pdfBytes.
byte [] pdfBytes = output.toByteArray ();

Part 7:Encode bytes to base64 encoding:-


// encode PDF bytes to base64.
merge = Base64.encodeBytes(pdfBytes);
// Add merged base64 encoded pdf to resultlist.
 result.addValue(merge);

message mapping.JPG

Below is the input message. Field “fileData” contains encoded images. For this POC I am not actually using “FileType”. I am using SOAP UI to test my interface.

Input Message.JPG

I have used the same structure for output as well. Below is the output message.

Output Message.JPG

I have written small code in NWDS using iText library to decode base64 string and create a PDF file.

Sample Code:-


import com.itextpdf.text.pdf.codec.*;
public class decodePDF {
            public static void main(String[] args) {
            // TODO Auto-generated method stub
               String file1 = "C:/Documents and Settings/Desktop/iText/PIencoded.txt";
               String file2 = "C:/Documents and Settings/Desktop/iText/PIadapter encoded2.pdf";
                Base64.decodeFileToFile(file1,file2);
      }
}

Full version of UDF mergeDoc UDF.

String merge = null;
Document pdfDoc = new Document();
ByteArrayOutputStream output = new ByteArrayOutputStream();
PdfWriter writer = null;
Image img = null;
        for(int i =0; i<encodedImgStr.length; i++)
     {
        String imgStr = encodedImgStr[i];
        byte[] imgBytes = Base64.decode(imgStr);
         try{
            img = Image.getInstance(imgBytes);
            float imgHeight = img.getPlainHeight() + 100; 
                  float imgWidth = img.getPlainWidth() + 100; 
                Rectangle pageSize = new Rectangle(imgWidth, imgHeight);
                pdfDoc.setPageSize(pageSize);
            if(i ==0)
            {
               writer = PdfWriter.getInstance(pdfDoc, output);
               writer.open();
                           pdfDoc.open();
                        }
                        pdfDoc.add(img);
                     }
                  catch (Exception e) {
                }
          }
 pdfDoc.close();
writer.close();
byte [] pdfBytes = output.toByteArray ();
merge = Base64.encodeBytes(pdfBytes);
result.addValue(merge);

We can use iText library to achieve functionality like encryption, removing malicious codes, modify metadata etc. I am still exploring it and hope to find many interesting features we can use.

To report this post you need to login first.

15 Comments

You must be Logged on to comment or reply to a post.

  1. Former Member

    Hi What version of PI are you using. I’m trying to use the Base64.decode in the UDF but im getting an error ” Cannot find symbol Base64″. I have imported java.util.* but its still not working

    (0) 
    1. Former Member Post author

      Hi Bhargav,

      if you are using itext jar files then use import statement as mentioned in the blog.

      import com.itextpdf.text.pdf.codec.Base64;

      Regards,

      Ranjeet.

      (0) 
  2. Former Member

    Hi Ranjeet,

    That was really good stuff and i have designed the interface as you suggested but really i don’t know how to encode and decode the PDF or images can you please give some suggestions.

    Regards,

    Avinash

    (0) 
      1. Former Member

        Hi Ranjeet,

        Thanks for replying.

        I have designed the UDF as you suggested and used it in the mapping to convert the base64 encoded image to base64 encoded PDF, so for that i need the input text value, base64 encoded image but i don’t know how to get it, so please suggest me some solution for getting an input value to pass.

        Regards,

        Avinash

        (0) 
        1. Former Member Post author

          Hi Avinash,

          I suppose you are using NWDS. create a java class using itext jar. use below code to convert image file to base64 encoded text file. use file content in source structure and test your udf. hope this answers your query.

          import com.itextpdf.text.pdf.codec.Base64;

          String file1 = “C:/Users/Ranjeet/Sample/image.bmp”;

          String file2 = “C:/Users/Ranjeet/Sample/base64img.txt”;

          Base64.encodeFileToFile(file1,file2);

          once you get output from udf you can write similar program to decode image into pdf.

          String file1 = “C:/Users/Ranjeet/Sample/base64pdf.txt”;

          String file2 = “C:/Users/Ranjeet/Sample/decodedImage.pdf”;

          Base64.decodeFileToFile(file1,file2);

          Regards,

          Ranjeet

          (0) 
  3. Former Member

    Hi Ranjeet,

    Good blog on Images and PDF handling.

    I was reading through Itext website for any License to use the api, found this :

    Affero General Public License

    Although iText is a free/open source software (F/OSS) project, giving you a lot of freedom and flexibility regarding the use of the iText software, this doesn’t mean you’re free to do anything you want with the product: you have to respect the Affero General Public License (AGPL).

    Buying a commercial license is mandatory as soon as you begin activities including distribution of iText software inside your product or deploying it on a network without disclosing the source code of your own applications under the AGPL license.

    Java, .NET and Android open source developers using iText in open-source projects have to comply with the Affero GPL (AGPL) requirements:

    • Distribution of all source code, including your own product and web-based applications
    • Licensing your own products and web-based applications under the AGPL license
    • Disclosure of modifications made to iText, if any
    • Prominent mention of iText and inclusion of the iText copyright and the AGPL license in output file metadata

    So, I believe commercial license is mandatory, to use the api in your UDF.

    Let us share if you have furthur info on this.

    Cheers

         – Praveen

    (0) 

Leave a Reply