Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsGeneralPHPASPPerlColdFusionFlashHTML, CSS, ScriptsBrowsers

Webmaster Forum / ColdFusion / Advanced Techniques / May 2008



Tip: Looking for answers? Try searching our database.

iText Watermark position issue

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Lumpia - 10 Apr 2008 20:07 GMT
Coders,

Please forgive me in advance as my java kung foo is not that strong.  I have a
site which allows users to merge multiple pdf documents they choose via a
checklist, and then it will apply a small watermark to the top left (xposition
158, yposition 743).  The problem is that there are some pdfs in which the
watermark gets set in the middle of the page instead of the top left.  I
believe I have read in some forums where it may be a size issue of sorts, but
I'm still confused.  I do know that I have found a weird work around, which
involves saving the pdf documents that don't work right, then rescanning via
copy machine and having my copy machine send it back to me via email.  It will
then apply the watermark to the upper left corner.  I will attach my code below
(note: code is written in coldfusion and java):

I hope this isn't confusing.  Thanks in advance for everyone's assistance.

<!--- BEGIN WATERMARK SCRIPT --->

<cfscript>

paths = arrayNew(1);

    /*
    This points to the jar we want to load.
    Could also load a directory of .class files
    */
    paths[1] = expandPath("iText.jar");

    //create the loader
    loader2 = createObject("component", "javaloader.JavaLoader").init(paths);

function insertWatermarkPDF(pdfFileIn, imageFile, pdfFileOut, xPos, yPos,
zIndex)
{
    // zindex refers to placing the image over or under the content, default is
under
    // but if your content has a background, it may obscure the watermark
    if (NOT structKeyExists(arguments, "zIndex"))
        arguments.zIndex = 0;

    //try
    //{
        document = loader2.create("com.lowagie.text.Document");
        document.init();

        pdfReader = loader2.create("com.lowagie.text.pdf.PdfReader");
        pdfReader.init(arguments.pdfFileIn);

        // page countING
        n = pdfReader.getNumberOfPages();

        // creae an outputstream
        streamOut = loader2.create("java.io.FileOutputStream");
        streamOut.init(arguments.pdfFileOut);

        // give it to the pdfstamper
        pdfStamper = loader2.create("com.lowagie.text.pdf.PdfStamper");
        pdfStamper.init(pdfReader, streamOut);
       
        // contentbyte is a static object, no constructor
        under = loader2.create("com.lowagie.text.pdf.PdfContentByte");

        // create the Image handler, static object, no constructor
        Image = loader2.create("com.lowagie.text.Image");

        // now create an instance with this file (report_watermark.jpg)
        img = Image.getInstance(JavaCast("string", arguments.imageFile));
        img.setAbsolutePosition(arguments.xPos, arguments.yPos);

        for (ii = 1; ii LT n; ii = ii + 1)
        {
            // pdfStamper supports getUnderContent() and getOverContent()
            if (arguments.zIndex)
                under = pdfStamper.getOverContent(JavaCast("int", ii));
            else
                under = pdfStamper.getUnderContent(JavaCast("int", ii));

            under.addImage(img);
        }

        pdfStamper.close();

        return true;
    //}
    //catch (any e)
    //{
    //    return false;
    //}
}

</cfscript>

<cfoutput>#insertWatermarkPDF("<PATH REMOVED>\concatenation.pdf"
    ,"<PATH REMOVED>\n#form.tail#up.gif"
    ,"<PATH REMOVED>\concatenation2.pdf"
    ,158
    ,743
    ,1)#
</cfoutput>
Lumpia - 22 Apr 2008 13:40 GMT
Ok, it appears that what I need to do is implement the use of iText's mediabox and cropbox.  Has anyone successfully used mediabox and cropbox within a cfscript tag?  Thanks.
-==cfSearching==- - 24 Apr 2008 03:24 GMT
What issue are you having with it? Using your code as a base, this simple
example displays the cropbox information for each page.

<cfscript>
    pdfReader = loader2.create("com.lowagie.text.pdf.PdfReader");
    pdfReader.init(arguments.pdfFileIn);
    n = pdfReader.getNumberOfPages();

    for (ii = 1; ii LTE n; ii = ii + 1)
    {
        // display crop box for each page
        box = pdfReader.getCropBox(ii);
        WriteOutput(" PAGE "& ii &" (crop box): ");
        WriteOutput(" Top="& box.getTop());
        WriteOutput(" Right="& box.getRight());
        WriteOutput(" Bottom="& box.getBottom());
        WriteOutput(" Left="& box.getLeft());
        WriteOutput(" Width="& box.getWidth());
        WriteOutput(" Height="& box.getHeight() &"<br>");
    }
</cfscript>

Having nothing to do with watermarks, I did notice a two things that might
cause other problems.

[i]//create the loader
loader2 = createObject("component", "javaloader.JavaLoader").init(paths);[/i]

There is a bug in ColdFusion that can cause a memory leak when using
java.net.URLClassLoader. So it is recommended that you store the javaLoader in
the server scope rather than instantiating it each time it is used.
http://www.transfer-orm.com/?action=displayPost&ID=212

[i]document = loader2.create("com.lowagie.text.Document");
streamOut.init(arguments.pdfFileOut);[/i]
...       
pdfStamper.close();
[/i]

