iTextSharp - Page Layout with Columns

I have now covered many of the basics involved in generating PDF files from ASP.NET using iTextSharp in the series of articles listed below. This article will look at additional ways to provide formatting to documents through the use of columns.

Create PDFs in ASP.NET - getting started with iTextSharp
iTextSharp - Working with Fonts
iTextSharp - Adding Text with Chunks, Phrases and Paragraphs
Lists with iTextSharp
iTextSharp - Links and Bookmarks
iTextSharp - Introducing Tables
iTextSharp - Working with Images
iTextSharp - Drawing shapes and Graphics

Most often, when working with columns you will want to add multiple columns for text layout, similar to a newspaper. iTextSharp has a MultiColumnText object which makes this pretty simple. All you need to tell it is where the leftmost column should start from along the X-axis, where that rightmost column should end, what space you would like between the columns and how many columns you want. The following adds 2 columns to a page, and then adds a paragraph 8 times:

  

string pdfpath = Server.MapPath("PDFs");

string imagepath = Server.MapPath("Columns");

Document doc = new Document();

try

{

    PdfWriter.GetInstance(doc, new FileStream(pdfpath + "/Columns.pdf", FileMode.Create));

   doc.Open();

    Paragraph heading = new Paragraph("Page Heading", new Font(Font.HELVETICA, 28f, Font.BOLD));

   heading.SpacingAfter = 18f;

   doc.Add(heading);

    string text = @"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse blandit blandit turpis. Nam in lectus ut dolor consectetuer bibendum. Morbi neque ipsum, laoreet id; dignissim et, viverra id, mauris. Nulla mauris elit, consectetuer sit amet, accumsan eget, congue ac, libero. Vivamus suscipit. Nunc dignissim consectetuer lectus. Fusce elit nisi; commodo non, facilisis quis, hendrerit eu, dolor? Suspendisse eleifend nisi ut magna. Phasellus id lectus! Vivamus laoreet enim et dolor. Integer arcu mauris, ultricies vel, porta quis, venenatis at, libero. Donec nibh est, adipiscing et, ullamcorper vitae, placerat at, diam. Integer ac turpis vel ligula rutrum auctor! Morbi egestas erat sit amet diam. Ut ut ipsum? Aliquam non sem. Nulla risus eros, mollis quis, blandit ut; luctus eget, urna. Vestibulum vestibulum dapibus erat. Proin egestas leo a metus?";

    MultiColumnText columns = new MultiColumnText();

    //float left, float right, float gutterwidth, int numcolumns

   columns.AddRegularColumns(36f, doc.PageSize.Width-36f, 24f, 2);

    Paragraph para = new Paragraph(text, new Font(Font.HELVETICA, 8f));

   para.SpacingAfter = 9f;

   para.Alignment = Element.ALIGN_JUSTIFIED;

    for (int i = 0; i < 8; i++)

    {

       columns.AddElement(para);

    }

    

   doc.Add(columns);

    

}

catch (Exception ex)

{

   //Log(ex.Message);

}

finally

{

   doc.Close();

}

  

 The result shows how the text just flowed from one column to another once the first column was filled.

AddRegularColumns() will set each column at exactly the same width. AddSimpleColumn() allows you to specify the width of individual columns, which means that you can work with irregularly sized columns:

  

columns.AddSimpleColumn(36f, 170f);

columns.AddSimpleColumn(194f, doc.PageSize.Width - 36f);

  

The code above replaces the AddRegularColumn() call in the preceding block and creates two separate column objects. The first starts at the left margin (36 points in from the left edge) and is 2 inches wide (170 - 36 = 144 points). The second column starts 24 points (1/3 of an inch) further over and butts up against the right hand margin.

Adding other elements such as images and tables is straightforward:

 

string pdfpath = Server.MapPath("PDFs");

string imagepath = Server.MapPath("Images");

Document doc = new Document();

try

