Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / Office

Export Tabular Data in PDF Format through the Web

4.57/5 (12 votes)
13 Oct 2011CPOL5 min read 48.1K   1.3K  
This article presents an example to export tabular data in PDF format through the web.

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".

SolutionExplorer.jpg

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.

C#
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
    {
        // Overall page layout, orientation and margins
        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; } }
 
        // Logo - Logo is always placed at the top left corner
        private int logImageScalePercent = 100;
        public string LogoPath { get; set; }
        public int LogImageScalePercent { get { return logImageScalePercent; }
            set { logImageScalePercent = value; } }
 
        // Title and subtitle. Titles are always center aligned at the top.
        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:

C#
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
{
    // The main class used to generate Pdf tabular report.
    public class PdfTabularReport
    {
        // Configurations
        public ReportConfiguration ReportConfiguration { get; set; }
 
        // Internal properties, not set outside of the class
        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; }
 
        // Private instance variables
        Document PdfDocument = null;
        PdfWriter PdfWriter = null;
        MemoryStream PdfStream = null;
 
        // Constructor
        public PdfTabularReport(ReportConfiguration configuration = null)
        {
            // If configuration is not provided, the default will be used
            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);
 
            // create logo, header and page number objects
            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);
                }
 
                // Get the height of the title section
                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;
        }
 
        // Method to get the pdf stream
        public MemoryStream GetPdf<T>(List<T> data, List<ReportColumn> displayColumns)
        {
            InitiateDocument();
 
            // Add the report data
            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);
        }
 
        // Overloaded method to get the pdf stream. It takes that data as DataTable
        public MemoryStream GetPdf(DataTable data, List<ReportColumn> displayColumns)
        {
            List<DataRow> list = data.AsEnumerable().ToList();
            return GetPdf<DataRow>(list, displayColumns);
        }
    }
 
    // Fonts used by the tabular report
    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); } }
    }
 
    // Utility class to render the Pdf table to the report document
    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 constructor. The instances need to use "CreateReportTable"
        // method to create.
        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);
            }
        }
 
        // Static method to create & return an instance object
        public static ReportTable CreateReportTable<T>(List<T> data,
            List<ReportColumn> displayColumns, float top, Document document)
        {
            // Construct an instance object
            var reportTable = new ReportTable(displayColumns, document, top);
            var type = typeof(T);
 
            // Add each data item into the PdfPTable.
            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;
        }
 
        // Render the table to the Pdf document.
        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();
                }
            }
        }
    }
 
    // PdfPageEventHelper: logo, title, sub-title, and page numbers.
    public class PageEventHelper: PdfPageEventHelper
    {
        public PdfTabularReport Report { get; set; }
 
        private void AddHeader(Document document, PdfWriter writer)
        {
            // Add logo
            if (Report.LogoImage != null)
            {
                document.Add(Report.LogoImage);
            }
 
            // Add titles
            if (Report.Title != null)
            {
                Report.Title.WriteSelectedRows(0, -1, document.LeftMargin,
                    document.PageSize.Height - document.TopMargin, writer.DirectContent);
            }
 
            // Add page number
            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:

C#
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:

C#
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:

HTML
<!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.

RunApplication.jpg

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.

PdfDocument.jpg

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)