Skip to Content
Author's profile photo Former Member

Working with Images/PDFs in PI using UDF

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.

Assigned tags

      15 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Good Work Ranjeet, very nicely explained.

      Thanks,

      Aman

      Author's profile photo Prabaharan Asokan
      Prabaharan Asokan

      Ranjeet,

      Thanks for the very useful blog. Please continue sharing useful stuffs like this.

      Prabaharan

      Author's profile photo Former Member
      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

      Author's profile photo Praveen Gujjeti
      Praveen Gujjeti

      Hi Bhargav,

      Base64 class will be under jar file: com.sap.aii.utilxi.core.jar within PI runtime environment

      http://<PIHOSTNAME>:<PORT>/rep/repository/com.sap.aii.utilxi.core.jar

      so use import statement com.sap.aii.utilxi.base64.api.*

      Best Regards,

      Praveen Gujjeti

      Author's profile photo Former Member
      Former Member
      Blog 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.

      Author's profile photo Avinash Ayanala
      Avinash Ayanala

      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

      Author's profile photo Former Member
      Former Member
      Blog Post Author

      Hi Avinash,

      Can you explain your requirement or how you have setup you interface?

      Regards,

      Ranjeet.

      Author's profile photo Avinash Ayanala
      Avinash Ayanala

      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

      Author's profile photo Former Member
      Former Member
      Blog 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

      Author's profile photo Former Member
      Former Member

      Ranjeet,

      Very good and useful blog.

      ........

      Mutti

      Author's profile photo Avinash Ayanala
      Avinash Ayanala

      Hi Ranjeet,

      Thanks for your response.

      Regards

      Avinash.

      Author's profile photo Avinash Ayanala
      Avinash Ayanala

      Hi Ranjeet,

      I am able to convert the image into PDF succesfully thank you very much.

      Author's profile photo Former Member
      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

      Author's profile photo Former Member
      Former Member

      Hi Ranjeeth,

      i want to generate the pdf file using java itext lib in sap view. Could you please share your suggestions or idea on this..

      Author's profile photo Evgeniy Kolmakov
      Evgeniy Kolmakov

      Nice job, Ranjeet!