Consider structuring your try/catch code so it always closes document,
outputstream and stamper objects. Even if an error occurs.  Sort of like a
try/catch/finally clause.  IMO it is a good practice that can prevent locked
files and other io related problems.
Lumpia - 24 Apr 2008 19:27 GMT
Cfsearching,

Thanks for your many advices.  Regarding the memory leak issue, I am going to
incorporate that now.  I figure the way to add it to server scope with UUID
would be like so:  

server.uuid.loader = createObject("component",
"javaloader.JavaLoader").init(paths);

However, for each referencing object, would I also add server.uuid on them,
like so:

pdfCopy=server.uuid.loader.create("com.lowagie.text.pdf.PdfCopy");
pdfReader=server.uuid.loader.create("com.lowagie.text.pdf.PdfReader");
pageSize=server.uuid.loader.create("com.lowagie.text.PageSize").init();
bookMark=server.uuid.loader.create("com.lowagie.text.pdf.SimpleBookmark");
pdfDocument=server.uuid.loader.create("com.lowagie.text.Document");

In reference to media & crop box, I've been reading 'iText In Action' manual
and it has this example:

Document document = new Document(new Rectangle(432, 792));
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream("page_boundaries.pdf"));
writer.setCropBoxSize(new Rectangle(5, 5, 427, 787));

which is written in java, of course, so I'm having troubles converting it to
use in cfscript and also knowing where to place it in my current code.

Thanks again for your help.
-==cfSearching==- - 24 Apr 2008 22:22 GMT
[i]I figure the way to add it to server scope with UUID would be like so:  
...[/i]

Almost. Just make sure the server key for the javaloader is unique.  I usually
create a UUID with the CreateUUID function and hardcode that value into my
Application.cfc file. The key is used when instantiating the javaLoader into
the server scope. I then make the key avaliable to all pages by setting a
variable in the OnRequest function.  Though you could also use Request
variables if you prefer.  See the attached example.

[i]In reference to media & crop box, I've been reading 'iText In Action'
manual and it has this example:

Document document = new Document(new Rectangle(432, 792));
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream("page_boundaries.pdf"));
writer.setCropBoxSize(new Rectangle(5, 5, 427, 787));
[/i]

Well, I do not think you want to set the cropBox so much as use its dimensions
to determine where to position the watermark.  At least that is the impression
I am getting.  Do you have an example of one of the pdfs in which the watermark
gets set to the middle of the page?

Application.cfc
<cfcomponent>
    <cfset this.name = "iTextExamples">
    <cfset this.Sessionmanagement = true>
    <cfset this.loginstorage = "session">

    <cffunction name="onApplicationStart">
        <!--- do not use this key. it is not a real UUID  --->
        <cfset application.MyUniqueKeyForJavaLoader =
"1E66946E-D893-FQ23-3B4DEFB37472374">
         <!--- if we have not already created the javaLoader --->
          <cfif NOT structKeyExists(server, application.MyUniqueKeyForJavaLoader)>
            <!--- construct an array containing the full path to the jars you wish to
load --->
            <cfset paths = arrayNew(1)>
            <cfset arrayAppend(paths, expandPath('/dev/iText/iText-2.0.7.jar'))>
            <cfset arrayAppend(paths, expandPath('/dev/iText/itextUtil.jar'))>
              <cflock scope="server" type="exclusive" timeout="10">
                  <!--- re-verify the javaloader was not already created --->
                 <cfif NOT StructKeyExists(server,
application.MyUniqueKeyForJavaLoader)>
                   <cfset server[application.MyUniqueKeyForJavaLoader] =
createObject("component", "javaloader.JavaLoader").init(paths)>
                 </cfif>
             </cflock>
         </cfif>
    </cffunction>

    <cffunction name="onRequest">
        <cfargument name="targetPage" type="String" required=true/>
        <cfset MyUniqueKeyForJavaLoader = application.MyUniqueKeyForJavaLoader >
       <cfinclude template="#Arguments.targetPage#">       
    </cffunction>
</cfcomponent>

Then call it in my CF pages like so

<cfscript>
// get a reference to the javaLoader
javaLoader = server[MyUniqueKeyForJavaLoader];
// the use the local variable for all subsequent actions
pdfCopy= javaLoader.create("com.lowagie.text.pdf.PdfCopy");
....
</cfscript>
Lumpia - 29 Apr 2008 21:10 GMT
cfSearching,

Thanks for the code for server scope.  I have implemented it and it works
great.

[i]Well, I do not think you want to set the cropBox so much as use its
dimensions to determine where to position the watermark. At least that is the
impression I am getting. Do you have an example of one of the pdfs in which the
watermark gets set to the middle of the page? [/i]

My understanding of the cropbox is that it sets the visable area, whereas the
mediabox sets the print area; therefore, it would appear I need them both set.  
I didn't change the numbers in the example, as I don't quite know how to change
the java code to work in Coldfusion.

Unfortunately, I tried to look for an example pdf file to show, but all of
them have proprietary information on them I cannot share publically.  I also
attempted to create one in hopes to replicate the affected, but it did not work.

Would you be able to convert the java code to Coldfusion in my current code?  
It's the last brick needed to complete my house; otherwise, it comes tumbling
down.  :confused;
-==cfSearching==- - 30 Apr 2008 03:42 GMT
[i]My understanding of the cropbox is that it sets the visable area, whereas
the mediabox sets the print area; therefore, it would appear I need them both
set.[/i]

