pdf libraries

pdf libraries

C#

C#

Generate Professional PDFs in .NET 8 Using PuppeteerSharp

Marcelo Abreu, founder of pdforge

Marcelo | Founder

Marcelo | Founder

Jan 21, 2025

Jan 21, 2025

Introduction to PuppeteerSharp for HTML to PDF

PuppeteerSharp is a powerful library that simplifies converting HTML to PDF using C# and .NET, making the task of implementing PDF reports in your SaaS application much easier. While developers often consider libraries like iTextSharp, PdfSharp, and tools like Playwright for generating PDFs in a .NET environment, PuppeteerSharp stands out by offering a headless Chrome browser API. This allows for precise rendering of HTML to PDF, ensuring that your PDFs look exactly as they would in a web browser, preserving styles and layouts seamlessly.

You can check out the full documentation here.

PuppeteerSharp vs. Other C# PDF Libraries

PuppeteerSharp remains a strong choice for HTML-to-PDF rendering, especially when precise styling is critical. Below is a quick overview of how it compares to leading PDF libraries:

Download comparison between puppeteersharp and other pdf libraries on C# by NugetTrends

PdfSharp: Great for creating PDFs from scratch programmatically but lacks HTML to PDF conversion capabilities.

iTextSharp: Offers extensive PDF manipulation features but can be complex and has licensing restrictions for commercial use.

Playwright: Similar to PuppeteerSharp but designed for end-to-end testing; less focused on PDF generation and gaining traction and download volume with time.

PuppeteerSharp: Provides accurate HTML to PDF conversion using Chromium, ideal for applications requiring precise styling and layout.

Guide to generate pdf from html using C# Puppeteer Sharp
Guide to generate pdf from html using C# Puppeteer Sharp

Quick Start: Installing PuppeteerSharp in .NET 8

Getting started with PuppeteerSharp is straightforward, even if you’re using the latest .NET 8 environment. Here’s the step-by-step:

Installing PuppeteerSharp

1. Install via NuGet Package Manager:

2. Download Chromium (if not already installed):

await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision

3. Initialize the Browser Instance:

var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true

With PuppeteerSharp installed, you’re ready to generate PDFs from HTML content seamlessly.

Main Features of PuppeteerSharp

Headless Browser Automation

PuppeteerSharp runs a headless Chromium instance—meaning no visible browser window. This approach offers real-browser accuracy for rendering HTML/CSS without the overhead of a full GUI.

Precise Rendering

Because PuppeteerSharp leverages the Chrome/Chromium engine, it closely replicates how a webpage appears in a real browser. Complex layouts, custom fonts, and advanced CSS rules are rendered just as they would in Chrome.

Robust Customization

You can fine-tune PDF creation by setting page size (PaperFormat), margins (MarginOptions), headers, footers, page orientation, and more. This level of detail ensures your PDFs look professional, whether for invoices, reports, or brochures.

Interactivity & JavaScript Execution

With PuppeteerSharp, you can execute JavaScript on a page before capturing it as a PDF, making it easy to load dynamic content or trigger client-side scripts as part of the rendering process.

Generating PDF from HTML Using PuppeteerSharp

Creating a dynamic PDF often starts with an HTML template. Let’s build a complete invoice example to illustrate this.

Dynamic Invoice Template in HTML & CSS

Let’s create a production-ready invoice template using HTML, CSS, and optional print style rules:

