Skip to main content
Participating Frequently
October 15, 2009
Answered

Adding text to PDF using iText instead of CFPDF

  • October 15, 2009
  • 1 reply
  • 14316 views

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

This topic has been closed for replies.
Correct answer -__cfSearching__-

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...


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/ChapterSection.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 ) );
            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">

1 reply

Inspiring
October 15, 2009

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.

mjp420Author
Participating Frequently
October 16, 2009

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.

Inspiring
October 16, 2009

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.