C# 实践教程:在 PDF 文档中生成与美化表格的完整指南

C#在PDF中创建表格

PDF文档因其跨平台、内容固定的特性,成为企业级应用中不可或缺的报告、发票、合同等输出格式。对于C#开发者而言,在处理这些文档时,将结构化数据以清晰、专业、美观的表格形式呈现,是常见的需求。然而,手动计算布局、绘制线条,或使用一些功能不完善的库来构建表格,往往耗时耗力,效率低下,且难以满足复杂的定制需求。

本文将深入探讨如何利用C#和一款强大的第三方库——Spire.PDF for .NET,高效、灵活地在PDF文档中创建美观且功能丰富的表格。无论你是需要一个简单的静态表格,还是需要一个能动态填充数据、支持复杂布局的报表,本文都将为你提供全面的解决方案,助你从零开始,掌握在PDF中构建表格的各项技能。

1. 轻松集成:为你的C#项目引入PDF处理库

在C#项目中处理PDF文档,通常需要借助第三方库。Spire.PDF for .NET是E-iceblue公司开发的一款功能强大、易于使用的PDF组件,它提供了丰富的API,支持PDF的创建、编辑、转换、打印等多种操作。对于表格创建而言,Spire.PDF提供了直观的对象模型,极大地简化了开发难度。

集成步骤:

在你的C#项目中,通过NuGet包管理器安装Spire.PDF库。打开Visual Studio,右键点击项目,选择“管理NuGet程序包”,搜索“Spire.PDF”,然后点击安装。

// 通过NuGet安装Spire.PDF后,你的项目会自动添加相应的引用。
// 你可以在代码中通过以下using语句引入必要的命名空间:
using Spire.Pdf;
using Spire.Pdf.Graphics;
using Spire.Pdf.Tables;
using System.Drawing;
using System.Data; // 用于动态数据表格
using System.Collections.Generic; // 用于动态数据表格

2. 从零开始:使用C#创建PDF中的简单表格

创建PDF文档中的表格,首先需要创建一个PDF文档实例,添加页面,然后定义表格对象,并设置其基本属性。

以下是一个创建包含少量静态数据的简单表格的示例:

using Spire.Pdf;
using Spire.Pdf.Graphics;
using Spire.Pdf.Tables;
using System.Drawing;

public class SimplePdfTable
{
    public static void Create()
    {
        // 1. 创建Pdf文档实例
        PdfDocument doc = new PdfDocument();
        // 2. 添加页面
        PdfPageBase page = doc.Pages.Add();

        // 3. 定义表格数据
        string[] header = { "产品ID", "产品名称", "单价", "数量" };
        string[][] data =
        {
            new string[] { "P001", "笔记本电脑", "8999.00", "1" },
            new string[] { "P002", "无线鼠标", "129.00", "2" },
            new string[] { "P003", "机械键盘", "459.00", "1" }
        };

        // 4. 创建PdfTable对象
        PdfTable table = new PdfTable();
        table.Style.CellPadding = 5; // 设置单元格内边距
        table.Style.BorderPen = new PdfPen(Color.Black, 0.75f); // 设置边框样式

        // 设置表头样式
        table.Style.HeaderSource = PdfHeaderSource.Rows;
        table.Style.HeaderRowCount = 1;
        table.Style.ShowHeader = true;
        table.Style.HeaderStyle.BackgroundBrush = PdfBrushes.CadetBlue;
        table.Style.HeaderStyle.Font = new PdfTrueTypeFont(new Font("Arial", 12f, FontStyle.Bold));
        table.Style.HeaderStyle.StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);

        // 设置默认单元格样式
        table.Style.DefaultStyle.Font = new PdfTrueTypeFont(new Font("Arial", 10f));
        table.Style.DefaultStyle.StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);

        // 5. 将数据填充到表格
        table.DataSource = data;

        // 6. 设置表格的列宽 (可选,不设置则自动调整)
        table.Columns[0].Width = 80;
        table.Columns[1].Width = 150;
        table.Columns[2].Width = 100;
        table.Columns[3].Width = 80;

        // 7. 绘制表格到页面
        PdfLayoutResult result = table.Draw(page, new PointF(50, 50)); // 在页面坐标(50, 50)处绘制表格

        // 8. 保存文档
        doc.SaveToFile("SimpleTable.pdf");
        doc.Close();
    }
}