Well, you certainly could set both but I am still wondering whether you need
to actually [i]set[/i] the values or just [i]read[/i] them.  In other words,
are you trying to read the existing values so you know where to position the
image, or overwrite the existing settings.  If all of the pdf's are a known and
fixed size overwriting should be okay. However, if the code is just arbitrarily
setting the media/cropbox sizes without knowledge of any existing settings,
that results might be unexpected.

The conversion of the java snippet you posted is below.  If you are going to
use PdfWriter in your code, I suspect you will need to make some additional
changes. Unfortunately I do not have time to work it into your example, but
this should be enough to get you started.

(BTW,  you should also update the function code to VAR scope the local
function variables)

<cfscript>
...

    // create re-usable Rectangle object
    Rectangle = loader2.create("com.lowagie.text.Rectangle");

    // create a document with the desired dimensions
    document = loader2.create("com.lowagie.text.Document");
    document.init( Rectangle.init( javacast("float", 432), javacast("float",
792)) );

    // create an outputstream for the output file
    streamOut = loader2.create("java.io.FileOutputStream");
    streamOut.init( arguments.pdfFileOut );

    // create a PdfWriter instance
    writer = loader2.create("com.lowagie.text.pdf.PdfWriter").getInstance(
document, streamOut );

    // create a rectangle representing the desired cropbox dimensions
    cropBox = Rectangle.init(     javacast("float", 5),
                                javacast("float", 5),
                                javacast("float", 427),
                                javacast("float", 787)
                            );

    writer.setCropBoxSize( cropBox );
...
</cfscript>
Lumpia - 30 Apr 2008 21:09 GMT
[i]Well, you certainly could set both but I am still wondering whether you need
to actually set the values or just read them. In other words, are you trying to
read the existing values so you know where to position the image, or do you
always want to overwrite the existing settings? If all of the pdf's are a known
and fixed size overwriting should be okay. However, if the code just
arbitrarily sets the media/cropbox sizes without knowledge of any existing
settings, the results might be unexpected.[/i]

