Copy link to clipboard
Copied
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 Correct answer
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 watermar
...Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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...
Copy link to clipboard
Copied
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">
Copy link to clipboard
Copied
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
Copy link to clipboard
Copied
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

