Tuesday, May 17, 2011

iTextSharp - how to control the PDF

I'll put it simply like that:
I want a PDF where I control each and every object's location / size / other properties. Usually the objects are either texts or images. So I ended up with a small piece of code which does that, and returns byte array. The byte array is easier to transmit over the net.

Let's begin with the wrapping:
public byte[] GetPDF()
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
iTextSharp.text.Document doc = new iTextSharp.text.Document();
iTextSharp.text.pdf.PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, ms);
doc.Open();
iTextSharp.text.Rectangle page = doc.PageSize;
iTextSharp.text.pdf.PdfContentByte cb = writer.DirectContent;
string fontsDirName = (string)Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders").GetValue("Fonts");
iTextSharp.text.FontFactory.RegisterDirectory(fontsDirName);
RenderPDFImages(doc);

RenderPDFTexts(cb, doc.PageSize.Height);
doc.Close();
ms.Flush();
return ms.ToArray();
}


Quite self-explanatory, wrap the Render PDF methods with the creation of the document and the transform to byte array. Note that RenderPDFImages gets the document and RenderPDFTexts gets the content + page's height.
Both of the render methods loop over the objects and call the "Add" method. I'm supplying the "Add" methods, instead of the complete loop.
Important things to remember:
1. The Y coordinate is calculated from the bottom, not from the top. For this reason the code is full of Page Height - Y - Local Height.
2. Before starting to add texts call cb.BeginText(); and when you're done call cb.EndText();

First the AddImage:
private void AddImage(iTextSharp.text.Document doc, System.Drawing.Image img, float X, float Y)
{
iTextSharp.text.Image gif = iTextSharp.text.Image.GetInstance(imageToByteArray(img));
gif.Alignment = iTextSharp.text.Image.ALIGN_LEFT;
gif.SetAbsolutePosition(X, doc.PageSize.Height - Y - gif.PlainHeight);
doc.Add(gif);
}


Note: I convert the image to byte array, that way I can control the type of image inserted + it avoids a strange behavior I've encountered when trying to add it directly.

And the last method, AddText:
private void AddText(iTextSharp.text.pdf.PdfContentByte cb, float PageHeight, string txt, System.Drawing.Font fnt, System.Drawing.Color clr, float x, float y)
{
iTextSharp.text.pdf.BaseFont bf = iTextSharp.text.FontFactory.GetFont(fnt.Name).BaseFont;
cb.SetRGBColorFill(clr.R, clr.G, clr.B);
cb.SetFontAndSize(bf, fnt.Size);

cb.SetTextMatrix(x, PageHeight - y - fnt.Height);
cb.ShowText(txt);

}

There are many more things which can be done with those objects. I used the bare minimum to get the output in PDF format while making sure it looks almost like the original picture.

1 comment:

Anonymous said...

Hi,

Nice blog! Anyone uses iTextSharp to create PDFs containing form fields. It is for a form that is generated from a database. iTextSharp is a C# port of a popular java library for creating Adobe Acrobat PDF files. It's powerful and flexible. It would be great if you can provide more details about it. Thank you.

Adobe Digital Rights