<!DOCTYPE html>
<html>
<head>
    <style>
        /* Invoice CSS styles */
        body { font-family: 'Arial', sans-serif; }
        .invoice-box {
            max-width: 800px;
            margin: auto;
            padding: 30px;
            border: 1px solid #eee;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
        }
        .invoice-box table { width: 100%; line-height: inherit; text-align: left; }
        .invoice-box table td { padding: 5px; vertical-align: top; }
        .invoice-box table tr.heading td { background: #eee; border-bottom: 1px solid #ddd; font-weight: bold; }
        .invoice-box table tr.item td { border-bottom: 1px solid #eee; }
        .invoice-box table tr.total td:nth-child(2) { border-top: 2px solid #eee; font-weight: bold; }
    </style>
</head>
<body>
    <div class="invoice-box">
        <table>
            <tr class="top">
                <td colspan="2">
                    <h2>Invoice #{{InvoiceNumber}}</h2>
                    <p>Date: {{Date}}</p>
                </td>
            </tr>
            <tr class="information">
                <td>
                    <strong>From:</strong><br>
                    {{CompanyName}}<br>
                    {{CompanyAddress}}
                </td>
                <td>
                    <strong>To:</strong><br>
                    {{CustomerName}}<br>
                    {{CustomerAddress}}
                </td>
            </tr>
            <tr class="heading">
                <td>Description</td>
                <td>Price</td>
            </tr>
            {{#each Items}}
            <tr class="item">
                <td>{{this.Description}}</td>
                <td>{{this.Price}}</td>
            </tr>
            {{/each}}
            <tr class="total">
                <td></td>
                <td>Total: ${{Total}}</td>
            </tr>
        </table>
    </div>
</body>
</html>

HTML Template Engines

To populate dynamic data into your HTML, consider using template engines like Handlebars.NET. For example:

var template = File.ReadAllText("invoice.html");
var data = new
{
    InvoiceNumber = "INV-1001",
    Date = DateTime.Now.ToString("MM/dd/yyyy"),
    CompanyName = "Acme Corp.",
    CompanyAddress = "123 Business Road, Business City, BC 54321",
    CustomerName = "John Doe",
    CustomerAddress = "789 Residential Ave, Hometown, HT 12345",
    Items = new[]
    {
        new { Description = "Consulting Services", Price = "$1,000.00" },
        new { Description = "Software Development", Price = "$2,500.00" },
        new { Description = "Support and Maintenance", Price = "$500.00" },
    },
    Total = "4,000.00"
};
var htmlContent = Handlebars.Compile(template)(data

Advanced PDF Options & Rendering a Live Webpage

PuppeteerSharp offers multiple ways to generate PDFs. Beyond static HTML, you can convert live webpages directly, including authenticated or dynamic content:

Generating PDF from HTML Content

var page = await browser.NewPageAsync();
await page.SetContentAsync(htmlContent);
await page.PdfAsync("invoice.pdf", new PdfOptions
{
    Format = PaperFormat.A4,
    PrintBackground = true,
    MarginOptions = new MarginOptions
    {
        Top = "20px",
        Right = "20px",
        Bottom = "20px",
        Left = "20px"
    },
    DisplayHeaderFooter = false,
    Landscape = false

Generating PDF from a URL

var page = await browser.NewPageAsync();
await page.GoToAsync("https://yourwebsite.com/invoice/INV-1001");
await page.PdfAsync("invoice.pdf", new PdfOptions
{
    Format = PaperFormat.A4,
    PrintBackground = true,
    MarginOptions = new MarginOptions
    {
        Top = "20px",
        Right = "20px",
        Bottom = "20px",
        Left = "20px"
    },
    DisplayHeaderFooter = false,
    Landscape = false

Utilizing PuppeteerSharp’s PDF API Features

PuppeteerSharp’s PDF options include:

  • Format: Paper size, e.g., PaperFormat.A4.

  • PrintBackground: Include background graphics.

  • MarginOptions: Set top, right, bottom, and left margins.

  • DisplayHeaderFooter: Show headers and footers.

  • HeaderTemplate and FooterTemplate: HTML templates for headers and footers.

  • Landscape: Set orientation to landscape.

  • Scale: Scale of the webpage rendering (default 1).

  • PageRanges: Specify pages to include, e.g., "1-5".

Example with all options:

await page.PdfAsync("invoice.pdf", new PdfOptions
{
    Scale = 1.0m,
    DisplayHeaderFooter = true,
    HeaderTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>My Company Header</div>",
    FooterTemplate = "<div style='font-size:10px; text-align:center; width:100%;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
    PrintBackground = true,
    Landscape = false,
    PageRanges = "1-2",
    Format = PaperFormat.A4,
    MarginOptions = new MarginOptions
    {
        Top = "50px",
        Bottom = "50px",
        Left = "20px",
        Right = "20px"

Common Pitfalls (and How to Avoid Them)

Even though PuppeteerSharp provides a smooth path to generating PDFs, there are a few common challenges you might face. Below are practical examples for each pitfall and how to circumvent them.

Large File Sizes

High-resolution images, large CSS files, and background graphics can inflate your PDF. Consider disabling background rendering or scaling down images to reduce the final file size:

// After loading your HTML or navigating to a page
var pdfOptions = new PdfOptions
{
    Format = PaperFormat.A4,
    // Turning off background images can significantly reduce the file size
    PrintBackground = false,
    // Optionally, reduce scale for less detailed rendering if appropriate
    Scale = 0.8m
};
await page.PdfAsync("invoice-optimized.pdf", pdfOptions

Tip: Compress or minify images and styles beforehand, or use a dedicated image compression library in your .NET application.

Missing Assets

If external CSS or JS files aren’t loading (e.g., firewall rules, incorrect paths), your rendered PDF could look broken. Use networkidle settings or checks for resource availability:

// Navigate and wait until no more network requests are happening
await page.GoToAsync("https://example.com/invoice", WaitUntilNavigation.Networkidle0);

// Optional: Check if critical CSS is present
bool hasStyles = await page.EvaluateExpressionAsync<bool>("document.styleSheets.length > 0");

if (!hasStyles)
{
    Console.WriteLine("Warning: Stylesheets might be missing—verify your CSS URLs."

Tip: If your environment blocks external assets (fonts, images), consider hosting them locally within the same domain or using a CDN with proper permissions.

Asynchronous Content

Many modern web pages fetch data dynamically via JavaScript. If PuppeteerSharp captures the page too early, your PDF might be incomplete. Make sure to wait for specific elements or JavaScript events before rendering:

await page.GoToAsync("https://example.com/dashboard");

// Wait for a specific element that indicates your content is loaded
await page.WaitForSelectorAsync("#chartContainer");

// Or wait for custom font loading to finish
await page.EvaluateExpressionHandleAsync("document.fonts.ready");

// Now generate your PDF with confidence that everything is loaded
await page.PdfAsync("dashboard.pdf"

Tip: If your site uses frameworks like React or Angular, consider waiting for certain network calls or UI states (e.g., “loading” indicators) to disappear before capturing the PDF.

Performance & Deployment

Generating PDFs at scale in a SaaS environment often requires careful consideration of both resource usage and deployment strategies. Below are a few tips to help you optimize performance and streamline your PuppeteerSharp workflows:

Reusing Browser Instances

One effective way to reduce overhead and memory usage is by reusing a single headless browser instance rather than launching a new instance for each PDF request. This approach drastically cuts down on initialization time and helps maintain a more consistent memory footprint. Just be sure to manage concurrent page usage to avoid potential conflicts during generation.

Containerization and Serverless Platforms

Deploying your .NET application in a Docker container or on serverless platforms like Azure Functions or AWS Lambda can simplify your environment setup. However, some serverless platforms require special handling for headless Chromium binaries:

• Ensure you’re using a compatible Chromium build that runs on your target platform.

• Take note of any memory and storage limits that might affect PuppeteerSharp’s performance.

Logging and Error Handling

Proper logging and exception handling become essential as your PDF generation load increases. Keep track of:

Timeouts or slow-loading pages.

Missing assets (CSS, JS, images).

Concurrency errors if multiple pages are being rendered simultaneously.

Having robust logs helps you quickly identify performance bottlenecks or resource constraints and ensures consistent PDF output for your end users.

Scaling PDF Generation with a Third-Party API

homepage of pdforge

For larger SaaS platforms requiring automated PDF generation at scale, integrating a PDF Generation API like pdforge can offload the heavy lifting. This approach is ideal for SaaS platforms with high volumes of PDF requests.

With pdforge, you can create beautiful reports with flexible layouts and complex components with an easy-to-use opinionated no-code builder. Let the AI do the heavy lifting by generating your templates, creating custom components or even filling all the variables for you.

You can handle high-volume PDF generation from a single backend call.

Here’s an example of how to generate pdf with pdforge via an API call:

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace PdfApiIntegration
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var client = new HttpClient();
            client.DefaultRequestHeaders.Add("Authorization", "Bearer your-api-key");
            var requestBody = new
            {
                templateId = "your-template",
                data = new { html = "your-html" }
            };
            var content = new StringContent(
                Newtonsoft.Json.JsonConvert.SerializeObject(requestBody),
                Encoding.UTF8,
                "application/json"
            );
            var response = await client.PostAsync("https://api.pdforge.com/v1/pdf/sync", content);
            if (response.IsSuccessStatusCode)
            {
                var pdfBytes = await response.Content.ReadAsByteArrayAsync();
                File.WriteAllBytes("invoice.pdf", pdfBytes);
                Console.WriteLine("PDF generated using PDFForge API.");
            }
            else
            {
                Console.WriteLine("Error generating PDF: " + response.ReasonPhrase

You can create your account, experience our no-code builder and create your first layout template without any upfront payment clicking here.

Conclusion

Choosing the right tool for PDF generation depends on your project’s requirements:

Opt for PuppeteerSharp when accurate HTML to PDF conversion is crucial, especially with complex CSS.

Use iTextSharp or PdfSharp when you need granular control over PDF elements without HTML rendering.

If you don't want to waste time maintaining pdfs layouts and their infrastructure or if you don't want to keep track of best practices to generate PDFs at scale, third-party PDF APIs like pdforge will save you hours of work and deliver a high quality pdf layout.

Generating pdfs at scale can be quite complicated!

Generating pdfs at scale can be quite complicated!

We take care of all of this, so you focus on what trully matters on your Product!

We take care of all of this, so you focus on what trully matters on your Product!

Try for free

7-day free trial

Table of contents

Title