{

  PdfWriter.GetInstance(doc, new FileStream(pdfpath + "/Columns.pdf", FileMode.Create));

  doc.Open();

  Paragraph heading = new Paragraph("Page Heading", new Font(Font.HELVETICA, 28f, Font.BOLD));

  heading.SpacingAfter = 18f;

  doc.Add(heading);

  string text = @"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse blandit blandit turpis. Nam in lectus ut dolor consectetuer bibendum. Morbi neque ipsum, laoreet id; dignissim et, viverra id, mauris. Nulla mauris elit, consectetuer sit amet, accumsan eget, congue ac, libero. Vivamus suscipit. Nunc dignissim consectetuer lectus. Fusce elit nisi; commodo non, facilisis quis, hendrerit eu, dolor? Suspendisse eleifend nisi ut magna. Phasellus id lectus! Vivamus laoreet enim et dolor. Integer arcu mauris, ultricies vel, porta quis, venenatis at, libero. Donec nibh est, adipiscing et, ullamcorper vitae, placerat at, diam. Integer ac turpis vel ligula rutrum auctor! Morbi egestas erat sit amet diam. Ut ut ipsum? Aliquam non sem. Nulla risus eros, mollis quis, blandit ut; luctus eget, urna. Vestibulum vestibulum dapibus erat. Proin egestas leo a metus?";

  MultiColumnText columns = new MultiColumnText();

  columns.AddSimpleColumn(36f, 336f);

  columns.AddSimpleColumn(360f, doc.PageSize.Width - 36f);

  

  Paragraph para = new Paragraph(text, new Font(Font.HELVETICA, 8f));

  para.SpacingAfter = 9f;

  para.Alignment = Element.ALIGN_JUSTIFIED;

  

  PdfPTable table = new PdfPTable(3);

  float[] widths = new float[] { 1f, 1f, 1f };

  table.TotalWidth = 300f;

  table.LockedWidth = true;

  table.SetWidths(widths);

  PdfPCell cell = new PdfPCell(new Phrase("Header spanning 3 columns"));

  cell.Colspan = 3;

  cell.HorizontalAlignment = 0;

  table.AddCell(cell);

  table.AddCell("Col 1 Row 1");

  table.AddCell("Col 2 Row 1");

  table.AddCell("Col 3 Row 1");

  table.AddCell("Col 1 Row 2");

  table.AddCell("Col 2 Row 2");

  table.AddCell("Col 3 Row 2");

  

  Image jpg = Image.GetInstance(imagepath + "/Sunset.jpg");

  jpg.ScaleToFit(300f, 300f);

  jpg.SpacingAfter = 12f;

  jpg.SpacingBefore = 12f;

  

  columns.AddElement(para);

  columns.AddElement(table);

  columns.AddElement(jpg);

  columns.AddElement(para);

  columns.AddElement(para);

  columns.AddElement(para);

  columns.AddElement(para);

  doc.Add(columns);

  

}

catch (Exception ex)

{

  //Log(ex.Message);

}

finally

{

  doc.Close();

}

 

 The example above takes a table and images from previous tutorials, and adds them together with the paragraph to the columns. the first column is 300 points wide, and the second one takes up the remainder of the page width.

MultiColumnText is great for creating columns without too much code, but it lacks an element of control. ColumnText provides much more, but requires more code. The following sample shows ColumnText being used to create irregular columns, in that the first one has chunk taken from the top left corner to accommodate an image which is shown here in the resulting PDF:

 

string pdfpath = Server.MapPath("PDFs");

string imagepath = Server.MapPath("Images");

FontFactory.RegisterDirectory("C:\\WINDOWS\\Fonts");

 

Document doc = new Document();

Font font1 = new Font(FontFactory.GetFont("adobe garamond pro", 36f, Color.GRAY));

Font font2 = new Font(Font.TIMES_ROMAN, 9f);

doc.SetMargins(45f, 45f, 60f, 60f);

try

