How to create a free PDF document using HTML & CSS in ASP.NET Core

Published: Thursday 1 April 2021

There are a number of Nuget packages out there that allow us to create a PDF document for free in ASP.NET Core.

PDF documents have many uses in a web application, such as sending an invoice to a customer, or generating a document.

They also have the advantage of working on all the major operating systems and having a consistent layout.

For this tutorial, we are going to use the SelectPDF package.

SelectPDF not only allows the ability to create a PDF using HTML and CSS, it also allows us to generate a PDF document from a website.

And, we can dictate the width of the website. That means we can capture the layout in mobile, tablet and desktop view.

Resources

To try out this application, download the code example from our code examples section.

As well as that, check our video tutorial where we follow the steps in this article to create a PDF document.

The Application

For this application, we are going to create an ASP.NET Core Web MVC app.

First of all, we are going to generate a PDF document, which displays the layout of RoundTheCode.com in mobile, tablet and desktop view.

Afterwards, we will produce a sample invoice using CSS and HTML.

Not only will we return the PDF as part of the HTTP response, we will also include the ability to save the PDF document to the server's file storage.

Setting Up a PDF Controller

The first thing we want to do is create a new controller called PdfController.

From there, we want to assign a Route attribute to it. This allows us to point the whole of the PdfController to a particular endpoint.

In our instance, we will set the Route attribute to pdf. Therefore, any action created in the PDFController will prefix with /pdf.

// PDFController.cs
[Route("pdf")]
public class PdfController : Controller
{
 
}

Next, we need to create our action.

As we will be calling an async method, we will mark the method as async and return an instance of Task.

In addition, we will assign a Route attribute and set it to website.

This means that this action will be rendered when we call /pdf/website.

// PDFController.cs
[Route("pdf")]
public class PdfController : Controller
{
	[Route("website")]
	public async Task<IActionResult> WebsiteAsync()
	{
		return null;
	}
}

Now that we have set up the controller, we need to bring in the Select.HtmlToPdf.NetCore Nuget package.

We can do that by opening up Package Manager Console in Visual Studio (Tools -> NuGet Packager Manager -> Package Manager Console), and running the following script:

Install-Package Select.HtmlToPdf.NetCore

This Nuget package will allow us to create a PDF document into ASP.NET Core.

Render a Website into a PDF Document

We now have the controller set up. The Nuget package manager has been imported.

From there, we can start creating a PDF document.

First, we will render a website (specifically RoundTheCode.com) into a PDF document.

The PDF document will consist of the mobile, tablet and desktop layout of RoundTheCode.com.

For that, we will create three instances of the HtmlToPdf class.

For each instance, we will set a web site width.

So for mobile, we will set the width to 480px. For tablet, it's 1024px. And finally, for desktop, we set it up for the HD width at 1920px.

Afterwards, we take each instance of the HtmlToPdf class, and run the ConvertUrl method. Within that method, we will pass in a parameter which will be the website address. In our instance the parameter will be https://www.roundthecode.com.

This will be stored into a PDFDocument instance and append to the document everytime we call the ConvertUrl

Finally, we can get the byte array of the PDFDocument instance by calling the Save method.

This then gets returned in the HTTP response, along with a mime type of application/pdf.

[Route("pdf")]
public class PdfController : Controller
{
	[Route("website")]
	public async Task<IActionResult> WebsiteAsync()
	{
		var mobileView = new HtmlToPdf();
		mobileView.Options.WebPageWidth = 480;

		var tabletView = new HtmlToPdf();
		tabletView.Options.WebPageWidth = 1024;

		var fullView = new HtmlToPdf();
		fullView.Options.WebPageWidth = 1920;

		var pdf = mobileView.ConvertUrl("https://www.roundthecode.com/");
		pdf.Append(tabletView.ConvertUrl("https://www.roundthecode.com/"));
		pdf.Append(fullView.ConvertUrl("https://www.roundthecode.com/"));

		var pdfBytes = pdf.Save();

		return File(pdfBytes, "application/pdf");
	}
}