I believe I am just trying to read the existing values so I my site will know
where to position the image; as all the pdf's are not a fixed size.  I must be
missing/not understanding something as I have incorporated your code (Thanks),
but it did not make a change.  I've been over the iText documentation many
times now, but can't seem to find my error.  I also used re-modified my code so
that the cropbox and mediabox adjust for a 8.5" X 11" page (per this site:  
http://itext.ugent.be/library/question.php?id=588) but the watermark still
stays in the center.  Perhaps this what you were referring to when you said
results might be unexpected.

I've attached my (full) altered code so hopefully you or someone may find the
flaws. Note that the first part of the code actually merges documents, and the
2nd part applies the watermark.  Thanks again.

<cfscript>
    // THIS FIRST PART OF CODE MERGES MULTIPLE PDFS

    // Get a reference to the javaloader
    // Note: Supporting data for MyUniqueKeyForJavaLoader is stored in the
application.cfc file
    javaLoader = server[MyUniqueKeyForJavaLoader];

    listOfPDFs="#listOfFilesWithPath#"; // list of PDFs to concatenate
    if (IsDefined("form.location"))
     finalOutPutFile="concatenation.pdf"; // new output file
    else
     finalOutPutFile="concatenation.pdf"; // new output file
   
    // setup needed objects
    pdfCopy=javaLoader.create("com.lowagie.text.pdf.PdfCopy");
    pdfReader=javaLoader.create("com.lowagie.text.pdf.PdfReader");
    pageSize=javaLoader.create("com.lowagie.text.PageSize").init();
    bookMark=javaLoader.create("com.lowagie.text.pdf.SimpleBookmark");
    pdfDocument=javaLoader.create("com.lowagie.text.Document");
    // setup new PDF
    newPDF=javaLoader.create("java.io.FileOutputStream").init(finalOutPutFile);
    //
newPDF=loader2.create("java.io.FileOutputStream").init(expandPath(finalOutPutFil
e));
    // grab existing PDFs   
    pageOffset=0;
    PDFs=listToArray(listOfPDFs); //pdfs to copy
    master=arrayNew(1); //master list
    for (i=1; i LTE arrayLen(PDFs); i=i+1) {
        reader=""; // clobber reader
        pdfFile=PDFs[i];
        // pdfFile=expandPath(PDFs[i]);
        reader=pdfReader.init(pdfFile);
        reader.consolidateNamedDestinations();
        pages=reader.getNumberOfPages(); // number of pages in this PDF
        bookmarks=bookMark.getBookmark(reader);
        if (isDefined("bookmarks")) {
            if (pageOffset NEQ 0)
                bookMark.shiftPageNumbers(bookmarks, pageOffset, javacast("null",""));
            arrayAppend(master,bookmarks);   
        } // if has bookmarks
        pageOffset=pageOffset+pages;
        if (i EQ 1) {
            pdfDocument.init(reader.getPageSizeWithRotation(1));
            pdfCopy.init(pdfDocument, newPDF);
            pdfDocument.open();       
        }  // first file in list?
        // now add pages to new PDF
        for (p=1; p LTE pages; p=p+1){
            page=pdfCopy.getImportedPage(reader,javacast("int",p));
            pdfCopy.addPage(page);
        }// loop pages in this PDF
        // special case: does this thing have any forms?
        acroForm=reader.getAcroForm();
        if (isDefined("acroForm"))
            pdfCopy.copyAcroForm(reader);
    } //loop PDFs   
    if (arraylen(master) GT 0)
        pdfCopy.setOutlines(master);
    pdfDocument.close(); //done & done   

    // BEGIN WATERMARK SCRIPT   
    function insertWatermarkPDF(pdfFileIn, imageFile, pdfFileOut, xPos, yPos,
zIndex)
    {
        // zindex refers to placing the image over or under the content, default is
under
        // but if your content has a background, it may obscure the watermark
        if (NOT structKeyExists(arguments, "zIndex"))
            arguments.zIndex = 0;
   
        //try
        //{
            // create re-usable Rectangle object
            rectangle = javaLoader.create("com.lowagie.text.Rectangle");
       
            document = javaLoader.create("com.lowagie.text.Document");
            document.init( rectangle.init(
                                javacast("float", 0),
                                javacast("float", 0),
                                javacast("float", 684),
                                javacast("float", 864)
                                ) );
   
            pdfReader = javaLoader.create("com.lowagie.text.pdf.PdfReader");
            pdfReader.init(arguments.pdfFileIn);
   
            // page counting
            n = pdfReader.getNumberOfPages();
           
            // create an outputstream for the output file
            streamOut = javaLoader.create("java.io.FileOutputStream");
            streamOut.init(arguments.pdfFileOut);
           
            // create a PdfWriter instance
            writer = javaLoader.create("com.lowagie.text.pdf.PdfWriter").getInstance(
document, streamOut );
   
            // give it to the pdfstamper
            pdfStamper = javaLoader.create("com.lowagie.text.pdf.PdfStamper");
            pdfStamper.init(pdfReader, streamOut);
           
            // create a rectangle representing the desired cropbox dimensions
            cropBox = rectangle.init(    
                                javacast("float", 36),
                                javacast("float", 36),
                                javacast("float", 648),
                                javacast("float", 828)
                            );

            writer.setCropBoxSize( cropBox );
           
             // contentbyte is a static object, no constructor
            under = javaLoader.create("com.lowagie.text.pdf.PdfContentByte");
   
            // create the Image handler, static object, no constructor
            Image = javaLoader.create("com.lowagie.text.Image");
   
            // now create an instance with this file (report_watermark.jpg)
            img = Image.getInstance(JavaCast("string", arguments.imageFile));
            img.setAbsolutePosition(arguments.xPos, arguments.yPos);
   
            for (ii = 1; ii LT n; ii = ii + 1)
            {
                // pdfStamper supports getUnderContent() and getOverContent()
                if (arguments.zIndex)
                    under = pdfStamper.getOverContent(JavaCast("int", ii));
                else
                    under = pdfStamper.getUnderContent(JavaCast("int", ii));
   
                under.addImage(img);
            }
   
            pdfStamper.close();
   
            return true;
        //}
        //catch (any e)
        //{
        //    return false;
        //}
    }
   
    </cfscript>
-==cfSearching==- - 01 May 2008 03:58 GMT
[i]I must be missing/not understanding something as I have incorporated your
code (Thanks)[/i]

Yes, I think so.  The code was just a straight translation of your java
snippet.  As mentioned, you would need to make additional changes if you were
to try using a PdfWriter (instead of a stamper) to add the watermarks. However,
to  just read the existing dimensions, you should not need a writer at all.  

Obtain the media/crop box sizes from the reader (like in my original example).
Then use the box values to calculate the desired x and y coordinates of the
watermark.

for (ii = 1; ii LT n; ii = ii + 1) {
   box = pdfReader.getCropBox( javacast("int", ii) );
   under = pdfStamper.getUnderContent( javaCast("int", ii) );
   // xOffset and yOffset being the desired left an top margin
   // ie  start drawing the image x points from the left of the crop box and y
points from the top
   xPos = box.getLeft() + xOffset;
   yPos = box.getTop() - yOffset - img.getHeight();
   img.setAbsolutePosition( xPos, yPos );
   ....
   under.addImage(img);

}

However, IIRC those methods do not take into account rotation.  So that is
something you will have to look into yourself.  Disclaimer: It has been a long
day. The mind is tired. Take this all with a grain of salt ;-)
Lumpia - 01 May 2008 20:57 GMT
cfSearching,

Thanks!  After modifying your code slightly, it now applies the watermark in
the correct position based on the size of the pdf page.  I do have one problem
however.  The way my watermark function works, is that it takes the following
arguments (pdfFileIn, imageFile, pdfFileOut, xPos, yPos, zIndex) and assigns
values based on my input.  The way my site works is that the user can put
checkmarks in all the pdfs they want merged and/or have watermark(s) applied.  
There are 2 dropdown menus to select watermarks; in which, the user can choose
1 or both to have applied to the top of the page.

With your newly added code, it appears that it now disregards my xPos and yPos
(shown below in the function) and uses the newly added cropbox code for the
coordinates.  

<cfscript>
function insertWatermarkPDF(pdfFileIn, imageFile, pdfFileOut, xPos, yPos,
zIndex)
{
...
}
</cfscript>