{

  FileStream output = new FileStream(pdfpath + "/IrregularColumns.pdf", FileMode.Create);

  PdfWriter writer = PdfWriter.GetInstance(doc, output);

  doc.Open();

  PdfContentByte cb = writer.DirectContent;
  ColumnText ct = new ColumnText(cb);

  ct.Alignment = Element.ALIGN_JUSTIFIED;

 

  Paragraph heading = new Paragraph("Chapter 1", font1);

  heading.Leading = 40f;

  doc.Add(heading);

  Image L = Image.GetInstance(imagepath + "/l.gif");

  L.SetAbsolutePosition(doc.Left, doc.Top - 180);

  doc.Add(L);

 

  ct.AddText(new Phrase("orem ipsum dolor sit amet, consectetuer adipiscing elit. Suspendisse blandit blandit turpis. Nam in lectus ut dolor consectetuer bibendum. Morbi neque ipsum, laoreet id; dignissim et, viverra id, mauris. Nulla mauris elit, consectetuer sit amet, accumsan eget, congue ac, libero. Vivamus suscipit. Nunc dignissim consectetuer lectus. Fusce elit nisi; commodo non, facilisis quis, hendrerit eu, dolor? Suspendisse eleifend nisi ut magna. Phasellus id lectus! Vivamus laoreet enim et dolor. Integer arcu mauris, ultricies vel, porta quis, venenatis at, libero. Donec nibh est, adipiscing et, ullamcorper vitae, placerat at, diam. Integer ac turpis vel ligula rutrum auctor! Morbi egestas erat sit amet diam. Ut ut ipsum? Aliquam non sem. Nulla risus eros, mollis quis, blandit ut; luctus eget, urna. Vestibulum vestibulum dapibus erat. Proin egestas leo a metus?\n\n", font2));

  ct.AddText(new Phrase("Vivamus enim nisi, mollis in, sodales vel, convallis a, augue? Proin non enim. Nullam elementum euismod erat. Aliquam malesuada eleifend quam! Nulla facilisi. Aenean ut turpis ac est tempor malesuada. Maecenas scelerisque orci sit amet augue laoreet tempus. Duis interdum est ut eros. Fusce dictum dignissim elit. Morbi at dolor. Fusce magna. Nulla tellus turpis, mattis ut, eleifend a, adipiscing vitae, mauris. Pellentesque mattis lobortis mi.\n\n", font2));

  ct.AddText(new Phrase("Nullam sit amet metus scelerisque diam hendrerit porttitor. Aenean pellentesque, lorem a consectetuer consectetuer, nunc metus hendrerit quam, mattis ultrices lorem tellus lacinia massa. Aliquam sit amet odio. Proin mauris. Integer dictum quam a quam accumsan lacinia. Pellentesque pulvinar feugiat eros. Suspendisse rhoncus. Sed consectetuer leo eu nisi. Suspendisse massa! Sed suscipit lacus sit amet elit! Aliquam sollicitudin condimentum turpis. Nunc ut augue! Maecenas eu eros. Morbi in urna consectetuer ipsum vehicula tristique.\n\n", font2));

  ct.AddText(new Phrase("Donec imperdiet purus vel ligula. Vestibulum tempor, odio ut scelerisque eleifend, nulla sapien laoreet dui; vel aliquam arcu libero eu ante. Curabitur rutrum tristique mi. Sed lobortis iaculis arcu. Suspendisse mauris. Aliquam metus lacus, elementum quis, mollis non, consequat nec, tortor.\n", font2));

  ct.AddText(new Phrase("Quisque id diam. Ut egestas leo a elit. Nulla in metus. Aliquam iaculis turpis non augue. Donec a nunc? Phasellus eu eros. Nam luctus. Duis eu mi. Ut mollis. Nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean pede. Nulla facilisi. Vestibulum mattis adipiscing nulla. Praesent orci ante, mattis in, cursus eget, posuere sed, mauris.\n\n", font2));

  ct.AddText(new Phrase("Nulla facilisi. Nunc accumsan risus aliquet quam. Nam pellentesque! Aenean porttitor. Aenean congue ullamcorper velit. Phasellus suscipit placerat tellus. Vivamus diam odio, tempus quis, suscipit a, dictum eu; lectus. Sed vel nisl. Ut interdum urna eu nibh. Praesent vehicula, orci id venenatis ultrices, mauris urna mollis lacus, et blandit odio magna at enim. Pellentesque lorem felis, ultrices quis, gravida sed, pharetra vitae, quam. Mauris libero ipsum, pharetra a, faucibus aliquet, pellentesque in, mauris. Cras magna neque, interdum vel, varius nec; vulputate at, erat. Quisque vitae urna. Suspendisse potenti. Nulla luctus purus at turpis! Vestibulum vitae dui. Nullam odio.\n\n", font2));

  ct.AddText(new Phrase("Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed eget mi at sem iaculis hendrerit. Nulla facilisi. Etiam sed elit. In viverra dapibus sapien. Aliquam nisi justo, ornare non, ultricies vitae, aliquam sit amet, risus! Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus risus. Vestibulum pretium augue non mi. Sed magna. In hac habitasse platea dictumst. Quisque massa. Etiam viverra diam pharetra ante. Phasellus fringilla velit ut odio! Nam nec nulla.\n\n", font2));

  ct.AddText(new Phrase("Integer augue. Morbi orci. Sed quis nibh. Nullam ac magna id leo faucibus ornare. Vestibulum eget lectus sit amet nunc facilisis bibendum. Donec adipiscing convallis mi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus enim. Mauris ligula lorem, pellentesque quis, semper sed, tristique sit amet, justo. Suspendisse potenti. Proin vitae enim. Morbi et nisi sit amet sapien ve.\n\n", font2));

 

  float gutter = 15f;

  float colwidth = (doc.Right - doc.Left - gutter) / 2;

  float[] left = { doc.Left + 90f , doc.Top - 80f,

                  doc.Left + 90f, doc.Top - 170f,

                  doc.Left, doc.Top - 170f,

                  doc.Left , doc.Bottom };

 

  float[] right = { doc.Left + colwidth, doc.Top - 80f,

                    doc.Left + colwidth, doc.Bottom };

 

  float[] left2 = { doc.Right - colwidth, doc.Top - 80f,

                    doc.Right - colwidth, doc.Bottom };

 

  float[] right2 = {doc.Right, doc.Top - 80f,

                    doc.Right, doc.Bottom };

 

  int status = 0;

  int i = 0;

  //Checks the value of status to determine if there is more text

  //If there is, status is 2, which is the value of NO_MORE_COLUMN

  while (ColumnText.HasMoreText(status))

  {

    if (i == 0)

    {

      //Writing the first column

      ct.SetColumns(left, right);

      i++;

    }

    else

    {

      //write the second column

      ct.SetColumns(left2, right2);

    }

    //Needs to be here to prevent app from hanging

    ct.YLine = doc.Top - 80f;

    //Commit the content of the ColumnText to the document

    //ColumnText.Go() returns NO_MORE_TEXT (1) and/or NO_MORE_COLUMN (2)

    //In other words, it fills the column until it has either run out of column, or text, or both

    status = ct.Go();

  }

}

