DEV Community

Allen Yang
Allen Yang

Posted on

Creating and Styling Tables in PDF Documents with C#

How to Create Tables in PDF with C#

In today's data-driven world, the ability to programmatically generate well-structured and visually appealing PDF documents is an indispensable skill for many C# developers. Whether it's for financial reports, invoices, data summaries, or academic papers, PDFs provide a static, universally viewable format for presenting information. Among the various elements that populate these documents, tables stand out as crucial for organizing and displaying complex datasets clearly and concisely.

However, anyone who has delved into programmatic PDF generation knows that achieving precise layout control, especially for tables, can be a significant challenge. The fixed nature of PDF documents often makes dynamic content placement and styling a painstaking process. This tutorial aims to demystify this process, guiding you through the creation of professional-looking tables in your PDF documents using C#. We'll explore a powerful library that simplifies these complexities, allowing you to focus on your data and presentation logic. By the end of this article, you'll have a solid understanding and practical examples to master PDF table creation programmatically with C#.

Setting Up Your C# Project for PDF Generation

To begin our journey into programmatic PDF generation, we first need a robust library that handles the intricacies of the PDF specification. For C# developers, Spire.PDF for .NET offers a comprehensive and intuitive API for working with PDF documents. This library allows you to create, read, edit, and convert PDFs with relative ease.

The quickest way to integrate Spire.PDF for .NET into your project is through NuGet Package Manager.

  1. Open your C# project in Visual Studio.
  2. Right-click on your project in the Solution Explorer and select "Manage NuGet Packages...".
  3. Go to the "Browse" tab and search for Spire.PDF.
  4. Install the Spire.PDF package.

Once installed, you can start by instantiating a new PDF document and adding a page to it, which will serve as our canvas for the table.

using Spire.Pdf;
using Spire.Pdf.Tables;
using Spire.Pdf.Graphics;
using System.Drawing; // For Color and PointF
using System.Data;    // For DataTable, if used

public class PdfTableGenerator
{
    public static void GenerateBasicPdfWithTable()
    {
        // Initialize a new PdfDocument instance
        PdfDocument doc = new PdfDocument();

        // Add a new page to the document. By default, it uses A4 size.
        // You can specify PdfPageSize.A4, PdfPageOrientation.Landscape, etc.
        PdfPageBase page = doc.Pages.Add();

        // At this point, the document and page are ready.
        // We'll add table creation logic here.

        // For demonstration, we'll save it at the end of the full example.
        // doc.SaveToFile("Output/MyFirstPdfTable.pdf");
        // doc.Close(); // Always remember to close the document
    }
}
Enter fullscreen mode Exit fullscreen mode

This initial setup ensures your project is ready to leverage the C# PDF library for all subsequent steps in create PDF table operations.

Building Your First Basic PDF Table

Creating a basic table involves defining its structure, providing data, and then drawing it onto a PDF page. Spire.PDF simplifies this by providing the PdfTable class, which is central to table manipulation.

Here's a step-by-step guide to generating a simple table:

  1. Prepare Table Data: The table data can be supplied in various formats, such as a string[][] (jagged array of strings) or a DataTable. Using a DataTable is particularly useful when dealing with data retrieved from databases or other structured sources, offering more flexibility.
  2. Create PdfTable Instance: Instantiate the PdfTable class.
  3. Assign Data Source: Set the DataSource property of the PdfTable to your prepared data.
  4. Define Column Widths: Control the layout by specifying the width for each column. This can be done absolutely (in points) or relatively.
  5. Draw the Table: Use the Draw method of the PdfTable instance, providing the target page and the starting PointF coordinates.

Let's put this into practice with a retail product list as our example:

// ... (inside the GenerateBasicPdfWithTable method)

// 1. Prepare table data using a jagged array
string[][] data = new string[][]
{
    new string[] {"Product ID", "Product Name", "Quantity", "Unit Price", "Total"},
    new string[] {"P001", "Laptop Pro", "2", "$1200.00", "$2400.00"},
    new string[] {"P002", "Wireless Mouse", "5", "$25.00", "$125.00"},
    new string[] {"P003", "Mechanical Keyboard", "3", "$75.00", "$225.00"},
    new string[] {"P004", "Monitor 27-inch", "1", "$300.00", "$300.00"}
};

// 2. Create a PdfTable instance
PdfTable table = new PdfTable();

// 3. Assign the data source to the table
table.DataSource = data;

// 4. Set column widths for a balanced layout on an A4 page (approx. 595 points wide)
table.Columns[0].Width = 80;  // Product ID
table.Columns[1].Width = 140; // Product Name
table.Columns[2].Width = 70;  // Quantity
table.Columns[3].Width = 90;  // Unit Price
table.Columns[4].Width = 90;  // Total

// 5. Draw the table on the page, starting at coordinates (50, 50)
table.Draw(page, new PointF(50, 50));

