Creating PDF documents with iTextSharp






4.90/5 (43 votes)
Creating PDF documents with iTextSharp using a combination of images and text.
Introduction
This article is about using iText to generate PDF files using a combination of images and dynamic text written on select locations on the image. The example project uses an ASP.NET MVC3 application to demonstrate the code but it should not be difficult to adapt it to your own needs.
Background
Me and some friends have been running a hobby web-site called malleus.dk dedicated to two role-playing games called Warhammer Fantasy and Dark Heresy.
One of the applications on our site is a "character generator" where users can build, configure, and maintain their role-playing characters. We wanted to expand this tool with a print functionality allowing our users to have their characters printed to a nice looking character sheet.
We decided that we really needed to generate a PDF document. PDF documents are supported in all browsers and seems to be the de-facto standard for documents on the web. There are a lot of PDF components available on the net, but we quickly settled on iText as it is a very mature product.
iText comes with a free option under the GNU Affero Public License, or with a commercial option which gives you more freedom. I encourage you to check out the details at http://itextpdf.com.
Using the example solution
The example solution contains two projects - a Class Library and an ASP.NET MVC application. iText is used from the Class Library, and the web application is used for testing the Class Library.
The example creates a "diploma" for a bicycle race. You can enter name, date, the name of the race, and the distance. The web application will produce a diploma with the entered text on top.
The basic flow of the example application is illustrated with images in figure 1, 2, and 3:
Clicking "Generate" will create a new PDF document:
From here on, it is only a matter of adding more artwork, tweaking texts, fonts, etc.
Even though the diploma application is very basic, it contains all the programmatic elements required for producing output that looks like figure 4. The artistic skill that goes into images and layout is a job for a designer.
Installing and setting up
Go to http://itextpdf.com/download.php and click "Download iTextSharp". In your Visual Studio project, you need to reference the itextsharp.dll that you just downloaded.
Document generation strategy
When you open up iText and begin examining the API, you will notice that it is capable of writing text, drawing figures, inserting images, creating PDF forms, etc.. For our task, we decided to use a combination of image fragments and text. The bottom layer will contain all the image fragments and the top layer will contain the text.
Creating a PDF document
When you create PDF documents in iText, the top-level abstraction is the Document
class. It provides various document level information such as title, page count, etc.:
public void Create (Stream output) {
Document document = new Document();
PdfWriter writer = PdfWriter.GetInstance(document, output);
document.Open();
The output stream is just a MemoryStream
which is returned as a FileContentResult
:
[HttpPost]
public FileContentResult Generate(GenerateModel m) {
DiplomaPrinter printer =
new DiplomaPrinter(m.Name, m.Distance, m.Date, m.RaceName, m.ShowRulers);
MemoryStream memoryStream = new MemoryStream();
printer.Create(memoryStream);
return File(memoryStream.GetBuffer(), "application/pdf", "diploma.pdf");
}
When this controller action is invoked, it will send the contents of the MemoryStream
back to the client as a PDF file called "diploma.pdf".
To actually fill the document with content, you need to learn to use the PdfWriter
, PdfReader
, and PdfTemplate
classes.
Bottom layer
The bottom layer of the page is filled with other PDF fragments and images. Figure 2 contains our background image. To load these elements into the document, you do the following:
- Load the PDF fragment using a
PdfReader
. - Use the reader to create a
PdfTemplate
instance. - Add the
PdfTemplate
to your currentDocument
instance.
PdfReader readerBicycle = new PdfReader(Resources.GetBicycle());
PdfTemplate background = writer.GetImportedPage(readerBicycle, 1);
Create a new page in the document and add the PdfTemplate
instance to it:
document.NewPage();
PdfContentByte pcb = writer.DirectContentUnder;
pcb.AddTemplate(background, 0, 0);
Resource.GetBicycle()
simply returns a Stream
instance to a resource embedded within the assembly. The call to document.NewPage()
generates a new page in the Document
instance and the writer.DirectContentUnder
property returns a PdfContentByte
instance
for the bottom layer of this new page. The pcb.AddTemplate()
invocation adds the loaded PDF fragment to the document and positions it on the bottom
left corner - coordinates x = 0
and y = 0
.
At first it seems odd that you have to use the PdfWriter
instance to transform the contents of the PdfReader
into a PdfTemplate
.
As Bruno Lowagie - the creator of iText - explained to me, this is done to ensure that shared objects are reused. For instance, if you embed your own fonts in the document,
the best option is to reuse this font whenever you write some text.
For more specific details, I encourage you to get a hold of the book "iText in Action - 2nd edition".
Top layer
The top layer is where I put the text. As with everything else in iText, you have several options. You can add entire paragraph objects or print text at specific points. Since I need to print on top of an image/PDF fragment, I went with the last option.
PdfContentByte pcb = writer.DirectContent;
pcb.BeginText();
SetFont36();
PrintTextCentered(_raceName, 280, 680);
PrintTextCentered(_name, 280, 190);
SetFont18();
PrintTextCentered(_date, 280, 640);
PrintTextCentered("has completed a distance of " + _distance, 280, 160);
_pcb.EndText();
writer.Flush();
The numbers in this code fragment represent a position in a coordinate system.
Setting the font and size:
private static readonly BaseFont font =
BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, false);
private void SetFont36() {
_pcb.SetFontAndSize(font, 36);
}
Invoking this method will set the font to Helvetica with size 36. This will affect the invocations to the following method:
private void PrintTextCentered(string text, int x, int y) {
_pcb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, text, x, y, 0);
}
The _name
, _distance
etc. fields are injected into the DiplomaPrinter
class via the constructor.
Closing the document
To end the document, you should flush your writer and call Close
on the document:
writer.Flush();
document.Close();
This has the effect of pushing the PDF document down to the output stream that you used to create the document with in the first place.
Tricks
When generating documents this way, you often need to adjust the position of text and images. To help with this, I created two methods for adding a ruler to the generated PDF document. If you check the "add rulers" box in the example application, you should see something like the following:
Having these rulers makes it much easier to find the positions of text and images.
You can also play around with the code-page being used by iText when generating text. This is done by changing the value of the encoding parameter
of the BaseFont.CreateFont()
method. The example uses the value "Cp1252" which is for Western Europe. Reading the source code for iText,
I can see that it also supports the values "Cp1250" (Central- and Eastern Europe) and "Cp1257" (the three Baltic states) but I have yet to play around with these.
Conclusion
iText is a mature piece of software with tons of possibilities. I have only scratched the surface of what is possible. A quick scan of the API reveals that you can do PDF forms, drawing, encryption, etc. I once tried to read the PDF specification and found that it is one complex beast. I very much prefer delegating the work to an API such as iText.
History
- 1 November 2011 - First edition.