catch (Exception ex)

{

  //Log(ex.Message);

}

finally

{

  doc.Close();

}

 

ColumnText requires that you need a PDFContentByte object, just as with graphics and drawing. This is nearly always the case when you want to place stuff at fixed positions. The code above starts by defining a document object, and then creating some fonts to work with, as well as setting the margins of the document (which needs to be done before the document is opened to take effect). A PdfWriter object is created so that the PDFContentByte object can be instantiated from its DirectContent property. Then a ColumnText object is created using the PDFContentByte. A heading is added, followed by the illustrated capital "L", which starts the Chapter. This is followed by a number of Phrases, set out as paragraphs.

So, now the ColumnText object has its text content, but nowhere to put it yet, so the two columns are created. The first array of floats defines the irregular shape of the left hand side fo the first column which has to the top corner chopped out of it to make way for the image. The second array defines the straight right hand edge. The third and fourth arrays take care of the regularly shaped second column. Two int variables are created to hold the value of the status of the column with regard to whether there is any more text to be added (status), and then the number of the column that is currently being worked on (i).

ColumnText.HasMoreText(status) is a convenience method that takes an int as a parameter. It examines the value of the parameter to see if there is any more text to be added and returns true if there is. The value that returns false is 1. It is initialised at 0 to start, so HasMoreText returns true. Since i is also 0, the code executes to add the first column, then the YLine is set to the top of the column. This must be done, otherwise the application will just spin until the execution timeout occurs. Finally, the ColumnText.Go() method is called. This actually commits the contents of the ColumnText to the document, and also returns a value to the variable status. It returns either NO_MORE_TEXT, which is 1, and/or NO_MORE_COLUMN (2). If NO_MORE_TEXT is returned, everything has been written, otherwise the second column is created and the text added to that. Once ColumnText.HasMoreText returns false, the page is written.