<!--- IMAGE1 WAS CHOSEN --->
<cfif form.image1 neq "">
  <cfoutput>#insertWatermarkPDF("concatenation.pdf"
    ,"#form.image1#.gif"
    ,"concatenation2.pdf"
    ,158
    ,743
    ,1)#
  </cfoutput>
</cfif>

<!--- IMAGE2 WAS CHOSEN --->
<cfif form.image2 neq "">
  <cfoutput>#insertWatermarkPDF("concatenation2.pdf"
   ,"#form.image2#.gif"
   ,"concatenation3.pdf"
   ,158
   ,700
   ,1)#
  </cfoutput>
</cfif>

<!--- OPEN ALTERED PDF FILE --->
<cfif IsDefined("form.image2") and form.image2 neq "">  <!--- USER CHOSE
IMAGE2 FROM DROPDOWN --->
  <cflocation url="concatenation3.pdf">
<cfelse>  <!--- IMAGE1 WAS CHOSEN --->
  <cflocation url="concatenation2.pdf">
</cfif>

So what ends up occuring is that both images are applied in the exact same
postion (upper left), but overlapping each other.  It makes sense why it is
using the newly added codes xPos and yPos, but I am wondering now how to let it
account for 2 images in 2 different yPos coordinates.

Thanks again!  This is working out great and the circle is almost complete!
BKBK - 01 May 2008 05:21 GMT
Lumpia, if you're on CF8 you can use the cfpdf tag to merge PDFs in one line of code.
Lumpia - 01 May 2008 13:20 GMT
BKBK,

True.  Unfortunately, my company is still using CFMX7 until next year. If only...
BKBK - 01 May 2008 14:12 GMT
I see you're using
http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?forumid=1&catid=7&t
hreadid=1114635. There has since been at least one important modification to
it. For example, the [i]master[/i] array becomes a vector. See the contribution
from  -==cfSearching==- on the 4th page of the thread.

 

<cfscript>
    // NO spaces in list! Else it crashes. Bug in CF8?
    listOfPDFs="install.pdf,configuring.pdf,cf8_cfml_ref.pdf,cf8_devguide.pdf";
// list of PDFs to concatenate
    finalOutPutFile="concatenated.pdf"; // new output file
    // setup needed objects
    pdfCopy=createObject("java", "com.lowagie.text.pdf.PdfCopy");
    pdfReader=createObject("java","com.lowagie.text.pdf.PdfReader");
    pageSize=createObject("java", "com.lowagie.text.PageSize").init();
    bookMark=createObject("java","com.lowagie.text.pdf.SimpleBookmark");
    pdfDocument=createObject("java","com.lowagie.text.Document");
    // setup new PDF   

    newPDF=createObject("java","java.io.FileOutputStream").init(expandPath(finalOut
PutFile));
    // grab existing PDFs
    pageOffset=0;
    PDFs=listToArray(listOfPDFs); //pdfs to copy
    //master=arrayNew(1); //master list
    master = createObject("java", "java.util.Vector"); //modification 5/1/2008
inspired by CF forum
    for (j=1; j LTE arrayLen(PDFs); j=j+1) {
        reader=""; // clobber reader
        pdfFile=expandPath(PDFs[j]);
        reader=pdfReader.init(pdfFile);
        reader.consolidateNamedDestinations();
        pages=reader.getNumberOfPages(); // number of pages in this PDF
        bookmarks=bookMark.getBookmark(reader);
        if (isDefined("bookmarks")) {
            if (pageOffset NEQ 0)
            bookMark.shiftPageNumbers(bookmarks, pageOffset, javacast("null",""));
            //arrayAppend(master,bookmarks);
            master.addAll(bookmarks);//modification 5/1/2008 inspired by CF forum
        } // if doc has bookmarks
        pageOffset=pageOffset+pages;
        if (j EQ 1) {
            pdfDocument.init(reader.getPageSizeWithRotation(1));
            pdfCopy.init(pdfDocument, newPDF);
            pdfDocument.open();
        } // first file in list?
        // now add pages to new PDF
        for (p=1; p LTE pages; p=p+1){
            page=pdfCopy.getImportedPage(reader,javacast("int",p));
            pdfCopy.addPage(page);
        }// loop pages in this PDF
        // special case: does this thing have any forms?
        acroForm=reader.getAcroForm();
        if (isDefined("acroForm")) pdfCopy.copyAcroForm(reader);
    } //loop PDFs
    //if (arraylen(master) GT 0) pdfCopy.setOutlines(master);
    if (NOT master.isEmpty()) pdfCopy.setOutlines( master );//modification
5/1/2008 inspired by CF forum
    pdfDocument.close(); //done & done
</cfscript>
PDF concatenation done!
Lumpia - 01 May 2008 21:04 GMT
BKBK,

Thanks for your tip and for looking at my code.  I have incorporated the change from cfSearchings post into my code and it works great.  This forum really is a handy tool to get assistance.
-==cfSearching==- - 01 May 2008 21:27 GMT
[i]
<cfif form.image1 neq "">
<cfoutput>#insertWatermarkPDF("concatenation.pdf"
,"#form.image1#.gif"
,"concatenation2.pdf"
,158
,743
,1)#
</cfoutput>
</cfif>
[/i]

Can you post your latest function code for adding the watermark?  

