Streamlining .NET Invoice PDFs

Sagar HSSagar HS
4 min read

Automating PDF invoice creation in .NET shouldn’t feel like wrestling with command-line tools or juggling raw HTML. InvoiceGenerator brings together Razor templating, IronPdf rendering, and idiomatic dependency-injection wiring into one cohesive library. Below, we unpack each layer so you can plug it into your application with confidence.

Overview

InvoiceGenerator.Core is a NuGet package that helps you generate elegant, typed PDF invoices with just a few lines of code. It supports:

  • Razor view templates (.cshtml)

  • IronPdf rendering

  • Dependency injection

  • Custom configuration options

  • Cross-platform support

🔗 NuGet | GitHub


🔧 Why I Built It

Many .NET teams build invoices using either static HTML templates or overcomplicated reporting tools.

Problems with those approaches:

  • Lack of customisability

  • No DI integration

  • Complex to maintain

So, I created a solution that integrates clean architecture, DI, and Razor templating using modern .NET practices.


🧠 High-Level Architecture

sequenceDiagram
  participant App as Your .NET App
  participant Services as IServiceProvider
  participant InvGen as IInvoiceGenerator
  participant PdfGen as InvoicePdfGenerator
  participant Razor as IRazorViewToStringRenderer
  participant View as Razor Template (e.g. Views/InvoiceReport.cshtml)
  participant Engine as IronPdfGenerator
  participant IronPdf as IronPdf.ChromePdfRenderer

  App->>Services: GetRequiredService<IInvoiceGenerator>()
  Services-->>App: InvoicePdfGenerator

  App->>InvGen: GenerateAsync(invoice)
  InvGen->>PdfGen: GenerateAsync(invoice)

  PdfGen->>PdfGen: ValidateInvoice(invoice)
  PdfGen->>Razor: RenderViewToStringAsync(TemplatePath, invoice)
  Razor->>View: Compile & Bind Razor View
  View-->>Razor: HTML string
  Razor-->>PdfGen: HTML content

  PdfGen->>Engine: GeneratePdf(html)
  Engine->>IronPdf: RenderHtmlAsPdf(html)
  IronPdf-->>Engine: PdfDocument
  Engine-->>PdfGen: byte[] PDF

  PdfGen-->>App: byte[] PDF

🧩 Usage Example

var services = new ServiceCollection()
    .AddInvoiceGenerator(options =>
    {
        options.DefaultPageSize = InvoiceGeneratorOptions.PageSizes.A4;
        options.DefaultOrientation = InvoiceGeneratorOptions.Orientations.Portrait;
        options.DefaultDocumentTitle = "Test Invoice";
    })
    .BuildServiceProvider();

var generator = services.GetRequiredService<IInvoiceGenerator>();
var invoice = InvoiceFactory.Create();

byte[] pdfBytes = await generator.GenerateAsync(invoice);
File.WriteAllBytes("Invoice.pdf", pdfBytes);

💡 Under the Hood: How It Works

1. AddInvoiceGenerator – Configurable DI Extension

This method sets up everything:

  • Loads Razor rendering service

  • Configures IronPdf

  • Registers IInvoiceGenerator

services.AddScoped<IPdfGenerator>(sp => new IronPdfGenerator(logger)
{
    PageSize = "A4",
    Orientation = "Portrait"
});

You can also:

  • Set a Razor view path (TemplatePath)

  • Provide an IronPdf license key


2. InvoicePdfGenerator – The Core Engine

This is the main implementation of IInvoiceGenerator. It:

  • Validates the Invoice object

  • Renders Razor HTML using the provided model

  • Converts that HTML to PDF using IronPdf

string html = await _razorRenderer.RenderViewToStringAsync(_templatePath, invoice);
byte[] pdfData = _pdfGenerator.GeneratePdf(html);

You can override the Razor view path like this:

invoiceGenerator.TemplatePath = "Views/CustomInvoice.cshtml";

3. Razor Rendering (RazorViewToStringRenderer)

Renders .cshtml files to HTML strings using RazorLight.

You get full freedom to:

  • Customize layout

  • Add logos, tables, styles

  • Use @model Invoice in views

Edit@model Invoice
<h1>Invoice #: @Model.Number</h1>
<p>Total: @Model.Total @Model.CurrencyCode</p>

4. PDF Generation (IronPdfGenerator)

Uses IronPdf.ChromePdfRenderer to convert rendered HTML to PDF:

renderer.RenderHtmlAsPdf(html);

Supports:

  • A4, Letter, Legal, etc.

  • Portrait / Landscape

  • Custom margins

  • Custom titles

Also logs PDF size and diagnostic info via ILogger.


🔍 Config Options

InvoiceGeneratorOptions lets you customize:

OptionDescriptionDefault
PageSizeA4, Letter, Legal, etc."A4"
OrientationPortrait or Landscape"Portrait"
MarginMmMargin in millimeters20
CustomTemplatePathPath to your Razor .cshtml fileViews/InvoiceReport.cshtml
IronPdfLicenseKeyLicense key (or read from config)(optional)
UseAppSettingsLoad from appsettings.jsontrue

🔐 Validation Built-in

Before generating any invoice, we validate:

  • 📅 Due date ≥ Issue date

  • 🧾 At least one line item

  • 🏢 Seller and customer addresses

Invalid invoice? You’ll get a ValidationException.


🧪 Real Example – Multi-Currency Invoices

await GenerateAndSaveInvoice(invoiceGenerator, InvoiceFactory.Create(), "USD_Invoice.pdf");
await GenerateAndSaveInvoice(invoiceGenerator, InvoiceFactory.CreateEuroInvoice(), "EUR_Invoice.pdf");
await GenerateAndSaveInvoice(invoiceGenerator, InvoiceFactory.CreateGbpInvoice(), "GBP_Invoice.pdf");

Each invoice PDF will be cleanly formatted, typed, and ready to share or email.


🧠 Lessons Learned

  • Razor + IronPdf is a powerful combo for document generation.

  • Validating data before rendering = fewer runtime surprises.

  • Abstracting services through interfaces made testing and extending painless.


📦 NuGet + GitHub


🛠️ Contributions Welcome!

This is an active project, I’d love your feedback, issues, or pull requests.

Have an idea? Need to support more paper sizes or multi-page layouts? Fork away or reach out!


🙌 Final Thoughts

Building InvoiceGenerator.Core was about making invoicing flexible, testable, and production-friendly.

Whether you're building:

  • B2B apps,

  • Admin dashboards,

  • Finance portals,

This library gives you clean, repeatable PDF generation with modern .NET practices.

0
Subscribe to my newsletter

Read articles from Sagar HS directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sagar HS
Sagar HS

Software engineer with 4 + years delivering high-performance .NET APIs, polished React front-ends, and hands-off CI/CD pipelines. Hackathon quests include AgroCropProtocol, a crop-insurance DApp recognised with a World coin pool prize and ZK Memory Organ, a zk-SNARK privacy prototype highlighted by Torus at ETH-Oxford. Recent experiments like Fracture Log keep me exploring AI observability.