Building on this, if the amount of text spans more than one page, the routine can be rewritten to call Document.NewPage(), and if Go() is called after each Phrase is added to the ColumnText object, it is possible to obtain the current Y position on the page to establish how much room there is left. this enables you to manage widows and orphans - single lines of text that are belong to a paragraph that appear at the end of the preceding page or column (orphans) or appear at the top of a new page or column (widows). It is also possible to pass the parameter "false" into Go(), which does not commit the text to the column, but still allows you to obtain the position as if it was to allow you much finer control over layout.

Date Posted: Monday, November 24, 2008 7:41 AM
Last Updated: Wednesday, September 28, 2011 10:24 PM
Posted by: Mikesdotnetting
Total Views to date: 160399

18 Comments

Friday, April 24, 2009 7:20 AM - Lucky

What about pdf conversion of aspx pages if i have dynamic data continuously on 8 pages. And i want to convert all these dynamic pages into a single pdf. I m struck on this problem. Can u help me out?

With Regards
lucky

Friday, April 24, 2009 8:06 PM - Mike

@Lucky

Unfortunately, iTextSharp is not great with converting web pages to PDF. You have a number of options. One is to create a template PDF in code and simply write the data to it on demand, or another is to export snippets of html like a GridView. Alternatively, you can try fiddling around with the HtmlAgility Pack to help: http://www.codeplex.com/htmlagilitypack. This is something I've been meaning to have a go at when I have some spare time.

Wednesday, May 20, 2009 4:54 AM - Cesar

Hi, in the last example, you add an image at the begining of the first paragraph, with absolute positioning, but, what if we need to add an image in every paragraph? how can you do that?

Thanks a lot
Cesar

Wednesday, May 20, 2009 9:42 PM - Mike

@Cesar,

That requires you to keep a tab of how much of the column you have filled with text so far. It involves a fair amount of mathematics taking into account the font-size, leading etc.

Friday, January 8, 2010 10:28 PM - KESM

Is it possible to have a two-column layout but force specific paragraphs to be in specific columns? So, I want to have paragraphs x, y, and z in column 1, then skip to the top of column 2 and add paragraphs a, b, and c. I've played with a few things like adding a MultiColumnText.NextColumn() call in between adding paragraphs, but that moves ALL of the paragraphs to the second column.

Thanks for the great tutorials!
Cheers,
K

Friday, January 8, 2010 10:51 PM - Mike

@KESM

It's a while since I played with iTextSharp, but my first instinct would be to look at using a table instead for what you want.

Sunday, January 10, 2010 8:47 PM - KESM

Thanks Mike, I think that might be the best solution too. Cheers!

Friday, May 21, 2010 3:15 PM - sareesh

Its really nice on Itextsharp.

Thursday, August 12, 2010 12:52 AM - Cris

Hi, I am new with iTextSharp and I see your page has very good examples but I guess there is no easy way to convert an aspx generated page with absolute position divs and tables if not by turning everything into a paragraph or some other object, or is it?