代码解释:

  • PdfDocumentPdfPageBase:PDF文档和页面的基本对象。
  • PdfTable:表示PDF中的表格。
  • table.Style.CellPadding:控制单元格内容的内边距。
  • table.Style.BorderPen:设置表格及单元格的边框颜色和粗细。
  • table.Style.HeaderSourcetable.Style.HeaderRowCount:指定表格数据的第一行作为表头。
  • table.Style.HeaderStyle:设置表头的背景、字体和文本对齐方式。
  • table.Style.DefaultStyle:设置表格中普通单元格的默认样式。
  • table.DataSource:将二维字符串数组 data 绑定到表格。Spire.PDF会自动根据数据源创建行和列。
  • table.Columns[index].Width:设置特定列的宽度。
  • table.Draw(page, new PointF(x, y)):将表格绘制到指定的页面和坐标。

3. 灵活掌控:实现复杂表格布局与动态数据填充

在实际应用中,表格往往需要更复杂的样式定制,例如合并单元格、调整行高列宽,以及从数据库或数据集合中动态填充数据。

3.1 自定义单元格样式与合并单元格

using Spire.Pdf;
using Spire.Pdf.Graphics;
using Spire.Pdf.Tables;
using System.Drawing;
using System.Data;

public class AdvancedPdfTable
{
    public static void Create()
    {
        PdfDocument doc = new PdfDocument();
        PdfPageBase page = doc.Pages.Add();

        PdfTable table = new PdfTable();
        table.Style.CellPadding = 5;
        table.Style.BorderPen = new PdfPen(Color.Gray, 0.5f);

        // 定义表头
        table.Columns.Add(new PdfColumn("ID"));
        table.Columns.Add(new PdfColumn("项目"));
        table.Columns.Add(new PdfColumn("描述"));
        table.Columns.Add(new PdfColumn("状态"));

        // 添加数据行
        PdfTableRow row1 = table.Rows.Add();
        row1.Cells[0].Value = "1";
        row1.Cells[1].Value = "任务A";
        row1.Cells[2].Value = "完成需求分析";
        row1.Cells[3].Value = "已完成";

        PdfTableRow row2 = table.Rows.Add();
        row2.Cells[0].Value = "2";
        row2.Cells[1].Value = "任务B";
        row2.Cells[2].Value = "开发模块X";
        row2.Cells[3].Value = "进行中";
        // 设置特定单元格背景色和字体
        row2.Cells[3].Style.BackgroundBrush = PdfBrushes.LightYellow;
        row2.Cells[3].Style.Font = new PdfTrueTypeFont(new Font("Arial", 10f, FontStyle.Italic));

        PdfTableRow row3 = table.Rows.Add();
        row3.Cells[0].Value = "3";
        row3.Cells[1].Value = "任务C";
        row3.Cells[2].Value = "测试功能Y";
        row3.Cells[3].Value = "待开始";

        // 合并单元格示例 (合并第4行,第0列和第1列)
        PdfTableRow row4 = table.Rows.Add();
        row4.Cells[0].Value = "总计:";
        row4.Cells[0].ColumnSpan = 2; // 合并当前单元格及其右侧一个单元格
        row4.Cells[2].Value = "3个任务";
        row4.Cells[3].Value = ""; // 可以留空或设置其他内容
        row4.Height = 25; // 设置行高
        row4.Cells[0].Style.BackgroundBrush = PdfBrushes.LightGray;
        row4.Cells[0].Style.Font = new PdfTrueTypeFont(new Font("Arial", 11f, FontStyle.Bold));
        row4.Cells[0].Style.StringFormat = new PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Middle);


        // 设置表头样式
        table.Style.HeaderSource = PdfHeaderSource.Rows;
        table.Style.HeaderRowCount = 1; // 第一行是表头
        table.Style.HeaderStyle.BackgroundBrush = PdfBrushes.DarkSlateBlue;
        table.Style.HeaderStyle.TextBrush = PdfBrushes.White;
        table.Style.HeaderStyle.Font = new PdfTrueTypeFont(new Font("Arial", 11f, FontStyle.Bold));
        table.Style.HeaderStyle.StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);

        PdfLayoutResult result = table.Draw(page, new PointF(50, 50));
        doc.SaveToFile("AdvancedTable.pdf");
        doc.Close();
    }
}

代码解释:

  • 通过 table.Columns.Add() 逐个添加列,并指定列名。
  • 通过 table.Rows.Add() 添加行,并通过 row.Cells[index].Value 填充单元格数据。
  • row.Cells[index].Style:可以针对单个单元格设置独立的样式,覆盖表格的默认样式。
  • row.Cells[index].ColumnSpan:实现单元格横向合并,值为需要合并的列数。
  • row.Height:设置特定行的高度。