// Save the document to a file (full example will include this)
// doc.SaveToFile("Output/BasicTable.pdf");
// doc.Close();
Enter fullscreen mode Exit fullscreen mode

This code snippet demonstrates the essential steps to add table to PDF documents with minimal effort, resulting in a functional, albeit unstyled, table.

Enhancing Tables with Styling and Advanced Features

A basic table is a good start, but professional PDF documents often require custom styling, proper formatting, and intelligent handling of large datasets. Spire.PDF provides extensive options to customize the appearance and behavior of your tables.

Customizing Cell Styles

The PdfTable class offers various style properties to control the look and feel of your table's header, data rows, and alternating rows. You can define fonts, colors, and text alignment.

  • table.Style.HeaderStyle: Controls the appearance of the table header.
  • table.Style.DefaultStyle: Sets the default style for all data cells.
  • table.Style.AlternateRowStyle: Applies a different style to alternate rows, improving readability.
// ... (within GenerateBasicPdfWithTable method, after table.DataSource = data;)

// Style the header row
table.Style.HeaderStyle.BackgroundBrush = PdfBrushes.DarkBlue;
table.Style.HeaderStyle.TextBrush = PdfBrushes.White;
table.Style.HeaderStyle.Font = new PdfTrueTypeFont(new Font("Arial", 10f, FontStyle.Bold), true);
table.Style.HeaderStyle.StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);

// Style data rows
table.Style.DefaultStyle.Font = new PdfTrueTypeFont(new Font("Arial", 9f), true);
table.Style.DefaultStyle.BackgroundBrush = PdfBrushes.LightSteelBlue; // Light background for default rows
table.Style.DefaultStyle.TextBrush = PdfBrushes.Black;

// Alternate row styling for better readability
table.Style.AlternateRowStyle.BackgroundBrush = PdfBrushes.LightSkyBlue;
table.Style.AlternateRowStyle.TextBrush = PdfBrushes.Black;
Enter fullscreen mode Exit fullscreen mode

Adding Borders and Backgrounds

Borders define the visual separation of cells and the table itself. You can customize the pen used for drawing borders, including color and thickness, and also set cell padding.

// ... (continuing from previous styling)

// Set table border properties
table.Style.BorderPen = new PdfPen(Color.Gray, 0.75f);
table.Style.ShowHeader = true; // Ensure header is shown with borders

// Set cell padding for better spacing
table.Style.DefaultStyle.Padding = new PdfPaddings(5, 5, 5, 5); // Left, Top, Right, Bottom
Enter fullscreen mode Exit fullscreen mode

Handling Dynamic Data and Pagination

For dynamic PDF tables that might exceed a single page, Spire.PDF offers robust pagination capabilities. The PdfTableLayoutFormat class is crucial here, allowing you to define how the table should be laid out across multiple pages. When using a DataTable as a data source, you can easily bind large datasets.

// ... (within GenerateBasicPdfWithTable method)

// To demonstrate dynamic data with DataTable:
DataTable productsTable = new DataTable();
productsTable.Columns.Add("Product ID", typeof(string));
productsTable.Columns.Add("Product Name", typeof(string));
productsTable.Columns.Add("Quantity", typeof(int));
productsTable.Columns.Add("Unit Price", typeof(decimal));
productsTable.Columns.Add("Total", typeof(decimal));

// Populate with more data for pagination
for (int i = 0; i < 50; i++) // Create 50 rows to force pagination
{
    productsTable.Rows.Add($"P{i:D3}", $"Item {i}", i % 10 + 1, (decimal)(i * 10 + 25), (decimal)((i % 10 + 1) * (i * 10 + 25)));
}
table.DataSource = productsTable; // Assign DataTable as source

// Set up layout format for pagination
PdfTableLayoutFormat tableLayout = new PdfTableLayoutFormat();
tableLayout.Break = PdfLayoutBreakType.FitElement; // Break table when it exceeds page height
tableLayout.Layout = PdfLayoutType.Paginate;       // Enable pagination

// Draw the styled table with pagination
table.Draw(page, new PointF(50, 50), tableLayout);
Enter fullscreen mode Exit fullscreen mode

This combination of styling and pagination ensures your C# PDF library solution can handle complex reporting needs, creating professional PDF document generation effortlessly.

A Complete Example and Best Practices

Let's consolidate all the concepts discussed into a complete, runnable C# example. This code will generate a PDF with a styled, paginated table.

using Spire.Pdf;
using Spire.Pdf.Tables;
using Spire.Pdf.Graphics;
using System.Drawing;
using System.Data;
using System.IO; // For file operations

