DEV Community

IronSoftware
IronSoftware

Posted on

Generate PDF Reports in C#

Our finance team requested daily sales reports delivered as PDF attachments. The reports pulled data from SQL Server, formatted it into tables and charts, and needed to look professional enough for executive review.

Excel exports weren't cutting it—they wanted locked-down PDFs with branding, headers, and page numbers. Here's how I automated the entire pipeline in C#.

What's the Best Approach for PDF Reports?

Generate HTML first, then convert to PDF. HTML gives you full control over layout, styling, and data presentation without wrestling with low-level PDF APIs.

using IronPdf;
// Install via NuGet: Install-Package IronPdf

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlFileAsPdf("sales-report.html");
pdf.SaveAs("sales-report.pdf");
Enter fullscreen mode Exit fullscreen mode

This code takes an HTML report and outputs a styled PDF. The HTML can come from templates, reporting tools, or dynamically generated markup.

How Do I Generate HTML from Database Data?

Pull data from SQL and inject it into an HTML template:

using IronPdf;
using System.Data.SqlClient;
using System.Text;

var connectionString = "Server=...;Database=Sales;";
var html = new StringBuilder();

html.Append("<html><body><h1>Sales Report</h1><table border='1'>");
html.Append("<tr><th>Product</th><th>Revenue</th></tr>");

using (var conn = new SqlConnection(connectionString))
{
    conn.Open();
    var cmd = new SqlCommand("SELECT Product, SUM(Revenue) as Total FROM Sales GROUP BY Product", conn);

    using (var reader = cmd.ExecuteReader())
    {
        while (reader.Read())
        {
            html.Append($"<tr><td>{reader["Product"]}</td><td>${reader["Total"]}</td></tr>");
        }
    }
}

html.Append("</table></body></html>");

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html.ToString());
pdf.SaveAs("sales-report.pdf");
Enter fullscreen mode Exit fullscreen mode

This pulls sales data from SQL and builds an HTML table dynamically. The resulting PDF looks like a proper report, not raw database output.

Can I Use Razor Templates for Reports?

Yes. Razor templates provide clean separation between data and presentation:

using IronPdf;

var model = new SalesReportModel
{
    Title = "Q4 Sales Report",
    Sales = GetSalesData() // List<SaleRecord>
};

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderRazorViewToP

df("Reports/SalesReport.cshtml", model);
pdf.SaveAs("q4-sales.pdf");
Enter fullscreen mode Exit fullscreen mode

Create SalesReport.cshtml:

@model SalesReportModel

<h1>@Model.Title</h1>
<table>
  <tr><th>Product</th><th>Revenue</th></tr>
  @foreach (var sale in Model.Sales)
  {
    <tr><td>@sale.Product</td><td>$@sale.Revenue</td></tr>
  }
</table>
Enter fullscreen mode Exit fullscreen mode

This keeps your C# code clean and lets designers work on report layouts without touching C# strings.

How Do I Add Headers and Footers?

Configure rendering options before generating the PDF:

using IronPdf;

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.TextHeader.CenterText = "Sales Report - {date}";
renderer.RenderingOptions.TextHeader.DrawDividerLine = true;

renderer.RenderingOptions.TextFooter.LeftText = "Confidential";
renderer.RenderingOptions.TextFooter.RightText = "Page {page} of {total-pages}";

var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("report.pdf");
Enter fullscreen mode Exit fullscreen mode

Placeholders like {date}, {page}, and {total-pages} auto-populate. Headers and footers appear on every page.

Can I Style Reports with CSS?

Yes. CSS controls all visual aspects:

