65.9K
CodeProject 正在变化。 阅读更多。
Home

通过 Web 导出表格数据为 PDF 格式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (12投票s)

2011 年 10 月 13 日

CPOL

5分钟阅读

viewsIcon

48871

downloadIcon

1284

本文提供了一个通过 Web 以 PDF 格式导出表格数据的示例。

引言

本文提供了一个通过 Web 以 PDF 格式导出表格数据的示例。

背景

有时,您可能希望以 PDF 格式导出一些数据。本文将通过 Web,使用“iTextSharp”提供一个以 PDF 格式导出表格数据的示例。该示例将使用一个“MVC”控制器来导出生成的 PDF 文件,因此我将假定读者对“ASP.NET MVC”有一定的基础知识。

SolutionExplorer.jpg

附件是使用 Visual Studio 2010 开发的一个“MVC”应用程序。此示例使用“iTextSharp”创建 PDF 文档。您可以从此处下载“iTextSharp”。

  • Utilities”文件夹中的“PdfTabularReport.cs”和“ReportConfiguration.cs”文件实现了用于生成 PDF 文档的一些实用类。
  • Models”文件夹中的“StudentRepository.cs”文件是应用程序的数据模型。
  • Controllers”文件夹中的“HomeController.cs”文件是“MVC”应用程序的控制器。其主要功能是调用 PDF 实用类来创建 PDF 文档并将其发送到 Web 浏览器。
  • Default.htm”是用户可以下载 PDF 文档的网页。

我将首先介绍这些实用类,然后向您展示如何使用这些类来创建 PDF 文档以及如何将它们发送到 Web 浏览器。

实用类

实用类实现在两个文件中。以下是“ReportConfiguration.cs”文件。

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; }
    }
}

此文件中实现了两个类。这两个类由“PdfTabularReport.cs”文件中实现的类用于配置 PDF 文档。

  • ReportColumn”类定义了表格数据的列的宽度和标题文本。
  • ReportConfiguration”类定义了 PDF 文档的通用布局。

PdfTabularReport.cs”文件的实现如下:

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);
        }
    }
}

PdfTabularReport.cs”文件实现了四个类:

  • PdfTabularReport”类是我们用于生成 PDF 文档的主要类。
  • ReportFonts”定义了 PDF 文档中使用的字体。
  • ReportTable”类是“PdfTabularReport”类用于渲染表格数据的一个辅助类。
  • PageEventHelper”类扩展了“PdfPageEventHelper”类。它在文档的每个 PDF 页面上渲染 Logo 图片、报表标题和页码。

在本文的其余部分,我将向您展示如何使用这些实用类来导出您的表格数据。

数据模型

此应用程序中的数据模型实现在“Models\StudentRepository.cs”文件中。

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;
        }
    }
}

HomeController.cs”实现了两个类:

  • Student”类定义了一个学生。
  • StudentRepository”实现了两个 static 方法来返回一组学生的信息。其中一个方法以“List<Student>”形式返回学生,另一个方法以“DataTable”形式返回学生。

示例应用程序将使用“StudentRepository”类的数据来渲染表格 PDF 文档并将其发送到 Web 浏览器。

MVC 控制器

“MVC”控制器实现在“Controllers\HomeController.cs”文件中。

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();
        }
    }
}

要在此示例中使用实用类生成 PDF 文档,我们需要执行以下步骤:

  • 创建一个包含相同信息的“List<Student>”对象或“DataTable”。在本文中,我使用“DataTable”为实用类“PdfTabularReport”提供数据。
  • 创建一个“ReportConfiguration”对象,并为 PDF 文档应用所需的配置。
  • 创建一个“List<ReportColumn>”来配置表格数据中的每一列。如果“Student”对象中的任何属性或“DataTable”中的任何列未包含在此“List”中,则相应的列不会在 PDF 文档中显示。“ReportColumn”类中的“Width”属性是列的相对宽度。表格的总宽度始终覆盖文档的总宽度。
  • PdfTabularReport”中的“GetPdf”方法返回生成的 PDF 文档,形式为“MemoryStream”。

当获取到 PDF “MemoryStream”后,我们可以将其保存到文件或发送到 Web 浏览器。在此示例中,我只是将其发送到了 Web 浏览器。

“Default.htm”页面

Default.htm”页面的实现如下:

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

这个简单的“html”页面只有一个指向“MVC”操作方法“GetPdf”的“超链接”。单击它将触发动态生成的“PDF”文档的生成和下载。

运行应用程序

我们现在完成了此示例应用程序的开发。让我们对其进行测试。

RunApplication.jpg

应用程序启动时,会加载“Default.htm”文件。应用程序中的 CSS 样式使“超链接”看起来像一个按钮。单击“Get Pdf Report”按钮,PDF 文档将生成并下载到 Web 浏览器。下图显示了文档的第 6 页。

PdfDocument.jpg

您可以看到,Logo 图片、标题、页码、总页数和数据列标题都显示在 PDF 文档的每一页上。

关注点

  • 本文提供了一个通过 Web 以 PDF 格式导出表格数据的示例。
  • 虽然此示例仅向您展示了如何将 PDF 文档发送到 Web 浏览器,但您也可以毫无问题地使用这些实用类来创建 PDF 文件并将其保存到您的硬盘驱动器。
  • 您可以准备您的数据,格式为“List<Object>”或“DataTable”。本示例中的实用类可以处理这两种格式。
  • 我将通过本文来纪念并致敬“Dennis Ritchie”,他是我辈所见过最伟大的人之一。
  • 我希望您喜欢我的文章,希望本文能以某种方式帮助您。

历史

  • 首次修订 - 2011/10/13
© . All rights reserved.