3.2 动态数据绑定 (以DataTable为例)

在实际业务中,表格数据通常来源于数据库查询或内存中的数据集合。Spire.PDF支持直接绑定 DataTableIEnumerable<T> 类型的数据源。

假设我们有一个 DataTable 存储了订单信息:

using Spire.Pdf;
using Spire.Pdf.Graphics;
using Spire.Pdf.Tables;
using System.Drawing;
using System.Data;
using System.Collections.Generic;

public class DynamicPdfTable
{
    // 模拟数据源
    private static DataTable GetOrderData()
    {
        DataTable dt = new DataTable("Orders");
        dt.Columns.Add("OrderId", typeof(int));
        dt.Columns.Add("CustomerName", typeof(string));
        dt.Columns.Add("OrderDate", typeof(DateTime));
        dt.Columns.Add("Amount", typeof(decimal));

        dt.Rows.Add(1001, "张三", DateTime.Now.AddDays(-5), 1250.50m);
        dt.Rows.Add(1002, "李四", DateTime.Now.AddDays(-2), 899.00m);
        dt.Rows.Add(1003, "王五", DateTime.Now.AddDays(-1), 2300.75m);
        dt.Rows.Add(1004, "赵六", DateTime.Now, 500.00m);
        return dt;
    }

    public static void Create()
    {
        PdfDocument doc = new PdfDocument();
        PdfPageBase page = doc.Pages.Add();

        PdfTable table = new PdfTable();
        table.Style.CellPadding = 3;
        table.Style.BorderPen = new PdfPen(Color.LightGray, 0.5f);

        // 设置表头样式
        table.Style.HeaderSource = PdfHeaderSource.Rows;
        table.Style.HeaderRowCount = 1;
        table.Style.HeaderStyle.BackgroundBrush = PdfBrushes.DarkGreen;
        table.Style.HeaderStyle.TextBrush = PdfBrushes.White;
        table.Style.HeaderStyle.Font = new PdfTrueTypeFont(new Font("Microsoft YaHei", 10f, FontStyle.Bold));
        table.Style.HeaderStyle.StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);

        // 设置交替行样式
        table.Style.AlternateStyle = new PdfCellStyle();
        table.Style.AlternateStyle.BackgroundBrush = PdfBrushes.LightCyan;

        // 绑定DataTable数据源
        table.DataSource = GetOrderData();

        // 调整列宽
        table.Columns[0].Width = 60; // OrderId
        table.Columns[1].Width = 120; // CustomerName
        table.Columns[2].Width = 100; // OrderDate
        table.Columns[3].Width = 80; // Amount

        // 设置特定列的文本对齐方式
        table.Columns[0].StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);
        table.Columns[3].StringFormat = new PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Middle);

        PdfLayoutResult result = table.Draw(page, new PointF(50, 50));
        doc.SaveToFile("DynamicTable.pdf");
        doc.Close();
    }
}

代码解释:

  • GetOrderData():模拟从数据库获取数据并填充到 DataTable
  • table.DataSource = GetOrderData():直接将 DataTable 实例赋值给 DataSource 属性。Spire.PDF会自动读取 DataTable 的列名作为表头,并填充所有数据行。
  • table.Style.AlternateStyle:设置交替行的样式,可以使表格更具可读性。
  • table.Columns[index].StringFormat:可以为整列设置统一的文本对齐方式。

4. 精益求精:优化表格呈现与文档保存

表格绘制完成后,我们还需要考虑其在页面上的位置、可能的跨页显示以及最终的文档保存。

using Spire.Pdf;
using Spire.Pdf.Graphics;
using Spire.Pdf.Tables;
using System.Drawing;
using System.Data;

public class OptimizedPdfTable
{
    // ... (GetOrderData 方法同上) ...

    public static void Create()
    {
        PdfDocument doc = new PdfDocument();
        PdfPageBase page = doc.Pages.Add();

        PdfTable table = new PdfTable();
        table.Style.CellPadding = 3;
        table.Style.BorderPen = new PdfPen(Color.LightGray, 0.5f);
        table.Style.HeaderSource = PdfHeaderSource.Rows;
        table.Style.HeaderRowCount = 1;
        table.Style.HeaderStyle.BackgroundBrush = PdfBrushes.DarkGreen;
        table.Style.HeaderStyle.TextBrush = PdfBrushes.White;
        table.Style.HeaderStyle.Font = new PdfTrueTypeFont(new Font("Microsoft YaHei", 10f, FontStyle.Bold));
        table.Style.HeaderStyle.StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);
        table.Style.AlternateStyle = new PdfCellStyle();
        table.Style.AlternateStyle.BackgroundBrush = PdfBrushes.LightCyan;

