Creating PDF on ASP.NET Core

Creating PDF files on ASP.NET Core has been issue for awhile. I needed some proof-of-concept solution to prove it’s possible to generate PDF files on ASP.NET Core easily without writing a lot of code or going through complex configuration. I solved the problem and here is my solution.

The best and most polished solution I found was Rotativa.AspNetCore. It takes some additional steps after installing NuGet package but it’s nothing complex. And what’s best – it is cross-platform package that works also on Linux and Apple machines.

Making Rotativa.AspNetCore work on Windows

Executables for Rotativa.AspNetCoreThere are two additional files that doesn’t come with NuGet package – wkhtmltoimage.exe and wkhtmltopdf.exe. By default it is expected that these files are located in folder wwwroot/Rotativa. As I don’t like the idea of having executables somewhere where direct requests can be done I moved the files to Rotativa folder in project root.

You can find these files from wwwroot/Rotiva folder of Rotativa.AspNetCore demo application repository at GitHub here. Just download these files and put them to Rotativa folder under project root like shown on the image on rights.

Before we can start with PFD we have to tell in configuration where those additional files are located.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...
    RotativaConfiguration.Setup(env, "..\\Rotativa\\");
}

On preparation side this is all when going with Windows. On Linux and Apple you may need some additional libraries to be available.

Invoice models

To make my proof-of-concept code more realistic I decided to go with simple invoice. Here are models for Invoice and InvoiceLine.

public class Invoice
{
    public string InvoiceNo { get; set; }
    public string CustomerName { get; set; }
    public string CustomerBillingAddress { get; set; }
    public DateTime InvoiceDate { get; set; }
    public DateTime DueDate { get; set; }

    public IList<InvoiceLine> Lines { get; set; }

    public Invoice()
    {
        Lines = new List<InvoiceLine>();
    }

    public decimal Total
    {
        get
        {
            return Lines.Sum(l => l.Total);
        }
    }

    public decimal Vat
    {
        get
        {
            return Lines.Sum(l => l.Vat);
        }
    }

    public static Invoice GetOne()
    {
        var invoice = new Invoice();
        invoice.CustomerBillingAddress = "Wellington str. 2-311, 10113, NY, USA";
        invoice.CustomerName = "Imaginary Corp.";
        invoice.DueDate = DateTime.Now.AddDays(30);
        invoice.InvoiceDate = DateTime.Now.Date;
        invoice.InvoiceNo = "B383810312-2213";

        var line = new InvoiceLine();
        line.Amount = 12;
        line.LineTitle = "Fancy work desks";
        line.UnitPrice = 800;
        line.VatPercent = 20;
        invoice.Lines.Add(line);

        line = new InvoiceLine();
        line.Amount = 5;
        line.LineTitle = "Espresso machines";
        line.UnitPrice = 200;
        line.VatPercent = 20;
        invoice.Lines.Add(line);

        line = new InvoiceLine();
        line.Amount = 30;
        line.LineTitle = "Meeting room whiteboards";
        line.UnitPrice = 50;
        line.VatPercent = 20;
        invoice.Lines.Add(line);

        return invoice;
    }
} public class InvoiceLine
{
    public string LineTitle { get; set; }
    public decimal UnitPrice { get; set; }
    public int Amount { get; set; }
    public int VatPercent { get; set; }

    public decimal Net
    {
        get
        {
            return UnitPrice * Amount;
        }
    }
    public decimal Total
    {
        get
        {
            return Net * (1 + VatPercent / 100M);
        }
    }
    public decimal Vat
    {
        get
        {
            return Net * (VatPercent / 100M);
        }
    }
}

Notice that Invoice class has static method GetOne() that created demo invoice for us.

Building invoice view

We have invoice and now we need view to show it. I named view as Invoice.cshtml. It doesn’t use layout page as we don’t need any frame UI shown around the view.

@model Invoice
@{
    Layout = null;
}
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Invoice @Model.InvoiceNo</title>
    <link rel="stylesheet" href="@Url.Content("~/css/invoice-report.css")" />
