Introduction
This article presents an example to export tabular data in PDF format through the web.
Background
Once in a while, you may want to export some data in PDF format. This article is to present an example to export tabular data in PDF format using "iTextSharp" through the web. The example will be using an "MVC" controller to export the generated PDF files, so I will assume the readers to have some basic knowledge on "ASP.NET MVC".
The attached is an "MVC" application developed in Visual Studio 2010. This example uses "iTextSharp" to create the PDF documents. You can download "iTextSharp" from here.
- The "PdfTabularReport.cs" and the "ReportConfiguration.cs" files in the "Utilities" folder implement some utility classes that will be used to generate PDF documents.
- The "StudentRepository.cs" file in the "Models" folder is the application's data model.
- The "HomeController.cs" file in the "Controllers" folder is the "MVC" application's controller. Its main functionality is call the PDF utility classes to create PDF documents and to send them to the web browsers.
- The "Default.htm" is the web page where the users can download the PDF document.
I will first introduce the utility classes and then show you how to use these classes to create PDF documents and how to send them to the web browsers.
The Utility Classes
The utility classes are implemented in two files. The following is the "ReportConfiguration.cs" file.
using iTextSharp.text;
namespace iTextTabularReport.Utilities
{
public class ReportColumn
{
public string ColumnName { get; set; }
public int Width { get; set; }
private string headerText;
public string HeaderText { set { headerText = value; }
get { return headerText ?? ColumnName; } }
}
public class ReportConfiguration
{
private Rectangle pageOrientation = PageSize.LETTER;
private int marginLeft = 30;
private int marginTop = 20;
private int marginRight = 30;
private int marginBottom = 30;
public Rectangle PageOrientation { get { return pageOrientation; }
set { pageOrientation = value; } }
public int MarginLeft { get { return marginLeft; } set { marginLeft = value; } }
public int MarginTop { get { return marginTop; } set { marginTop = value; } }
public int MarginRight
{ get { return marginRight; } set { marginRight = value; } }
public int MarginBottom
{ get { return marginBottom; } set { marginBottom = value; } }
private int logImageScalePercent = 100;
public string LogoPath { get; set; }
public int LogImageScalePercent { get { return logImageScalePercent; }
set { logImageScalePercent = value; } }
public string ReportTitle { get; set; }
public string ReportSubTitle { get; set; }
}
}
Two classes are implemented in this file. These two classes are used by the classes implemented in the "PdfTabularReport.cs" file to configure the PDF document.
- The "
ReportColumn
" class defines a column's width and header text of the tabular data. - The "
ReportConfiguration
" class defines the general layout of the PDF document.
The "PdfTabularReport.cs" file is implemented as the following:
using System;
using System.Linq;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.Data;
using System.Collections.Generic;
namespace iTextTabularReport.Utilities
{
public class PdfTabularReport
{
public ReportConfiguration ReportConfiguration { get; set; }
public Image LogoImage { get; private set; }
public PdfPTable Title { get; private set; }
public PdfPTable PageNumberLabel { get; private set; }
public float HeaderSectionHeight { get; private set; }
public int PageCount { get; private set; }
Document PdfDocument = null;
PdfWriter PdfWriter = null;
MemoryStream PdfStream = null;
public PdfTabularReport(ReportConfiguration configuration = null)
{
ReportConfiguration = configuration ?? new ReportConfiguration();
}
private void InitiateDocument()
{
PdfStream = new MemoryStream();
PdfDocument = new Document(ReportConfiguration.PageOrientation);
PdfDocument.SetMargins(ReportConfiguration.MarginLeft,
ReportConfiguration.MarginRight,
ReportConfiguration.MarginTop, ReportConfiguration.MarginBottom);
PdfWriter = PdfWriter.GetInstance(PdfDocument, PdfStream);
PdfPCell cell;
HeaderSectionHeight = 0;
LogoImage = null;
if (ReportConfiguration.LogoPath != null)
{
LogoImage = Image.GetInstance(ReportConfiguration.LogoPath);
LogoImage.ScalePercent(ReportConfiguration.LogImageScalePercent);
LogoImage.SetAbsolutePosition(PdfDocument.LeftMargin,
PdfDocument.PageSize.Height - PdfDocument.TopMargin
- LogoImage.ScaledHeight);
HeaderSectionHeight = LogoImage.ScaledHeight;
}
Title = null;
float titleHeight = 0;
if ((ReportConfiguration.ReportTitle != null) ||
(ReportConfiguration.ReportSubTitle != null))
{
Title = new PdfPTable(1);
Title.TotalWidth = PdfDocument.PageSize.Width
- (PdfDocument.LeftMargin + PdfDocument.RightMargin);
if (ReportConfiguration.ReportTitle != null)
{
cell = new PdfPCell(new Phrase(ReportConfiguration.ReportTitle,
new Font(ReportFonts.HelveticaBold, 12)));
cell.HorizontalAlignment = Element.ALIGN_CENTER;
cell.Border = 0;
Title.AddCell(cell);
}
if (ReportConfiguration.ReportSubTitle != null)
{
cell = new PdfPCell(new Phrase(ReportConfiguration.ReportSubTitle,
new Font(ReportFonts.Helvetica, 10)));
cell.HorizontalAlignment = Element.ALIGN_CENTER;
cell.PaddingTop = 5;
cell.Border = 0;
Title.AddCell(cell);
}
for (int i = 0; i < Title.Rows.Count; i++)
{
titleHeight = titleHeight + Title.GetRowHeight(i);
}
}
HeaderSectionHeight = (HeaderSectionHeight > titleHeight)
? HeaderSectionHeight : titleHeight;
PageNumberLabel = new PdfPTable(1);
PageNumberLabel.TotalWidth = PdfDocument.PageSize.Width
- (PdfDocument.LeftMargin + PdfDocument.RightMargin);
cell = new PdfPCell(new Phrase
("Page Label", new Font(ReportFonts.Helvetica, 8)));
cell.Border = 0;
float pagenumberHeight = PageNumberLabel.GetRowHeight(0);
HeaderSectionHeight = (HeaderSectionHeight > pagenumberHeight)
? HeaderSectionHeight : pagenumberHeight;
}
private MemoryStream RenderDocument(ReportTable reportTable)
{
PdfWriter.PageEvent = new PageEventHelper { Report = this };
PdfDocument.Open();
reportTable.RenderTable(PdfDocument, PdfWriter);
PdfDocument.Close();
PdfWriter.Flush();
return PdfStream;
}
public MemoryStream GetPdf<T>(List<T> data, List<ReportColumn> displayColumns)
{
InitiateDocument();
var top = (HeaderSectionHeight == 0)
? PdfDocument.PageSize.Height - PdfDocument.TopMargin
: PdfDocument.PageSize.Height - PdfDocument.TopMargin
- HeaderSectionHeight - 10;
var reportTable = ReportTable.CreateReportTable<T>(data,
displayColumns, top, PdfDocument);
PageCount = reportTable.PageCount;
return RenderDocument(reportTable);
}
public MemoryStream GetPdf(DataTable data, List<ReportColumn> displayColumns)
{
List<DataRow> list = data.AsEnumerable().ToList();
return GetPdf<DataRow>(list, displayColumns);
}
}
public class ReportFonts
{
public static BaseFont Helvetica {
get { return BaseFont.CreateFont(BaseFont.HELVETICA,
BaseFont.CP1252, false); } }
public static BaseFont HelveticaBold {
get { return BaseFont.CreateFont(BaseFont.HELVETICA_BOLD,
BaseFont.CP1252, false); } }
}
internal class ReportTable
{
private PdfPTable headerTable;
private PdfPTable dataTable;
private List<Tuple<int, int>> pageSplitter;
private float width;
private float top;
private float height;
public int PageCount
{
get { return (pageSplitter.Count == 0) ? 1 : pageSplitter.Count; }
}
private ReportTable(List<ReportColumn> displayColumns,
Document document, float top)
{
pageSplitter = new List<Tuple<int, int>>();
this.top = top;
width = document.PageSize.Width
- document.LeftMargin - document.RightMargin;
height = top - document.BottomMargin;
float[] columnWidths =
(from c in displayColumns select (float)c.Width).ToArray();
headerTable = new PdfPTable(columnWidths);
headerTable.TotalWidth = width;
dataTable = new PdfPTable(columnWidths);
dataTable.TotalWidth = width;
foreach (var column in displayColumns)
{
AddCell(headerTable, column.HeaderText,
new Font(ReportFonts.HelveticaBold, 10), BaseColor.WHITE, 5f);
}
}
private static void AddCell(PdfPTable table, string Text, Font font,
BaseColor backgroundColor = null, float padding = 3f)
{
PdfPCell cell = new PdfPCell(new Paragraph(Text, font));
cell.Padding = padding;
cell.Border = 0;
cell.VerticalAlignment = Element.ALIGN_MIDDLE;
cell.BackgroundColor = backgroundColor ?? BaseColor.WHITE;
table.AddCell(cell);
}
private static void AddRow(Object dataitem, System.Type type,
List<ReportColumn> displayColumns, BaseColor color, PdfPTable table)
{
foreach (var column in displayColumns)
{
var text = string.Empty;
if (type.FullName == "System.Data.DataRow")
{
text = ((DataRow) dataitem)[column.ColumnName].ToString();
}
else
{
var propertyInfo = type.GetProperty(column.ColumnName);
text = (propertyInfo.GetValue(dataitem, null) == null)
? ""
: propertyInfo.GetValue(dataitem, null).ToString();
}
AddCell(table, text, new Font(ReportFonts.Helvetica, 8), color);
}
}
public static ReportTable CreateReportTable<T>(List<T> data,
List<ReportColumn> displayColumns, float top, Document document)
{
var reportTable = new ReportTable(displayColumns, document, top);
var type = typeof(T);
int srartRow = 0;
int pageRowIndex = 0;
float headerHeight = reportTable.headerTable.GetRowHeight(0);
float actualHeight = headerHeight;
for (int i = 0; i < data.Count; i++)
{
var dataItem = data[i];
BaseColor color = (pageRowIndex++ % 2 == 0)
? BaseColor.LIGHT_GRAY : BaseColor.WHITE;
AddRow(dataItem, type, displayColumns, color, reportTable.dataTable);
actualHeight = actualHeight + reportTable.dataTable.GetRowHeight(i);
var lastRowReached = i == data.Count - 1;
if ((actualHeight > reportTable.height) || lastRowReached)
{
reportTable.pageSplitter.Add(new Tuple<int, int>(srartRow,
lastRowReached ? -1 : i));
if (!lastRowReached)
{
reportTable.dataTable.DeleteLastRow();
AddRow(dataItem, type, displayColumns, BaseColor.LIGHT_GRAY,
reportTable.dataTable);
pageRowIndex = 1;
}
actualHeight = headerHeight + reportTable.dataTable.GetRowHeight(i);
srartRow = i;
}
}
return reportTable;
}
public void RenderTable(Document document, PdfWriter writer)
{
float left = (document.PageSize.Width - headerTable.TotalWidth)/2;
var pageCount = pageSplitter.Count;
float headerHeight = headerTable.GetRowHeight(0);
for (int i = 0; i < pageCount; i++)
{
var rownumbers = pageSplitter[i];
headerTable.WriteSelectedRows(0, 1, left, top, writer.DirectContent);
dataTable.WriteSelectedRows(rownumbers.Item1, rownumbers.Item2,
left, top - headerHeight, writer.DirectContent);
if (i != pageCount - 1)
{
document.NewPage();
}
}
}
}
public class PageEventHelper: PdfPageEventHelper
{
public PdfTabularReport Report { get; set; }
private void AddHeader(Document document, PdfWriter writer)
{
if (Report.LogoImage != null)
{
document.Add(Report.LogoImage);
}
if (Report.Title != null)
{
Report.Title.WriteSelectedRows(0, -1, document.LeftMargin,
document.PageSize.Height - document.TopMargin, writer.DirectContent);
}
Report.PageNumberLabel.DeleteLastRow();
var cell = new PdfPCell(new Phrase("Page " + document.PageNumber.ToString()
+ " of "
+ Report.PageCount.ToString(), new Font(ReportFonts.Helvetica, 8)));
cell.Border = 0;
cell.HorizontalAlignment = Element.ALIGN_RIGHT;
Report.PageNumberLabel.AddCell(cell);
var cellHeight = Report.PageNumberLabel.GetRowHeight(0);
Report.PageNumberLabel.WriteSelectedRows(0, -1, document.LeftMargin,
document.PageSize.Height - document.TopMargin - Report.HeaderSectionHeight
+ cellHeight, writer.DirectContent);
}
public override void OnStartPage(PdfWriter writer, Document document)
{
base.OnStartPage(writer, document);
AddHeader(document, writer);
}
}
}
The "PdfTabularReport.cs" file implements four classes:
- The "
PdfTabularReport
" class is the main classes that we will be using to generate PDF documents. - The "
ReportFonts
" defines the fonts used in the PDF documents. - The "
ReportTable
" class is a helper classes used by the "PdfTabularReport
" class to render the tabular data. - The "
PageEventHelper
" class extends the "PdfPageEventHelper" class. It renders the logo image, the report title, and the page numbers on each of the PDF pages in the document.
In the remaining part of this article, I will be showing you how to use these utility classes to export your tabular data.
The Data Model
The data model in this application is implemented in the "Models\StudentRepository.cs" file:
using System;
using System.Collections.Generic;
using System.Data;
namespace iTextTabularReport.Models
{
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Enrollment { get; set; }
public int Score { get; set; }
}
public static class StudentRepository
{
public static List<Student> GetStudentList(int NoOfStudents)
{
var students = new List<Student>();
var rand = new Random();
for (int i = 1; i <= NoOfStudents; i++)
{
var student = new Student();
student.Id = i;
student.Name = "Student Name No." + i.ToString();
student.Enrollment = DateTime.Now;
student.Score = 60 + (int)(rand.NextDouble() * 40);
students.Add(student);
}
return students;
}
public static DataTable GetStudentsTable(int NoOfStudents)
{
var students = new DataTable();
students.Columns.Add(new DataColumn("ID", Type.GetType("System.Int32")));
students.Columns.Add(new DataColumn("Name", Type.GetType("System.String")));
students.Columns.Add(new DataColumn("Enrollment",
Type.GetType("System.DateTime")));
students.Columns.Add(new DataColumn("Score", Type.GetType("System.Int32")));
var rand = new Random();
for (int i = 1; i <= NoOfStudents; i++)
{
Object[] data = new Object[4];
data[0] = i;
data[1] = "Student Name No." + i.ToString();
data[2] = DateTime.Now;
data[3] = 60 + (int)(rand.NextDouble() * 40);
students.Rows.Add(data);
}
return students;
}
}
}
The "HomeController.cs" implements two classes:
- The "
Student
" class defines a student. - The "
StudentRepository
" implements two static
methods to return the information for a group of students. One of the methods returns the students as a "List<Student>
", the other returns the students
as a "DataTable".
The example application will be using the data from the "StudentRepository
" class to render a tabular PDF document and send it to the web browsers.
The "MVC" Controller
The "MVC" controller is implemented in the "Controllers\HomeController.cs" file:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using iTextSharp.text;
using iTextTabularReport.Utilities;
using iTextTabularReport.Models;
namespace iTextTabularReport.Controllers
{
[HandleError]
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return new RedirectResult("~/Default.htm");
}
[HttpGet]
public void GetPdf()
{
var configuration = new ReportConfiguration();
configuration.PageOrientation = PageSize.LETTER_LANDSCAPE.Rotate();
configuration.LogoPath
= Server.MapPath(Url.Content("~/Content/Images/Logo.jpg"));
configuration.LogImageScalePercent = 50;
configuration.ReportTitle
= "Export Tabular Data in Pdf Format through the Web";
configuration.ReportSubTitle = "Created by iText Report Tool";
var report = new PdfTabularReport();
report.ReportConfiguration = configuration;
List<ReportColumn> columns = new List<ReportColumn>();
columns.Add(new ReportColumn { ColumnName = "Id", Width = 100 });
columns.Add(new ReportColumn { ColumnName = "Name", Width = 100 });
columns.Add(new ReportColumn { ColumnName = "Enrollment", Width = 100 });
columns.Add(new ReportColumn { ColumnName = "Score", Width = 100 });
var stream = report.GetPdf(StudentRepository.GetStudentsTable(1000), columns);
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition",
"attachment;filename=ExampleReport.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.BinaryWrite(stream.ToArray());
Response.End();
}
}
}
To generate the PDF document in this example using the utility classes, we will need to go through the following steps:
- Create a "
List<Student>
" object or a "DataTable
" that contains the same information. In this article, I used a "DataTable
" to supply the data to the utility class "PdfTabularReport
". - Create a "
ReportConfiguration
" object and apply the desired configuration to the PDF document. - Create a "
List<ReportColumn>
" to configure each column in the tabular data. If any property in the "Student
" object or any column in the "DataTable
" is not included in this "List
", the corresponding column is not displayed in the PDF document. The "Width
" property in the "ReportColumn
" class is the relative width of the column. The total width of the table always covers the total width of the document. - The "
GetPdf
" method in the "PdfTabularReport
" returns the generated PDF document as a "MemoryStream
".
When we get the PDF "MemoryStream
", we can either save it to a file or send it to the web browser. In this example, I simply passed it to the web browser.
The "Default.htm" Page
The "Default.htm" page is implemented as the following:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>iText Tabular Report</title>
<link href="Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div>
<a href="Home/GetPdf" class="button">Get Pdf Report</a>
</div>
</body>
</html>
This simple "html
" page has only one "hyperlink" to the "MVC" action method "GetPdf
". Clicking on it will trigger the generation and download of the dynamically generated "PDF" document.
Run the Application
We now finish the development of this example application. Let us test run it.
When the application starts, the "Default.htm" file is loaded. The CSS style in the application makes the "hyperlink" look like a button. Click on the "Get Pdf Report" button, the PDF document is generated and downloaded to the web browser. The following picture shows the No.6th page of the document.
You can see that the logo image, the title, the page number, the total number of the pages, and the column headers of the data are shown on each of the pages in the PDF document.
Points of Interest
- This article presented an example to export tabular data in PDF format through the web.
- Although this example only shows you how to send PDF documents to the web browsers, you do not have any problem to use the utility classes to create PDF files and save them to your hard drives.
- You can prepare your data in either "
List<Object>
" or "DataTable
" format. The utility classes in this example can handle both formats. - I will use this article to show my memory and my respect to "Dennis Ritchie", one of the greatest that our planet ever had.
- I hope you like my postings and I hope this article can help you one way or the other.
History
- First revision - 10/13/2011