Generating PDF Documents From Templates
Generating PDF documents on the fly isn't exactly rocket science and - with the help of tools like iText - is easily done in Java and something I've talked about before.
The trouble is that creating them is a cumbersome and often tedious task if what you want is anything other than a set of plain text paragraphs added in series. Unlike creating HTML everything in PDF-land is of absolute size and position. I find that getting the result you want is often a case of repetitive, pixel by pixel tweaking to get it right. You need a lot of patience.
If you had a complicated document to create with logos and background images then you're in for a rough ride. What would make more sense is to use a GUI tool to author a base template and then just have your code add the text bits where necessary. This is the situation I found myself in recently and after a bit of digging I worked out how to take an existing PDF and add to it. PDF templating if you will.
Creating a Template
First thing to do, assuming you've not already been provided with a template is to create one. Let's look at creating a letter head. I did this in Word, as you can see below and simply pasted in some images, moved them about and added some company details to the footer in an off grey colour.
Once I was happy with it I used the File - Save As - PDF menu option to save the file in PDF format.
You can see the resulting PDF below:
What I did with the PDF is attached it to a document in the same Notes database as we're generating the PDFs from. In reality I had been provided with a PDF template by my client's design agency, so that's what I attached. I could skip the bit above about creating a template, but went through it here in case you need help or inspiration.
The idea behind adding it as an attachment to a document is that the client has control over the template and can change it as and when they please. That being another benefit of this approach - if they happened to change the design of the letterhead then I'd have to go back to messing with the code each time. With this approach there should be no need to.
All we need to do now is add the actual content. Here's an example of what the generated PDF looks like once we've done adding to it with our own code:
Let's look at how we'd do that.
Adding To The Template
The code below is the Java needed to create a new PDF document, import the existing template and then add our own text atop of it.
//Setup a new PDF Document Document document = new Document(); ByteArrayOutputStream os = new ByteArrayOutputStream(); PdfWriter writer = PdfWriter.getInstance(document, os); document.open(); document.newPage(); //Get the template PDF as the starting point! View files = db.getView("files"); lotus.domino.Document filestore = settings.getDocumentByKey("pdf_template.pdf", true); String filename = filestore.getItemValueString("FileName"); EmbeddedObject file = filestore.getAttachment(filesname); InputStream is = file.getInputStream(); //Here's the key part. Let's turn the template in to //usable PDF object PdfReader reader = new PdfReader(is); PdfImportedPage page = writer.getImportedPage(reader, 1); //Now, add it to the blank PDF document we've opened PdfContentByte cb = writer.getDirectContent(); cb.addTemplate(page, 0, 0); //Now do the usual addition of text atop the template document.add( new Paragraph("Here some text added to the template") ); //Etc, etc //Done! document.close(); //Tidy up is.close(); file.recycle(); //Make SURE you do this!!! //Attach the resuling PDF to the Notes document Stream stream = session.createStream(); stream.write(os.toByteArray()); MIMEEntity m = doc.createMIMEEntity("Files"); MIMEHeader header = m.createHeader("content-disposition"); header.setHeaderVal("attachment;filename=\""+filename+"\""); m.setContentFromBytes(stream, "application/pdf", MIMEEntity.ENC_IDENTITY_BINARY); m.decodeContent();
Code's boring though. What you want is a demo and a download, no?
A Demo
Here's the demo form. Fill in the fields and submit to see what gets generated. Here's an example of an outputted PDF file.
The working download will be in form of DEXT 20091123, which will be in the Sandbox in the next few minutes.
Once again, you're flying ahead of the curve. Lovely stuff.
Reply
My good pal Dragon bet me to it - Jake, you're my hero! Haha. Amazing stuff. The black art of PDF generation might finally be demystified!
Now I'm annoyed ... I'm scheduled up on work all week but want to go and play instead!!!
:-)
Thanks
Reply
Thanks Jake. Another great demo. We can put this directly to work!
-- Jeff
Reply
Thanks Jake, another great suggestion. In the sandbox area, perhaps you linked the previous database.
Thanks again
Reply
Woops. You're right Renato. I uploaded the wrong Zip file. Should be the right one now...
Reply
Excellent Jake, this is incredibly useful as I often have the need to create pdfs on the fly and have been looking for a less cumbersome solution.
Reply
Very helpful Jake to know this is possible. I'm a bit confused, however.
--
First I think you've got a typo here:
String filename = filestore.getItemValueString("FileName");
EmbeddedObject file = filestore.getAttachment(filesname);
You have "filename" first and then "filesname" second.
--
I don't find any definition for "doc" in this line of code.
MIMEEntity m = doc.createMIMEEntity("Files");
--
When you add the template into the blank PDF the code looks like this:
cb.addTemplate(page, 0, 0);
I'm guessing the "0,0" is the coordinates of where to add the template page. The code to add the new paragraph looks like this:
//Now do the usual addition of text atop the template
document.add(
new Paragraph("Here some text added to the template")
But this codes has no coordinates. How do you know where it's going to land on the PDF page?
--
In fact the relationship between these variables is confusing:
"document" appears to be a Notes document;
"writer" a PdfWriter associated with "document";
"os" a ByteArrayOutputStream associates with "writer";
"cb" a PdfContentByte seems to be a temp to hold template page but I never see it used again once written to.
I guess they are all different ways to write to the same document.
Is "document" a Notes document?
It appears that "os" is only used when creating the final attached PDF while "writer" gets used in creating the PDF.
Well, I'm stopping right here because I'm not where I can access the documentation that might clear up some of my questions. Clearly you've put a lot of work into figuring out how to do this and I know it's asking a lot, but please consider could walking us through the convoluted transformations happening in this code.
Thanks again for sharing this.
Peace,
Rob:-]
Reply
The code you see above isn't working code, as such. It's code I pasted from working code and then altered to make it "read better". Being an idiot I've made a few typos while doing that. If you want to better understand the code then you'll need to download the NSF demo file and see the Java in there. It should make more sense. I also left out things I thought it safe to assume - like that the "doc" is the DocumentContext etc. Although I can see the confusion with the "document" object which is the PDF document and not a Notes one. My variable naming has never been that clear really.
Truth is I don't really understand the interplay between all the objects you mentioned enough to feel qualified to document it that well. All I'm good at is getting stuff to work. You don't always need to understand it to do that ;o)
Jake
Reply
Show the rest of this thread
A very nice, simple solution. It would be great to see this merged with the good work that Tony Palmer has done http://palmerweb.blogspot.com/search/label/PDF
Anyone with good Java skills and some free time want to take this on?
Reply
I was going to reply and say "thanks for the link, not seen that before" but then I noticed I was the first person to respond to his post here:
http://palmerweb.blogspot.com/2008/10/simple-java-library-for-creating-pdfs.html
As I said there, what he's done is a brilliant idea. Like I mentioned above, working with iText is tedious to say the least. Using Tony's extra layer removes some of this tedium so well worth using if you're planning on creating PDFs.
Reply
Show the rest of this thread
I've used a couple of techniques to generate PDFs on the fly. The first one used a Java agent with the iText library. Everything was hand-cranked - the whole content of the PDF was generated by the Java agent and whilst it worked pretty well it was a long-winded job.
The second method used a Word template with bookmarks as described in this post by Jakob Majkilde (http://blog.majkilde.com/2008/12/word-integration.html). The Word document was then saved as a PDF also described by Jakob (http://blog.majkilde.com/2009/09/creating-pdf-files-with-office-2007.html). Again this worked pretty well, although I seem to remember that images on the Word template appeared to degrade when the document was saved to PDF.
I need to do this again in the near future, so I think I'll take a look at the technique you've described here. Thanks, Jake.
Reply
Have a look at the PdfStamper class in iText too. Something I'll try and look in to and write about at some point too...
Reply
Great work Jake. Thanks for sharing
Reply
Well done, Jake. This was the subject of a customer requirement last year (the customer I'm presently working for) and at that time the solution came down to OLE automating the population of an Excel template and routing to a PDF print driver. Your solution is much cleaner even though getting the same sort of output from iText would be difficult in our case... it is worth building on, though. Thanks for the ongoing and outstanding contributions to the community tool-box!
Reply
iText HeaderFooter class works nicely when you want to change the header/footer in multi-page docs (which you usually do)
Reply
I'm no iText expert (and I haven't used the HeaderFooter class), but my understanding is that the recommended method of handling a change of page is to use the PDFPageEventHelper class that has onEndPage and onStartPage events.
Reply
Anyone that has an idea of how to use this with "landscape" oriented template, just can't figure it out. Anyone ?
Reply
Want to know which .jar files used. Urgent please reply.
Reply
I downloaded the dext demo.zip but lotus notes contents to pdf is not working...although excel & text conversion happened....please advise what is wrong in that getting below messages in log.
domino version 6.5.3
Regards
Raj
05/11/2010 12:00:16 PM HTTP JVM: java.lang.UnsupportedClassVersionError: Unsupported major.minor version 48.0
Reply
The domino version you're using uses a version of Java to low to support the classes needed for this demo.
Reply
Show the rest of this thread
Hi,
I want to view pdf file in jframe using swings(corejava),is there any possiblity let me know,thanks advance
Reply