7 Replies Latest reply: Oct 18, 2009 1:59 PM by -==cfSearching==- RSS

    Adding text to PDF using iText instead of CFPDF

    mjp420

      Hi,

       

      I know this may seem a bit off topic being posted here but i'm asking this board since i'm a complete JAVA noob and i figure some of you CF folk might have had to do this before.

       

      Anyway, about my question...i'm already adding a watermark image to a pdf using iText (CF8) thanks to the help of fellow poster (=cfSearching=).  What i'm looking for is the best way to go about adding some text to this same pdf.  I need to add 4 lines of text (with specific font and size) and center it underneath the added image.   Does anyone have a site they could point me to as to how to add formatted text and how to get the width of that text so as to align it correctly?  I've search Google and looked at a lot of JAVA code but being a JAVA noob it's tough to figure out exactly which libs and methods can be used to do this. 

       

      Any help would be greatly appreciated!

       

      -Michael

        • 1. Re: Adding text to PDF using iText instead of CFPDF
          -==cfSearching==- Community Member

          It all depends, but from what you have described you could create a merged image (ie current image plus the text beneath it).  Then apply the merged image as the watermark.  But are the four lines of text static or dynamic and do you need to figure out how to wrap the text? Maybe you can 'rig up' an example in your favorite image program to show the desired results.

          • 2. Re: Adding text to PDF using iText instead of CFPDF
            mjp420 Community Member

            Hi again!

             

            Well, the merged image is an idea but i'd rather have it be actual text so that it is at least copy/paste-able if viewed on a computer.

             

            The four lines of text are dynamic (company name, broker name, phone number, email address) and limited to 40 characters.  Right now they are being added via CFPDF and DDX and use the following code in the DDX file to add it to the PDF.

             

            <PDF result="DestinationFile">
                 <PDF source="SourceFile">
                      <Watermark 
                      rotation="0" 
                      opacity="100%" 
                      horizontalAnchor="#horzAnchor#" 
                      horizontalOffset="#horzOffset#" 
                      verticalAnchor="#vertAnchor#" 
                      verticalOffset="#vertOffset#" 
                      alternation="OddPages"
                      >
                           <StyledText text-align="center">
                                <p font="#font#" color="#color#" >#left(dCompany,maxlinechars)#</p>
                                <p font="#font#" color="#color#" >#left(dName,maxlinechars)#</p>
                                <p font="#font#" color="#color#" >#left(dPhone,maxlinechars)#</p> 
                                <p font="#font#" color="#color#" >#left(dEmail,maxlinechars)#</p>
                           </StyledText>
                      </Watermark>
                 </PDF>
            </PDF>
            

             

             

            Then using the created pdf from above, i use a slightly modified version of the cfscript code ( that uses iText) you provided me previously to add a logo image just above this text.  The only changes i made to it were resizing of the image and adding where to place it.  Here is that code:

             

             

            <cfscript>                    
                fullPathToInputFile = "#tempdestfilepath#";
                 writeoutput("<br>fullPathToInputFile=#fullPathToInputFile#");
                fullPathToWatermark = osFile("#request.logofilepath##qord.userlogo_file#",request.os);
                 writeoutput("<br>fullPathToWatermark=#fullPathToWatermark#");
                fullPathToOutputFile =  "#destfilepath#";
                 writeoutput("<br>fullPathToOutputFile=#fullPathToOutputFile#");
                 ppi = 72; // points per inch
                 
                 watermark_x =  ceiling(#qord.pdftemplate_logo_x# * ppi);      // from bottom left corder of pdf
                 watermark_y =  ceiling(#qord.pdftemplate_logo_y# * ppi);     // from bottom left corder of pdf
                 
                 fh = ceiling(0.75 * ppi);
                 fw = ceiling(1.75 * ppi);
                 
               if( not fileexists(fullPathToInputFile) )
               {
                          savedErrorMessage = savedErrorMessage & "<li>Input file pdf for logo add does not exist<br>#fullPathToInputFile#</li>";
               }
               else
               {
                         try {
                         // create PdfReader instance to read in source pdf
                         pdfReader = createObject("java", "com.lowagie.text.pdf.PdfReader").init(fullPathToInputFile);
                         totalPages = pdfReader.getNumberOfPages();
                  
                         // create PdfStamper instance to create new watermarked file 
                         outStream = createObject("java", "java.io.FileOutputStream").init(fullPathToOutputFile);
                         pdfStamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init(pdfReader, outStream);
                  
                         // Read in the watermark image
                         img = createObject("java", "com.lowagie.text.Image").getInstance(fullPathToWatermark);
                            w = img.scaledWidth();
                           h = img.scaledHeight();
                           
                           //$is[0] = w
                           //$is[1] = h
                           
                           if( w >= h )
                          {
                              orientation = 0;
                          }
                          else
                          {
                              orientation = 1;
                              fw = max_h;
                              fh = max_w;
                          }
                          if ( w > fw || h > fh )
                          {
                              if( ( w - fw ) >= ( h - fh ) )
                              {
                                  iw = fw;
                                  ih = ( fw / w ) * h;
                              }
                              else
                              {
                                  ih = fh;
                                  iw = ( ih / h ) * w;
                              }
                              t = 1;
                          }
                          else
                          {
                              iw = w;
                              ih = h;
                              t = 2;
                          }
                           
                         // adding content to each page
                         i = 0;
                         //while (i LT totalPages) {
                             i = i + 1;
                             content = pdfStamper.getOverContent( javacast("int", i) );
                             img.setAbsolutePosition(javacast("float", watermark_x), javacast("float", watermark_y));
                                if(t==1)
                                {
                                     img.scaleAbsoluteWidth( javacast("float", iw) );
                                     img.scaleAbsoluteHeight( javacast("float", ih) );
                                }
                                
                             content.addImage(img);
                             WriteOutput("Watermarked page "& i &"<br>");
                         //}
                  
                         //WriteOutput("Finished!");
                         }        
                         catch (java.lang.Exception e) {
                         savedErrorMessage = savedErrorMessage & "<li>#e#</li>";
                         } 
                     // closing PdfStamper will generate the new PDF file
                     if (IsDefined("pdfStamper")) {
                         pdfStamper.close();
                     }
                     if (IsDefined("outStream")) {
                         outStream.close();
                     }
               }
                    
            </cfscript>
            

             

            The above code resized the image to a certain width/height if needed and adds it to the pdf. 

             

            I just figured they might be a way to tap into one of the java objects that would allow adding the text.  Ideally, adding the text and image to some sort of 'bounding box' that would allow centering of the image and text in relation to that bounding box.  Or if there is no way to add to a bounding box, a way to get the horizontal length of the longest line of text so i could calculate a common centerline for the image and text.

             

            I've attached the following pdf to show how the image and text would look together.  This example is not to scale but a similar image and text would be added to a separate pdf.

             

            Thanks for you help.

            • 3. Re: Adding text to PDF using iText instead of CFPDF
              -==cfSearching==- Community Member

              Well, the merged image is an idea but i'd rather have it be

              actual text so that it is at least copy/paste-able if viewed

              on a computer.

               

              Ah, I see. I suggested a merged image because that would be the simplest to implement. But if you want to preserve the text "as text", obviously that would not do the trick. You could probably achieve something similar using tables. Put the image in one row/cell and the centered text in another. 

               

              Bear in mind it has been a long day, and my brain is fried But I think that should work.

              • 4. Re: Adding text to PDF using iText instead of CFPDF
                mjp420 Community Member

                Thanks for the reply!

                 

                So, taking my cfscript into account above, which JAVA object has the methods that i need to:

                     1) add a table to a pdf  (1 col 2 row with each cell set to center)

                     2) insert an image into the top cell

                     3) insert text into the bottom cell

                 

                Here are the java objects defined in my code:

                     pdfReader = createObject("java", "com.lowagie.text.pdf.PdfReader").init(fullPathToInputFile);

                     outStream = createObject("java", "java.io.FileOutputStream").init(fullPathToOutputFile);

                     pdfStamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init(pdfReader, outStream);

                     img = createObject("java", "com.lowagie.text.Image").getInstance(fullPathToWatermark);

                 

                Can i use one of the objects above or do i need to instantiate a different object?  This is where i'm confused...if i knew which lib to research i'm sure i could figure it out...

                 

                Thanks...

                • 5. Re: Adding text to PDF using iText instead of CFPDF
                  -==cfSearching==- Community Member

                  The classes you need are PdfTable and PdfCell

                  http://cfsearching.blogspot.com/2008/09/getting-started-with-itext-tables.html

                   

                  I was curious, so I threw together a rough example. It adds the table to a pdfTemplate, then adds the watermark template to each page. The positioning and table dimensions are based on my sample data, so you will need to tweak them.  Just make sure the template dimensions are large enough to accomodate your image and text. If the dimensions are not large enough, the watermark may not be visible.

                   


                  <!---
                      NOTES:
                      1. This code uses deprecated methods for compatibility with the iText version in CF8
                      2. The table dimensions and watermark positions used are for demo purposes only and should be adjusted.

                      Test Files:
                      PDF:   http://itextdocs.lowagie.com/examples/com/lowagie/examples/general/copystamp/ChapterSectio n.pdf
                      IMAGE: http://code.google.com/apis/themes/images/beveled_purpleblue.png
                  --->

                  <cfscript>
                      err = "";
                      // TEST VALUES
                      maxlinechars = 40;
                      dCompany = "Google Inc";
                      dName = "1600 Amphitheatre Parkway";
                      dPhone = "+1 650-253-0000";
                      dEmail = "someone@google.com";
                     
                      // simplify code by putting the watermark text values into an array
                      textElements    = [ dCompany, dName, dPhone, dEmail ];
                      inputFile        = ExpandPath("ChapterSection.pdf");
                      outputFile        = ExpandPath("ChapterSection_Watermark_ImageWithText.pdf"); 
                      imgPath        = ExpandPath("/dev/beveled_purpleblue.png");

                      try {
                          // initalize objects for reading and writing the pdf       
                          pdfReader = createObject("java", "com.lowagie.text.pdf.PdfReader").init(inputFile);
                          outStream = createObject("java", "java.io.FileOutputStream").init(outputFile);
                          pdfStamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init( pdfReader, outStream );

                          // get the dimension of the watermark image       
                          // note: the table width and height are sample values for demo only ..
                          // must ensure your dimensions are large enough to accomdate your text and image
                          // otherwise the watermark may not be fully visible
                          img = createObject("java", "com.lowagie.text.Image").getInstance(imgPath);
                          tableWidth = img.width() + 100;
                          tableHeight = img.height() + 75;
                         
                          // create a template for storing the table
                          cb = pdfStamper.getOverContent(1);
                          template = cb.createTemplate(tableWidth, tableHeight);

                          // create a single column table       
                          table = createObject("java", "com.lowagie.text.pdf.PdfPTable").init( 1 );
                          table.setTotalWidth( tableWidth );

                          // reusable objects for adding table rows
                          PdfCell = createObject("java", "com.lowagie.text.pdf.PdfPCell");
                          Phrase = createObject("java", "com.lowagie.text.Phrase");

                          // add the watermark image to the first row       
                          imageCell = PdfCell.init( img, false );
                          imageCell.setBorder( PdfCell.NO_BORDER);
                          imageCell.setHorizontalAlignment( PdfCell.ALIGN_CENTER );
                          table.addCell( imageCell );

                          // add each text element in the array to a new row
                          for (x = 1; x <= arrayLen(textElements); x++) {
                              textCell = PdfCell.init( Phrase.init( textElements[x] ) );
                              textCell.setBorder( PdfCell.NO_BORDER );
                              textCell.setHorizontalAlignment( PdfCell.ALIGN_CENTER );
                              table.addCell( textCell );
                          }
                         
                          // get the calculated table height
                          table.calculateHeightsFast();
                          tableHeight = table.getTotalHeight();
                          table.writeSelectedRows(0, -1, 0, tableHeight, template);
                          WriteOutput("Calculated tableWidth="& tableWidth &", tableHeight="& tableHeight &"<br>");

                          // add the template watermark to each page
                          // note: the x/yPos values are for demo only (top right) ...
                          i = 0;
                          totalPages = pdfReader.getNumberOfPages();
                          while (i LT totalPages) {
                              i = i + 1;
                              content = pdfStamper.getOverContent( javacast("int", i) );
                              // arbitrary positioning code
                              pageSize = pdfReader.getPageSize(i);
                              yPos = pageSize.height() - tableHeight - 15;
                              xPos = (pageSize.width() - tableWidth) - 25;
                              content.addTemplate( template, xPos, yPos );
                              WriteOutput("Watermarked page "& i &" at xPos="& xPos &",yPos="& yPos &"<br>");
                          }
                      }
                      catch (Exception e) {
                          err = e;
                      }
                      if (IsDefined("pdfStamper")) {
                               pdfStamper.close();
                      }

                      if (IsDefined("outStream"))
                      {
                          outStream.close();
                      }
                  </cfscript>

                  <cfdump var="#err#" label="ERROR">
                  • 6. Re: Adding text to PDF using iText instead of CFPDF
                    mjp420 Community Member

                    With some slight modification to your code i was able to get it to work.  Many thanks again for all your help with iText.  I can really see the benfits of using the underlying java objects to maniputlate PDF's instead of using the CFPDF items (if needing finer control of modification).

                     

                    BTW, the i had to change the following lines:

                     

                    FROM:   imageCell.setHorizontalAlignment( PdfCell.ALIGN_CENTER );

                    TO   imageCell.setHorizontalAlignment( img.ALIGN_CENTER );

                     

                    and

                     

                    FROM:    textCell.setHorizontalAlignment( PdfCell.ALIGN_CENTER );

                    TO:    textCell.setHorizontalAlignment( Phrase.ALIGN_CENTER );

                     

                    From what my google search found, you need to set the alignment value on the object within the table cell.  Which seems odd to me (from an HTML point of view)

                     

                    Again,  many thanks...  I'll have to look into your blog ( http://cfsearching.blogspot.com ) more for the info on using iText in CF.

                     

                    -Michael

                    • 7. Re: Adding text to PDF using iText instead of CFPDF
                      -==cfSearching==- Community Member

                      FROM:   imageCell.setHorizontalAlignment(

                      PdfCell.ALIGN_CENTER );

                       

                      Hi again,

                       

                      Yes, I just realized that I was originally testing the code with a later version.  So the last minute change to use PdfCell.ALIGN_CENTER did not work with CF8. It was basically a lazy short-cut, to avoid creating another object just to get the alignment constants. But I see it does not work with CF8's version of iText.

                       

                      The alignment constants are defined higher up in the chain: ie Elements class.  In later versions of iText you can access the constants through PdfCell because it extends => Rectangle => which implements Element (where the constants are).  But that does not seem to be the case with CF8's version.  So PdfCell.ALIGN_CENTER results in an error, because no such constant exists.

                       

                      What does work is to create an "Element" object and then use Element.ALIGN_CENTER:

                       

                      Element = createObject("java", "com.lowagie.text.Element");

                      imageCell.setHorizontalAlignment( Element.ALIGN_CENTER );

                      .... 

                      textCell.setHorizontalAlignment( Element.ALIGN_CENTER );

                       

                       

                      -Leigh