var html = @"
<style>
  body { font-family: Arial; margin: 40px; }
  h1 { color: #003366; border-bottom: 2px solid #003366; }
  table { width: 100%; border-collapse: collapse; }
  th { background-color: #003366; color: white; padding: 10px; }
  td { padding: 8px; border: 1px solid #ddd; }
  tr:nth-child(even) { background-color: #f2f2f2; }
</style>
<h1>Monthly Revenue</h1>
<table>
  <tr><th>Month</th><th>Revenue</th></tr>
  <tr><td>January</td><td>$120,000</td></tr>
  <tr><td>February</td><td>$135,000</td></tr>
</table>";

var pdf = renderer.RenderHtmlAsPdf(html);
Enter fullscreen mode Exit fullscreen mode

This produces a professional-looking report with branded colors and zebra-striped rows.

How Do I Handle Multi-Page Reports?

Use CSS page breaks to control where content splits across pages:

<style>
  .section { page-break-after: always; }
  table { page-break-inside: avoid; }
</style>

<div class="section">
  <h2>Q1 Summary</h2>
  <p>Content for Q1...</p>
</div>

<div class="section">
  <h2>Q2 Summary</h2>
  <p>Content for Q2...</p>
</div>

<table>
  <tr><th>Product</th><th>Sales</th></tr>
  <!-- Rows won't split across pages -->
</table>
Enter fullscreen mode Exit fullscreen mode

The .section class forces page breaks. The page-break-inside: avoid on tables prevents rows from splitting mid-table.

Can I Convert Crystal Reports to PDF?

Export Crystal Reports as HTML, then convert:

  1. In Crystal Reports: File → Export → HTML 4.0
  2. Save as report.html
  3. Convert with IronPDF:
var renderer = new ChromePdfRenderer();
renderer.RenderHtmlFileAsPdf("report.html").SaveAs("report.pdf");
Enter fullscreen mode Exit fullscreen mode

This workflow lets you leverage existing Crystal Reports infrastructure while outputting PDFs for distribution.

How Do I Add Charts and Graphs?

Use a JavaScript charting library like Chart.js, then render with JavaScript enabled:

var html = @"
<html>
<head>
  <script src='https://cdn.jsdelivr.net/npm/chart.js'></script>
</head>
<body>
  <canvas id='myChart'></canvas>
  <script>
    const ctx = document.getElementById('myChart').getContext('2d');
    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: ['Jan', 'Feb', 'Mar'],
        datasets: [{
          label: 'Sales',
          data: [12000, 15000, 13000]
        }]
      }
    });
  </script>
</body>
</html>";

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.RenderDelay = 500; // Wait for chart to render

var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("chart-report.pdf");
Enter fullscreen mode Exit fullscreen mode

The RenderDelay ensures Chart.js finishes drawing before the PDF renders.

How Do I Generate Reports from XML?

Parse XML and transform it to HTML:

using System.Xml.Linq;
using System.Text;

var xml = XDocument.Load("sales-data.xml");
var html = new StringBuilder();

html.Append("<html><body><h1>Sales Report</h1><table border='1'>");

foreach (var sale in xml.Descendants("Sale"))
{
    html.Append($"<tr><td>{sale.Element("Product").Value}</td>");
    html.Append($"<td>${sale.Element("Amount").Value}</td></tr>");
}

html.Append("</table></body></html>");

var pdf = renderer.RenderHtmlAsPdf(html.ToString());
Enter fullscreen mode Exit fullscreen mode

This approach works for any XML schema. Use XSLT for more complex transformations.

Can I Schedule Automated Report Generation?

Yes. Use Windows Task Scheduler, Azure Functions, or a background service:

// Console app triggered by Task Scheduler
static void Main()
{
    var html = GenerateReportHTML(); // Pull data, build HTML

    var renderer = new ChromePdfRenderer();
    var pdf = renderer.RenderHtmlAsPdf(html);

    var filename = $"sales-report-{DateTime.Now:yyyy-MM-dd}.pdf";
    pdf.SaveAs(filename);

    EmailReport(filename); // Send via SMTP
}
Enter fullscreen mode Exit fullscreen mode

I run this nightly at 6 AM. Finance gets their report in their inbox before the workday starts.

How Do I Digitally Sign Reports?

Add a digital signature for authenticity:

using IronPdf;

var pdf = renderer.RenderHtmlAsPdf(html);

pdf.SignWithFile("certificate.p12", "password", null,
    IronPdf.Signing.SignaturePermissions.NoChangesAllowed);

pdf.SaveAs("signed-report.pdf");
Enter fullscreen mode Exit fullscreen mode

Signed PDFs show a verification badge in PDF readers, proving the report hasn't been tampered with.

What About Report Performance?

For large datasets, paginate your queries:

const int pageSize = 1000;
int offset = 0;
var html = new StringBuilder();

html.Append("<html><body>");

while (true)
{
    var data = GetSalesData(offset, pageSize); // SQL with OFFSET/FETCH
    if (!data.Any()) break;

    html.Append("<table>");
    foreach (var row in data)
    {
        html.Append($"<tr><td>{row.Product}</td><td>{row.Revenue}</td></tr>");
    }
    html.Append("</table><div style='page-break-after:always'></div>");

    offset += pageSize;
}

html.Append("</body></html>");

var pdf = renderer.RenderHtmlAsPdf(html.ToString());
Enter fullscreen mode Exit fullscreen mode

This prevents memory issues when generating reports with 10,000+ rows.

Can I Merge Multiple Reports?

Generate individual PDFs and merge them:

var salesPdf = renderer.RenderHtmlAsPdf(salesHTML);
var inventoryPdf = renderer.RenderHtmlAsPdf(inventoryHTML);

var merged = PdfDocument.Merge(salesPdf, inventoryPdf);
merged.SaveAs("combined-report.pdf");
Enter fullscreen mode Exit fullscreen mode

This is useful for monthly reports that combine data from multiple departments.

How Do I Handle Images in Reports?

Embed images as base64 or use absolute URLs:

<!-- Base64 embedded -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." />

<!-- Absolute URL -->
<img src="https://example.com/logo.png" />

<!-- Local file path -->
<img src="file:///C:/Reports/logo.png" />
Enter fullscreen mode Exit fullscreen mode

Base64 embeds images directly in the HTML, avoiding path issues.

What Are Alternatives to IronPDF?

QuestPDF offers a fluent C# API for building reports programmatically without HTML. It's open-source and great for code-first report design.

iTextSharp works but requires low-level PDF manipulation. Building tables and layouts takes significantly more code than HTML templating.

SSRS (SQL Server Reporting Services) is overkill for simple reports and requires infrastructure setup.

I chose IronPDF because HTML templating is flexible, designers can work on layouts, and the library handles complex rendering edge cases (CSS Grid, Flexbox, JavaScript charts) reliably.

How Do I Test Report Generation?

Create unit tests with sample data:

[Test]
public void GeneratesSalesReport()
{
    var data = new List<Sale>
    {
        new Sale { Product = "Widget", Revenue = 1000 },
        new Sale { Product = "Gadget", Revenue = 1500 }
    };

    var html = BuildSalesReportHTML(data);
    var pdf = renderer.RenderHtmlAsPdf(html);

    Assert.Greater(pdf.PageCount, 0);
    Assert.IsTrue(File.Exists("test-report.pdf"));
}
Enter fullscreen mode Exit fullscreen mode

I also generate sample reports and visually inspect them before deploying changes.


Written by Jacob Mellor, CTO at Iron Software. Jacob created IronPDF and leads a team of 50+ engineers building .NET document processing libraries.

Top comments (0)