Thursday, August 12, 2010 4:45 AM - Mike

@Cris

You are right. There is no easy way to convert html to PDF using iTextSharp.

Wednesday, September 8, 2010 4:10 PM - John V

Hi,
This is one of the best tutorial I have seen on iTextShatp and .Net. Is there a way to create the PDF in memory (without first saving as a fiel) and display as a popup page?

Thanks

Saturday, September 11, 2010 10:25 AM - Mike

The PdfWriter.GetInstance() method can take a MemoryStream object as the second parameter.

Tuesday, December 7, 2010 4:18 PM - Gordon

1. Great set of tututorials, so massive thank you. :)

2. Have you done anything with the later versions of iTextSharp inheriting from IPdfPageEvents to set Headers and Footers? I'm really struggling to take the header text out of this class file and into my code behind so I can use this class file as a generic setup for multiple apps on a website.

Any and all help gratefully appreciated!

Cheers

Thursday, December 9, 2010 7:46 PM - Mike

@Gordon,

No. I haven't actually touched any version of iTextSharp for a couple of years.

Tuesday, February 15, 2011 10:05 PM - Mych

Hi... this may sound silly but I am new to Visual Studio. I have been programming in vb. Can I use a c# to vb converter to convert your samples and code in vb or do I have to use C#. Thanks

Monday, April 4, 2011 1:43 AM - schwab

Have you been able to generate pdf docs using xml and xslt files. I was unable to find any kind of sample code realted to this on the internet. Is this possible with itextsharp or do I have to uses paragraphs and ordered ist to display the document.Thanks fo your help.

Tuesday, June 24, 2014 4:26 AM - Amy

I can't find the MultiColumnText , why ? either the version 5.1.3 nor 5.5

Thursday, June 26, 2014 1:28 PM - Mike

@Amy

These tutorials were written 5 or 6 years ago and were based on version 4.1.6 of iTextSharp. I have no plans to update them in line with version 5 of the library because it is not entirely free for commercial use like version 4 was.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

Gautam 11/20/2014 8:01 AM
In response to I'm Writing A Book On WebMatrix
Hello Mike, I read your book, loved it! However, I have a few request/suggestions: 1) an example...

Bret Dev 11/19/2014 8:39 PM
In response to The Difference Between @Helpers and @Functions In WebMatrix
Excellent post! One concern - where can you place global @Functions code within an MVC project to Is...

Rob Farquharson 11/19/2014 4:28 PM
In response to iTextSharp - Links and Bookmarks
How can I place text at an absolute position on the page? Also, how can I rotate text?...

Andy 11/17/2014 8:08 PM
In response to MVC 5 with EF 6 in Visual Basic - Sorting, Filtering and Paging
Hello I'm testing your sorting instructions above. This is great and I was able to get it to work...

Gautam 11/17/2014 5:51 PM
In response to WebMatrix - Database Helpers for IN Clauses
Hi Mike, I am very new to programming: In the above example if I want to use a delete button the...

donramon 11/17/2014 3:22 PM
In response to Entity Framework 6 Recipe - Alphabetical Paging In ASP.NET MVC
Congratulations on your new website look and the excellent articles. Thank you!...

Gautam 11/17/2014 11:26 AM
In response to Looking At The WebMatrix WebGrid
Hi Mike, I add the jquery script at the end of my html file.. when ajax attribute is added to the be...

Chet Ripley 11/15/2014 6:57 PM
In response to Adding A New Field
It appears the command is case sensitive. I had the same issue as Cameron. When I changed the to it...

Alvin 11/14/2014 12:49 PM
In response to Razor Web Pages E-Commerce - Adding A Shopping Cart To The Bakery Template Site
Great article Mike! When do you plan to extend the bakery shopping cart beyond this point?...

Gautam 11/14/2014 10:16 AM
In response to Web Pages - Efficient Paging Without The WebGrid
to get the count can we use only the below sql, why to join category and author table var sql =...