Because I do not think my previous code snippet would work with those xPos,
yPos values (148, 743).  They look like absolute positions.  The idea with my
previous example was to pass in the desired [i]offsets[/i] and let the function
determine what the x and y coordinates should be. In other words, you tell the
function the watermark should be positioned 1/2 inch from the left (xOffset)
and 3/4 of an inch from the top (yOffset) of the crop box.  The function then
uses that information to calculate the x and y coordinates needed to achieve
this.
Lumpia - 03 May 2008 20:20 GMT
cfSearching,

You are correct.  The way I originally coding this site was with absolute
positions, but obviously, my method did not take into account the mediabox and
cropbox sizes and adjust accordingly.  You are definitely on the right track,
and I'm not opposed to taking the absolute postions out and just using the java
offsets instead, I really just need it to work with 2 images/coordinates at
this point (instead of overlapping each other).  Thanks again for your help.

Here is the entire Watermark function code:

<cfscript>

// BEGIN WATERMARK SCRIPT   
function insertWatermarkPDF(pdfFileIn, imageFile, pdfFileOut, xPos, yPos,
zIndex)
{
    // zindex refers to placing the image over or under the content, default is
under
    // but if your content has a background, it may obscure the watermark
    if (NOT structKeyExists(arguments, "zIndex"))
        arguments.zIndex = 0;

    //try
    //{
        document = javaLoader.create("com.lowagie.text.Document");
        document.init();

        pdfReader = javaLoader.create("com.lowagie.text.pdf.PdfReader");
        pdfReader.init(arguments.pdfFileIn);

        // page countING
        n = pdfReader.getNumberOfPages();
       
        // create an outputstream
        streamOut = javaLoader.create("java.io.FileOutputStream");
        streamOut.init(arguments.pdfFileOut);

        // give it to the pdfstamper
        pdfStamper = javaLoader.create("com.lowagie.text.pdf.PdfStamper");
        pdfStamper.init(pdfReader, streamOut);
       
        // contentbyte is a static object, no constructor
        under = javaLoader.create("com.lowagie.text.pdf.PdfContentByte");

        // create the Image handler, static object, no constructor
        Image = javaLoader.create("com.lowagie.text.Image");

        // now create an instance with this file (report_watermark.jpg)
        img = Image.getInstance(JavaCast("string", arguments.imageFile));
        img.setAbsolutePosition(arguments.xPos, arguments.yPos);

        for (ii = 1; ii LTE n; ii = ii + 1)
        {
            box = pdfReader.getCropBox( javacast("int", ii) );
            if (arguments.zIndex)
                under = pdfStamper.getOverContent( javaCast("int", ii) );
            else
                under = pdfStamper.getUnderContent( javaCast("int", ii) );
               
            // xOffset and yOffset being the desired left an top margin
            // ie start drawing the image x points from the left of the crop box and y
points from the top
            xPos = box.getLeft() + 160;
            yPos = box.getTop() - 20 - img.getHeight();
            img.setAbsolutePosition( xPos, yPos );

            under.addImage(img);
        }

        pdfStamper.close();

        return true;
    //}
    //catch (any e)
    //{
    //    return false;
    //}
}

</cfscript>
   
   
<!--- IMAGE1 WAS CHOSEN --->
<cfif form.image1 neq "">
<cfoutput>#insertWatermarkPDF("concatenation.pdf"
,"#form.image1#.gif"
,"concatenation2.pdf"
,158
,743
,1)#
</cfoutput>
</cfif>

<!--- IMAGE2 WAS CHOSEN --->
<cfif form.image2 neq "">
<cfoutput>#insertWatermarkPDF("concatenation2.pdf"
,"#form.image2#.gif"
,"concatenation3.pdf"
,158
,700
,1)#
</cfoutput>
</cfif>

<!--- OPEN ALTERED PDF FILE --->
<cfif IsDefined("form.image2") and form.image2 neq ""> <!--- USER CHOSE IMAGE2
FROM DROPDOWN --->
<cflocation url="concatenation3.pdf">
<cfelse> <!--- IMAGE1 WAS CHOSEN --->
<cflocation url="concatenation2.pdf">
</cfif>
-==cfSearching==- - 03 May 2008 23:29 GMT
[i]xPos = box.getLeft() + 160;
yPos = box.getTop() - 20 - img.getHeight();
[/i]

The code is drawing both images in the same place, because that is what you
are telling it do ;-)  The values are hard coded.to always draw the image 160pt
from the left and 20pt from the top. Every time. So when you call the function
multiple times, the images overlap because the offsets, and consequently the x
and y positions, never change.  The xOffset and yOffset must be dynamic, so you
can adjust the image positions. Make sense?

Just change the function to accept xOffset/yOffset instead of xPos/yPos. Then
use xOffset/yOffset inside your loop, instead of the hard coded values 160 and
20.  When you call the function, pass in the desired offsets.  Just remember to
adjust the values to take into account the width or height of first watermark.  
Otherwise the images will still overlap.

But again, do not forget to VAR your local function variables to avoid
thread/scope problems.  Also be sure to fix the code to always close the
FileOutputStream or you may have problems accessing the final files.