</head>
<body>
  <div class="invoice-title">
    Invoice @Model.InvoiceNo
  </div>

  <div class="invoice-head clearfix">
    <div class="invoice-to">
      <strong>@Model.CustomerName</strong><br />
      @Model.CustomerBillingAddress
    </div>
    <div class="invoice-details">
      Invoice no: @Model.InvoiceNo<br />
      Date: @Model.InvoiceDate.ToShortDateString()<br />
      Due date: @Model.DueDate.ToShortDateString()
    </div>
  </div>

  <table>
    <tr>
      <th>Item</th>
      <th>Unit price</th>
      <th>Amount</th>
      <th>Net</th>
      <th>VAT (%)</th>
      <th>Total</th>
    </tr>
    @foreach(var line in Model.Lines)
    {
      <tr>
         <td>@line.LineTitle</td>
         <td class="numeric-cell">@line.UnitPrice.ToString("0.00") EUR</td>
         <td class="numeric-cell">@line.Amount</td>
         <td class="numeric-cell">@line.Net.ToString("0.00") EUR</td>
         <td class="numeric-cell">@line.VatPercent%</td>
         <td class="numeric-cell">@line.Total.ToString("0.00") EUR</td>
       </tr>
    }
    <tr>
       <td colspan="4"></td>
       <td><strong>Total:</strong></td>
       <td><strong>@Model.Total.ToString("0.00") EUR</strong></td>
    </tr>
  </table>
</body>
</html>

This view with styles forms nice invoice we can also show in browser to print it out. Here are styles for view.

body {
margin: 0px;
  font-family: 'Segoe UI', Arial;
}

.invoice-head {
  clear:both;
  display:block;
  padding: 10px;
  margin-bottom: 40px;
}
.invoice-title {
  background-color: navy;
  color: white;
  font-size: 20px;
  padding: 10px;
  width: 100%;
}
.invoice-to {
  float:left;
}
.invoice-details {
  float:right;
}

table {
  clear: both;
  border: 1px solid darkgray;
  border-collapse: collapse;
  margin: auto;
}
th {
  background-color: navy;
  color: white;
}
td, th {
  border: 1px solid darkgray;
  padding: 5px;
}
.numeric-cell {
  text-align:right;
}

.clearfix::after {
  content: "";
  clear: both;
  display: table;
}

Here is InvoiceHtml() action I added to Home controller.

public IActionResult InvoiceHtml()
{
    return View("Invoice", Invoice.GetOne());
}

When running web application and going to /InvoiceHtml address we will see the invoice like shown below.

PDF invoice as HTML

Creating PDF invoice

Let’s add now another action to Home controller and name it as InvoicePDF(). This action uses the same view we built above but it turns to PDF-file.

public IActionResult InvoicePdf()
{
    return new ViewAsPdf("Invoice", Invoice.GetOne());
}

One thing to notice – instead of another hell of code to just get the PDF and get it to browser we have single line of code that takes care of everything. Here is our invoice as PDF-file.

PDF invoice with ASP.NET Core

Two flies with one hit – invoice in browser ready for printing and invoice as PDF-file ready for download.

Wrapping up

There are not many cross-platform options to generate PDF-files on ASP.NET Core but ??? is the best one I met this far. Although it needs some small steps to do besides adding a NuGet package to solution it is still simple enough to go with it. I like the idea that it is easy to generate PDF-file based on view and return it as an action result without writing any additional code.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    10 thoughts on “Creating PDF on ASP.NET Core

    • Pingback:Dew Drop – May 8, 2018 (#2720) | Morning Dew

    • May 11, 2018 at 4:30 pm
      Permalink

      Have you tested this Invoice example with a multi-page invoice? If you had too many invoice lines to fit on a single page, how could you create a multi-page invoice with header/footer and page numbers on each page?

    • Pingback:Szumma #109 – 2018 19. hét + Microsoft Build 2018 – ./d/fuel

    • May 18, 2018 at 11:36 am
      Permalink

      Jake, there are some options to set margings etc. I will dig around there and write new blog post if I find something useful for more complex features.

    • July 3, 2018 at 8:30 am
      Permalink

      I would suggest to try ZetPDF.com that works so well in generating pdf files into .net

    • July 9, 2018 at 4:40 pm
      Permalink

      Derek, is ZetPDF commercial product or not? I cannot read it out from their homepage.

    • August 27, 2018 at 1:29 pm
      Permalink

      Nice article! It’s really helpful!

      As a professional content writer, who constantly works with PDF files, I would also suggest https://zetpdf.com/. It’s really convenient and easy to use.

    • September 21, 2018 at 5:21 pm
      Permalink

      Word of warning – that ZetPDF.com site is an obvious scam

    • October 9, 2018 at 12:26 pm
      Permalink

      thanks heaps Gunnar! Saved me a heap of time.

    • November 22, 2018 at 5:00 pm
      Permalink

      ZetPDF is a C ASP NET VB NET PDF API to create PDF documents from scratch using API or XML PDF library for Windows Forms, WPF and Silverlight as well
      https://zetpdf.com

    Leave a Reply

    Your email address will not be published. Required fields are marked *