Saving the PDF Document to File Storage

It's all very well rendering a PDF document as part of the HTTP response, but what if we want to save it to file storage?

Well, we can do that by creating a new instance of StreamWriter, saving the stream into a file called RoundTheCode.pdf.

public async Task<IActionResult> WebsiteAsync()
{
	...

	var pdfBytes = pdf.Save();

	using (var streamWriter = new StreamWriter(@"C:\inetpub\wwwroot\RoundTheCode.Pdf\RoundTheCode.pdf"))
	{
		await streamWriter.BaseStream.WriteAsync(pdfBytes, 0, pdfBytes.Length);
	}

	return File(pdfBytes, "application/pdf");
}

And that's it! Our PDF file has been saved to file storage.

Create a PDF Document from HTML and CSS

Our next task is to render a PDF document from HTML and CSS.

In this example, we will add an invoice into a PDF document.

For this, we have already added our CSS file into wwwroot/css/invoice.css and our HTML into a partial view located inside Views/Pdf/_Invoice.cshtml.

These files are included when using our code examples section to download the code example.

Our next task is to render and store the HTML from Views/Pdf/_Invoice.cshtml.

To do that, we need to pass the instance of ICompositeViewEngine into PdfController.

// PdfController.cs
public class PdfController : Controller
{
	protected readonly ICompositeViewEngine _compositeViewEngine;

	public PdfController(ICompositeViewEngine compositeViewEngine)
	{
		_compositeViewEngine = compositeViewEngine;
	}
	...
}

From there, we can start writing the method to turn our invoice into a PDF document.

First, we create a new instance of StreamWriter. This instance will allow us to write the PDF file from the view.

Next, we will check that the view actually exists.

To do that, we call the FindView method from the ICompositeViewEngine instance.

Assuming it exists, we need to create a new ViewContext instance. This is what happens normally when rendering a view in MVC.

We then write this ViewContext instance into our StreamWriter instance. From there, we can convert our StreamWriter instance into a string, and that will give us the HTML of our invoice.

After that, we create a new instance of HtmlToPdf. Then, we can call the ConvertHtmlString method and pass in the HTML as the parameter.

Just like with rendering a website, we can render the PDF into the HTTP response, or we can save the PDF to file storage.

// PdfController.cs
public class PdfController : Controller
{
	...

	[Route("invoice")]
	public async Task<IActionResult> InvoiceAsync()
	{
		using (var stringWriter = new StringWriter())
		{
			var viewResult = _compositeViewEngine.FindView(ControllerContext, "_Invoice", false);

			if (viewResult.View == null)
			{
				throw new ArgumentNullException($"'Views/Pdf/_Invoice.cshtml' does not match any available view");
			}

			var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());

			var viewContext = new ViewContext(
				ControllerContext,
				viewResult.View,
				viewDictionary,
				TempData,
				stringWriter,
				new HtmlHelperOptions()
			);

			await viewResult.View.RenderAsync(viewContext);

			var htmlToPdf = new HtmlToPdf(1000, 1414);
			htmlToPdf.Options.DrawBackground = true;

			var pdf = htmlToPdf.ConvertHtmlString(stringWriter.ToString());
			var pdfBytes = pdf.Save();

			using (var streamWriter = new StreamWriter(@"C:\inetpub\wwwroot\RoundTheCode.Pdf\Invoice.pdf"))
			{
				await streamWriter.BaseStream.WriteAsync(pdfBytes, 0, pdfBytes.Length);
			}

			return File(pdfBytes, "application/pdf");
		}
	}
}

Dynamic PDF Documents

This now allows us to create dynamic PDF documents for our ASP.NET Core web application.

This can be used for many examples, such as creating a website order receipt, creating terms for insurance purchased or sending out a quote.

I'm sure there are many benefits in creating dynamic PDF documents for any ecommerce website out there.