ADD OFFSETS TO FUNCTION

   function insertWatermarkPDF(pdfFileIn, imageFile, pdfFileOut, xOffset,
yOffset, zIndex)
      ...
     
      for ( ii = 1; ii LTE totalPages; ii = ii + 1)
      {
         ...
         box  = reader.getCropBox( javacast("int", ii) );
         //   calculate the x and y postions based on the given offsets
         xPos = box.getLeft() + arguments.xOffset;
         yPos = box.getTop() - arguments.yOffset - img.getHeight();
         img.setAbsolutePosition( xPos, yPos );
         ..
      }
      ...
   }      

   CALL FUNCTION WITH OFFSETS
   
   <!---
      position the first image 1/2 inch (36pt) from left
      and 3/4 inch (54pt) from the top of cropbox
   --->
   <cfset variables.xOffset = 36>
   <cfset variables.yOffset = 54>
   
   <cfif len(trim(form.image1)) >
      <!--- NOTE - these should be absolute paths --->
      <cfset insertWatermarkPDF(    fullPathToInputFile
                           , fullPathToImage1
                           , fullPathToOutputFile1
                           , variables.xOffset
                           , variables.yOffset
                           , 1
                        )>
      <!---
         to place the second image BELOW the first one, we must
         add the height of the first image to the yOffset
         
         to place it to the RIGHT of the first one, add the width
         to the xoffset
      --->  
      <cfset variables.img =
javaLoader.create("com.lowagie.text.Image").getInstance(
variables.fullPathToImage1 )>
      <cfset variables.yOffset = variables.yOffset + variables.img.getHeight()>
                                   
   </cfif>
   
   <cfif len(trim(form.image2)) >
      <!--- NOTE - these should be absolute paths --->
      <cfset insertWatermarkPDF(    fullPathToOutputFile1
                           , fullPathToImage2
                           , fullPathToOutputFile2
                           , variables.xOffset
                           , variables.yOffset
                           , 1
                        )>
   </cfif>
Lumpia - 06 May 2008 20:03 GMT
cfSearching,

Something appears to be off as it is still not adjusting for 2 different
images at different yOffset positions.  I'm not getting an error, just a
previous pdf merge instead.  You stated that this code:

<cfset variables.img =
javaLoader.create("com.lowagie.text.Image").getInstance(
variables.fullPathToImage1 )>
<cfset variables.yOffset = variables.yOffset + variables.img.getHeight()>

takes into account the size of the first image and readjusts the yOffset
accordingly.  Essentially, could I not just 2 different yOffsets like so:

<cfset variables.xOffset = 160>
<cfset variables.yOffset = 20>
   
<cfif len(trim(form.image1)) >
    <!--- NOTE - these should be absolute paths --->
    <cfset insertWatermarkPDF(    fullPathToInputFile
                           , fullPathToImage1
                           , fullPathToOutputFile1
                           , variables.xOffset
                           , variables.yOffset
                           , 1
                        )>
</cfif>

<cfset variables.xOffset = 160>
<cfset variables.yOffset = 30>

<cfif len(trim(form.image2)) >
      <!--- NOTE - these should be absolute paths --->
      <cfset insertWatermarkPDF(    fullPathToOutputFile1
                           , fullPathToImage2
                           , fullPathToOutputFile2
                           , variables.xOffset
                           , variables.yOffset
                           , 1
                        )>
   </cfif>

Regardless of how I modify the code, it still wants to overlap the images.  
Correct me if I'm wrong, but it seems like the code within the function
(img.setAbsolutePosition(xPos, yPos)) needs to be adjusted to account if there
are 2 images.  

for ( ii = 1; ii LTE totalPages; ii = ii + 1)
      {
         ...
         box  = reader.getCropBox( javacast("int", ii) );
         //   calculate the x and y postions based on the given offsets
         xPos = box.getLeft() + arguments.xOffset;
         yPos = box.getTop() - arguments.yOffset - img.getHeight();
         img.setAbsolutePosition( xPos, yPos );
         ..
      }

I don't know, it just seems like the problem lies somewhere in there, perhaps
an if statement or something.  I may be completely off, just a thought.  Again,
your assistance is ALWAYS appreciated.  Thanks.
-==cfSearching==- - 06 May 2008 22:59 GMT
[i]Essentially, could I not just 2 different yOffsets like so:[/i]

Yes. It should work as long as the values correctly account for the size of
the first image.  Using
<cfset variables.yOffset = variables.yOffset + variables.img.getHeight()>  

Just makes it more dynamic.  If you ever changed the watermark images, the
code would still work without having to change hard coded values.

[i]Regardless of how I modify the code, it still wants to overlap the images.
Correct me if I'm wrong, but it seems like the code within the function
(img.setAbsolutePosition(xPos, yPos)) needs to be adjusted to account if there
are 2 images.[/i]

Yes, you are right in thinking that.  However, it is already taken care of by
<cfset variables.yOffset = variables.yOffset + variables.img.getHeight()>.  
That code adjusts the yOffset, right before the second call, so it takes into
account the height of the first watermark.  Let us say your first watermark has
a height of 40.  The result should be the same as if you were to adjusted the
positions manually like this:

<cfset variables.xOffset = 160>
<!--- Draw the first watermark 20pt from the top of the crop box--->
<cfset variables.yOffset = 20>
<cfset insertWatermarkPDF( ..., variables.xOffset, variables.yOffset, ...)>

<cfset variables.xOffset = 160>
<!--- Draw the second watermark 60pt from the top of the crop box--->
<!--- ie yOffset = 20 + 40 (image height) --->
<cfset variables.yOffset = 60>
<cfset insertWatermarkPDF( ..., variables.xOffset, variables.yOffset, ...)>