        // 模拟大量数据,以便观察跨页效果
        DataTable largeData = GetOrderData();
        for (int i = 0; i < 50; i++) // 增加数据量
        {
            largeData.Rows.Add(1005 + i, "客户" + (i + 7), DateTime.Now.AddDays(-i), 100.00m + i * 5);
        }
        table.DataSource = largeData;

        // 设置列宽
        table.Columns[0].Width = 60;
        table.Columns[1].Width = 120;
        table.Columns[2].Width = 100;
        table.Columns[3].Width = 80;

        // **重要:处理表格跨页**
        // Spire.PDF的Draw方法会自动处理表格的跨页,如果表格内容超出当前页面,
        // 它会自动在下一页继续绘制,并返回PdfLayoutResult指示绘制结果。
        // 如果需要重复表头,可以设置 table.Style.RepeatHeader = true;
        table.Style.RepeatHeader = true; // 跨页时重复表头

        // 定义表格绘制的区域和布局方式
        PdfLayoutFormat format = new PdfLayoutFormat();
        format.Break = PdfLayoutBreakType.FitPage; // 尽量在一页内显示,超出则分页
        format.Layout = PdfLayoutType.Paginate;   // 自动分页

        // 绘制表格到页面,从指定位置开始
        // 注意:Draw方法会返回一个PdfLayoutResult对象,其中包含表格绘制后的最后一个位置,
        // 方便后续内容接着表格绘制。
        PdfLayoutResult result = table.Draw(page, new PointF(50, 50), format);

        // 如果表格绘制到了新页面,result.Page 会是新的页面对象
        // 可以在这里添加一些页面底部或新页面的内容
        // 例如:
        // if (result.Page != page)
        // {
        //     result.Page.Canvas.DrawString("(续上页)", new PdfTrueTypeFont(new Font("Arial", 8)), PdfBrushes.Gray, 50, 20);
        // }


        // 保存文档
        doc.SaveToFile("OptimizedTable.pdf");
        doc.Close();
    }

    private static DataTable GetOrderData()
    {
        DataTable dt = new DataTable("Orders");
        dt.Columns.Add("OrderId", typeof(int));
        dt.Columns.Add("CustomerName", typeof(string));
        dt.Columns.Add("OrderDate", typeof(DateTime));
        dt.Columns.Add("Amount", typeof(decimal));

        dt.Rows.Add(1001, "张三", DateTime.Now.AddDays(-5), 1250.50m);
        dt.Rows.Add(1002, "李四", DateTime.Now.AddDays(-2), 899.00m);
        dt.Rows.Add(1003, "王五", DateTime.Now.AddDays(-1), 2300.75m);
        dt.Rows.Add(1004, "赵六", DateTime.Now, 500.00m);
        return dt;
    }
}

代码解释:

  • table.Style.RepeatHeader = true;:这是处理表格跨页时非常关键的设置,它确保在表格内容延续到新页面时,表头能够在新页面的顶部重复显示,提高了报告的可读性。
  • PdfLayoutFormat:这个对象允许你更精细地控制表格的布局行为。
    • format.Break = PdfLayoutBreakType.FitPage;:指示Spire.PDF在表格内容超出当前页面时,在页面底部自动断开,并在下一页继续。
    • format.Layout = PdfLayoutType.Paginate;:启用自动分页功能。
  • table.Draw(page, new PointF(50, 50), format);:将 PdfLayoutFormat 对象传递给 Draw 方法,使表格按照定义的布局规则进行绘制。PdfLayoutResult 包含了绘制后的信息,例如表格结束的位置和绘制到的最后一页。

总结

通过本文的详细讲解和代码示例,你已经掌握了使用C#和Spire.PDF for .NET库在PDF文档中创建各种类型表格的核心技术。从简单的静态表格到复杂的动态数据表格,再到精细的单元格样式定制和跨页处理,Spire.PDF都提供了强大且直观的API支持。

C#结合专业PDF处理库,为开发者在处理PDF文档时带来了前所未有的高效与灵活性。我鼓励你根据本文提供的示例,进一步探索Spire.PDF的更多功能,例如添加页眉页脚、图片、条形码等,并将其灵活应用于你的实际项目中,为你的应用程序带来更专业、更强大的PDF文档处理能力。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容