public class FullPdfTableExample
{
    public static void GenerateComprehensivePdfTable()
    {
        // 1. Initialize a new PdfDocument
        PdfDocument doc = new PdfDocument();

        // 2. Add a new page to the document
        PdfPageBase page = doc.Pages.Add();

        // 3. Prepare dynamic table data using a DataTable
        DataTable productsTable = new DataTable("Products");
        productsTable.Columns.Add("Product ID", typeof(string));
        productsTable.Columns.Add("Product Name", typeof(string));
        productsTable.Columns.Add("Quantity", typeof(int));
        productsTable.Columns.Add("Unit Price", typeof(decimal));
        productsTable.Columns.Add("Total", typeof(decimal));

        // Populate with sample data (50 rows for pagination demo)
        Random rand = new Random();
        for (int i = 0; i < 50; i++)
        {
            int quantity = rand.Next(1, 10);
            decimal unitPrice = Math.Round((decimal)(rand.NextDouble() * 100 + 10), 2);
            productsTable.Rows.Add($"P{i + 1:D3}", $"Item Description {i + 1}", quantity, unitPrice, quantity * unitPrice);
        }

        // 4. Create a PdfTable instance and assign data source
        PdfTable table = new PdfTable();
        table.DataSource = productsTable;

        // 5. Set column widths
        table.Columns[0].Width = 80;
        table.Columns[1].Width = 160;
        table.Columns[2].Width = 70;
        table.Columns[3].Width = 90;
        table.Columns[4].Width = 90;

        // 6. Apply styling
        // Header Style
        table.Style.HeaderStyle.BackgroundBrush = new PdfSolidBrush(Color.FromArgb(50, 70, 100)); // Darker blue
        table.Style.HeaderStyle.TextBrush = PdfBrushes.White;
        table.Style.HeaderStyle.Font = new PdfTrueTypeFont(new Font("Segoe UI", 10f, FontStyle.Bold), true);
        table.Style.HeaderStyle.StringFormat = new PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle);

        // Default Data Row Style
        table.Style.DefaultStyle.Font = new PdfTrueTypeFont(new Font("Segoe UI", 9f), true);
        table.Style.DefaultStyle.BackgroundBrush = new PdfSolidBrush(Color.WhiteSmoke);
        table.Style.DefaultStyle.TextBrush = PdfBrushes.Black;

        // Alternate Data Row Style
        table.Style.AlternateRowStyle.BackgroundBrush = new PdfSolidBrush(Color.LightCyan); // Lighter blue for alternate rows
        table.Style.AlternateRowStyle.TextBrush = PdfBrushes.Black;

        // Table Borders and Padding
        table.Style.BorderPen = new PdfPen(Color.DimGray, 0.5f);
        table.Style.CellPadding = 5; // Uniform padding for all cells
        table.Style.ShowHeader = true; // Ensure header is visible

        // 7. Define table layout for pagination
        PdfTableLayoutFormat tableLayout = new PdfTableLayoutFormat();
        tableLayout.Break = PdfLayoutBreakType.FitElement;
        tableLayout.Layout = PdfLayoutType.Paginate;
        tableLayout.RepeatHeader = true; // Repeat header on new pages

        // 8. Draw the table onto the PDF page
        // The Draw method returns the bounds of the drawn table, useful for placing subsequent elements.
        table.Draw(page, new PointF(50, 50), tableLayout);

        // 9. Save the document
        string filePath = "Output/ComprehensiveProductReport.pdf";
        try
        {
            // Ensure the output directory exists
            Directory.CreateDirectory(Path.GetDirectoryName(filePath));
            doc.SaveToFile(filePath);
            Console.WriteLine($"PDF generated successfully at: {Path.GetFullPath(filePath)}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error saving PDF: {ex.Message}");
        }
        finally
        {
            // 10. Close the document to release resources
            doc.Close();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Best Practices:

  • Resource Management: Always use using statements or explicitly call Close() on PdfDocument objects to ensure all resources are properly released, especially when dealing with files.
  • Error Handling: Implement try-catch blocks around file operations (SaveToFile) to gracefully handle potential issues like file access errors.
  • Modularity: For complex PDF generation tasks, consider encapsulating table creation logic within separate methods or classes to improve code readability and maintainability.
  • Performance: For extremely large tables (thousands of rows across many pages), consider optimizing data loading and rendering. While Spire.PDF is efficient, pre-processing data or rendering in chunks might be beneficial in extreme cases.

Conclusion

Programmatically generating PDF documents with well-structured tables is a common requirement in many business applications. As we've demonstrated, Spire.PDF for .NET provides a powerful and intuitive set of tools for mastering table creation in PDF documents with C#. From basic table structures to advanced styling, dynamic data binding, and intelligent pagination, this library simplifies what could otherwise be a complex task.

By following the steps and examples provided in this tutorial, you should now be equipped to create PDF tables that are both informative and visually appealing. We encourage you to experiment further with different styling options, data sources, and layout configurations. The ability to programmatically generate professional reports and data presentations is a valuable asset, and integrating tables effectively is a cornerstone of this capability. Explore other features of the Spire.PDF library to add charts, images, and form fields, further enriching your C# PDF generation endeavors.

Top comments (0)