Try the attached example.  Download these (2) sample files from the iText
site. Save them to the same directory as your CFM script. You could use
different files, but this way we know you will get the same results.


http://itext.ugent.be/library/com/lowagie/examples/general/copystamp/ChapterSect
ion.pdf

http://itext.ugent.be/library/com/lowagie/examples/general/copystamp/watermark.j
pg       

Run the example then open the file named "ChapterSectionFinal.pdf".  You
should see that the second watermark is placed below the first one.

FUNCTION

<cfscript>
//     BEGIN WATERMARK SCRIPT   
//
//     zindex refers to placing the image over or under the content, default is
under
//     but if your content has a background, it may obscure the watermark
function insertWatermarkPDF(pdfFileIn, imageFile, pdfFileOut, xOffset,
yOffset, zIndex)
{

    var Local = structNew();
   
    Local.success = true;
   
    try
    {
        // create a PdfReader to read the input file
        Local.reader =
javaLoader.create("com.lowagie.text.pdf.PdfReader").init( arguments.pdfFileIn );
        Local.totalPages = Local.reader.getNumberOfPages();
       
        // create a PdfStamper to generate the output file
        Local.out = javaLoader.create("java.io.FileOutputStream").init(
arguments.pdfFileOut );
        Local.stamper =
javaLoader.create("com.lowagie.text.pdf.PdfStamper").init( Local.reader,
Local.out);
       
        // Load the watermark image from the supplied path
        Local.img = javaLoader.create("com.lowagie.text.Image").getInstance(
arguments.imageFile );

        for ( Local.ii = 1; Local.ii LTE Local.totalPages; Local.ii = Local.ii
+ 1)
        {
            if ( arguments.zIndex ) {
                Local.layer = Local.stamper.getOverContent( javaCast("int",
Local.ii) );
            }    
            else {
                Local.layer = Local.stamper.getUnderContent( javaCast("int",
Local.ii) );
            }    
           
            //    get the size of the cropbox
            Local.box  = Local.reader.getCropBox( javacast("int", Local.ii) );
            //    calculate the x and y postions based on the given offsets
            Local.xPos = Local.box.getLeft() + arguments.xOffset;
            Local.yPos = Local.box.getTop() - arguments.yOffset -
Local.img.getHeight();

            Writeoutput("DEBUGGING ONLY: page["& Local.ii &"] xPos="&
Local.xPos &", yPos="& Local.yPos &"<br>");

            Local.img.setAbsolutePosition( Local.xPos, Local.yPos );
            //    draw the watermark image
            Local.layer.addImage( Local.img );
        }

        //     always close the file and outstream
        Local.stamper.close();
        Local.out.close();
        Local.success = true;
    }
    catch (any e)
    {
        if ( structKeyExists(Local, "stamper") ) {
            Local.stamper.close();
        }
        if ( structKeyExists(Local, "out") ) {
            Local.out.close();
        }
        // NOTE - you may wish to rethrow the error here instead
        Local.success = false;
    }
   
    return Local.success;
}

</cfscript>

EXAMPLE
    <!---
        Requires these (2) source files exist in the current directory.
       
http://itext.ugent.be/library/com/lowagie/examples/general/copystamp/ChapterSect
ion.pdf
       
http://itext.ugent.be/library/com/lowagie/examples/general/copystamp/watermark.j
pg        
    --->

    <cfset form.image1 = "watermark">
    <cfset form.image2 = "watermark">
   
    <!--- These must be absolute paths --->
    <cfset variables.fullPathToImage1        =
ExpandPath("./#form.image1#.jpg")>
    <cfset variables.fullPathToImage2        =
ExpandPath("./#form.image2#.jpg")>
    <cfset variables.fullPathToInputFile    =
ExpandPath("./ChapterSection.pdf")>
    <cfset variables.fullPathToOutputFile1     =
ExpandPath("./ChapterSectionTemp.pdf")>
    <cfset variables.fullPathToOutputFile2     =
ExpandPath("./ChapterSectionFinal.pdf")>
   
    <!---
        position the first image 160pt from left and 20pt from the top of
cropbox
    --->
    <cfset variables.xOffset = 160>
    <cfset variables.yOffset = 20>
   
    <cfif len(trim(form.image1)) >
        <cfset insertWatermarkPDF(     fullPathToInputFile
                                    , fullPathToImage1
                                    , fullPathToOutputFile1
                                    , variables.xOffset
                                    , variables.yOffset
                                    , 0
                                )>
        <!---
            to place the second image BELOW the first one, we must
            add the height of the first image to the yOffset
           
            to place it to the RIGHT of the first one, add the width
            to the xoffset
        --->    
        <cfset variables.img =
javaLoader.create("com.lowagie.text.Image").getInstance(
variables.fullPathToImage1 )>
        <cfset variables.yOffset = variables.yOffset +
variables.img.getHeight()>
                                               
    </cfif>
   
    <cfif len(trim(form.image2)) >
        <cfset insertWatermarkPDF(     fullPathToOutputFile1
                                    , fullPathToImage2
                                    , fullPathToOutputFile2
                                    , variables.xOffset
                                    , variables.yOffset
                                    , 0
                                )>
    </cfif>
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.