Let's imagine that we have a .NET Core Web API project in which we need to generate a PDF report. Even though it shouldn't suppose to be too hard to do something like that, we could end up losing too much time if we don't know how to do it properly.
In this article, we are going to show how to use the DinkToPDF
library to easily generate PDF documents while working on the .NET Core Web API project.
So, without further ado, let's dive right into the fun part.
- Global Error Handling in .NET Core Web API
- Implementing Action Filters in ASP.NET Core Web API
- ASP.NET Core Authentication with JWT and Angular
- Async Generic Repository Pattern in .NET Core
- Basic Tips and Tricks to Boost Productivity in Visual Studio
VIDEO: How to Easily Create a PDF Document in ASP.NET Core Web API video.
You can download the source code for this article at Creating PDF Document Source Code.
In this post, we are going to cover:
- Basic Project Preparations
- DinkToPdf Library Configuration
- Preparing Data for the PDF Document
- Saving the PDF Document on the Local Storage
- Showing a PDF Document in a Browser
- Using Existing HTML Page to Generate PDF Content
- Enabling Download Mode
- Update Project For Deployment
- Conclusion
Basic Project Preparations
Let's start, by creating a brand new .NET Core 3.0 Web API project named PDF_Generator
:
After the project creation, we are going to modify the launchSettings.json
file to disable our browser to start automatically:
{ "profiles": { "PDF_Generator": { "commandName": "Project", "launchBrowser": false, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
DinkToPdf Library Configuration
DinkToPdf
is a cross-platform oriented library which is the wrapper for the Webkit HTML to PDF library. It uses the WebKit engine to convert HTML to PDF.
It will allow us to create a PDF document from our HTML string that we generate in the .NET Core project, or to create a PDF document from an existing HTML page. Furthermore, we can download the created PDF document or save it on a certain location or return a new HTML page with the PDF content.
We are going to cover all these features in this article.
So, let's install the DinkToPdf
library first:
PM> Install-Package DinkToPdf
Or search for DinkToPdf
inside the Nuget Package window:
After the installation completes, we need to import native library files to our root project. We can find those files in our source project in the NativeLibrary folder. Inside we will find two folders 32bit
and 64bit
, so we need to choose the appropriate library for our OS. We are going to choose the files from the 64bit folder:
Finally, we need to register this library with our IoC container in the StartUp
class:
public void ConfigureServices(IServiceCollection services) { services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools())); services.AddControllers(); }
To learn in more detail about service registration in .NET Core and how to keep Startup methods cleaner, you can read the .NET Core Service Configuration.
Excellent.
We have everything in place and we are ready to proceed.
Preparing Data for the PDF Document
In a real-world project, we can collect data from the database or receive it from other API. But for the sake of simplicity, we are going to collect data for our PDF document from the local storage. Then we are going to create an HTML template and store it in the PDF document.
So let's first create a new folder Models
and inside it the Employee.cs
file:
namespace PDF_Generator.Models { public class Employee { public string Name { get; set; } public string LastName { get; set; } public int Age { get; set; } public string Gender { get; set; } } }
To continue, we are going to create a new folder Utility
and two class files inside it DataStoage.cs
and TemplateGenerator.cs
. A complete structure should look like this:
Now, let's modify the DataStorage.cs
file:
using PDF_Generator.Models; using System.Collections.Generic; namespace PDF_Generator.Utility { public static class DataStorage { public static List<Employee> GetAllEmployees() => new List<Employee> { new Employee { Name="Mike", LastName="Turner", Age=35, Gender="Male"}, new Employee { Name="Sonja", LastName="Markus", Age=22, Gender="Female"}, new Employee { Name="Luck", LastName="Martins", Age=40, Gender="Male"}, new Employee { Name="Sofia", LastName="Packner", Age=30, Gender="Female"}, new Employee { Name="John", LastName="Doe", Age=45, Gender="Male"} }; } }
In this code, we just return a list of employees that will be displayed inside the HTML template.
HTML Template Generation
We want to generate an HTML template, so we need to modify the TemplateGenerator.cs
file:
using System.Text; namespace PDF_Generator.Utility { public static class TemplateGenerator { public static string GetHTMLString() { var employees = DataStorage.GetAllEmployess(); var sb = new StringBuilder(); sb.Append(@" <html> <head> </head> <body> <div class='header'><h1>This is the generated PDF report!!!</h1></div> <table align='center'> <tr> <th>Name</th> <th>LastName</th> <th>Age</th> <th>Gender</th> </tr>"); foreach (var emp in employees) { sb.AppendFormat(@"<tr> <td>{0}</td> <td>{1}</td> <td>{2}</td> <td>{3}</td> </tr>", emp.Name, emp.LastName, emp.Age, emp.Gender); } sb.Append(@" </table> </body> </html>"); return sb.ToString(); } } }
So, we are fetching data from our static DataStorage
class and fill our template with it. The HTML template is nothing more than a pure HTML code.
But we want to style our table and h1 tag as well, so let's create the new folder assets
and inside it the new styles.css
file and modify it:
.header { text-align: center; color: green; padding-bottom: 35px; } table { width: 80%; border-collapse: collapse; } td, th { border: 1px solid gray; padding: 15px; font-size: 22px; text-align: center; } table th { background-color: green; color: white; }
This CSS file is going to be loaded later in the Controller class.
That is it, we have our HTML template to use for the PDF creation. Now, we can continue to the Controller logic.
Saving the PDF Document on the Local Storage
In the Controllers
folder, we are going to create a new empty API controller PdfCreatorController
:
namespace PDF_Generator.Controllers { [Route("api/pdfcreator")] [ApiController] public class PdfCreatorController : ControllerBase { } }
Now, let's modify the PdfCreatorController
class to support the creation and saving a PDF document to a local drive:
using DinkToPdf; using DinkToPdf.Contracts; using Microsoft.AspNetCore.Mvc; using PDF_Generator.Utility; using System.IO; namespace PDF_Generator.Controllers { [Route("api/pdfcreator")] [ApiController] public class PdfCreatorController : ControllerBase { private IConverter _converter; public PdfCreatorController(IConverter converter) { _converter = converter; } [HttpGet] public IActionResult CreatePDF() { var globalSettings = new GlobalSettings { ColorMode = ColorMode.Color, Orientation = Orientation.Portrait, PaperSize = PaperKind.A4, Margins = new MarginSettings { Top = 10 }, DocumentTitle = "PDF Report", Out = @"D:\PDFCreator\Employee_Report.pdf" }; var objectSettings = new ObjectSettings { PagesCount = true, HtmlContent = TemplateGenerator.GetHTMLString(), WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet = Path.Combine(Directory.GetCurrentDirectory(), "assets", "styles.css") }, HeaderSettings = { FontName = "Arial", FontSize = 9, Right = "Page [page] of [toPage]", Line = true }, FooterSettings = { FontName = "Arial", FontSize = 9, Line = true, Center = "Report Footer" } }; var pdf = new HtmlToPdfDocument() { GlobalSettings = globalSettings, Objects = { objectSettings } }; _converter.Convert(pdf); return Ok("Successfully created PDF document."); } } }
Code Explanation
In the code above we first inject our registered Converter with the Dependency Injection inside our constructor by using IConverter
interface. Then we create two objects globalSettings
and objectSettings
and use them as a configuration in the HtmlToPdfDcoument
property. Finally, we convert our pdf configuration into a real PDF Document on our local machine.
Now let's talk about the GlobalSettings
and ObjectSettings
classes.
About the GlobalSettings Class
The GlobalSettings
class consists of the overall configuration properties for the PDF document. We use just a couple of those properties to set up the color mode, orientation, paper size, document title, etc… but if we go to the implementation of the GlobalSettings
class we can find more of those properties.
The Out property is very important if we want to save our files on a local machine. So we need to set it to the path where we want our document to. If we set the Out
property then we can use _converter.Convert(pdf);
to convert our document. We will see how this will change once we try to show our PDF document inside a browser.
One more important note is that all the folders from the Out
path should be previously created or the conversion won't work. So in our example where we create a PDF document in the D:
drive in thePDFCreator
folder, we had to create the PDFCreator
folder prior to PDF document creation.
About the ObjectSettings Class
The ObjectSettings
class consists of the properties related to the contents of the PDF document. So, we can configure the visibility of the page counter, formatting of headers and footers, the body content of our document (HtmlContent
property) or the web settings for our document.
Of course, these are not all of the configuration properties but that's all we need for this article.
The HtmlContent
property is the very important property of this class. It contains our generated HTML template and shows the main body of a PDF document.
WebSettings
is pretty important as well, especially if we have an external CSS file for the styling as we do. In this property, we can configure the encoding of our document and provide the path to our CSS file. If we inspect this property, we are going to find out more settings that we can configure like the background of a PDF document or if we should load images or what the minimum font size is, etc…
Inspecting Results
Let's start our app, open our browser and send a simple request towards our PDF creator endpoint:
As a result, we have our document created in the PDFCreator folder:
And let's inspect the content of the document:
That is awesome.
We can now continue on.
Showing a PDF Document in a Browser
If we want to show our document in a browser instead, we can configure that quite easily.
First, we need to remove the Out
property from the globalSettings
object.
Then instead of this type of conversion:
_converter.Convert(pdf);
We are going to use this type:
var file = _converter.Convert(pdf);
Why is that?
Well as we said if we use the Out
property then the file is sent to stdout and saved to our local machine. But without the Out
property, our output will be stored in a buffer. While converting we need to create a byte array and store it inside the file
variable.
Finally, we are using that file
variable and return it to the requester with a content type.
This is our CreatePDF()
method after modification:
[HttpGet] public IActionResult CreatePDF() { var globalSettings = new GlobalSettings { ColorMode = ColorMode.Color, Orientation = Orientation.Portrait, PaperSize = PaperKind.A4, Margins = new MarginSettings { Top = 10 }, DocumentTitle = "PDF Report" }; var objectSettings = new ObjectSettings { PagesCount = true, HtmlContent = TemplateGenerator.GetHTMLString(), WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet = Path.Combine(Directory.GetCurrentDirectory(), "assets", "styles.css") }, HeaderSettings = { FontName = "Arial", FontSize = 9, Right = "Page [page] of [toPage]", Line = true }, FooterSettings = { FontName = "Arial", FontSize = 9, Line = true, Center = "Report Footer" } }; var pdf = new HtmlToPdfDocument() { GlobalSettings = globalSettings, Objects = { objectSettings } }; var file = _converter.Convert(pdf); return File(file, "application/pdf"); }
And this is the result:
Using Existing HTML Page to Generate PDF Content
We don't have to use our custom HTML template to generate PDF content, we can use an existing HTML page. The effort is minimal. All we have to do is to remove the HtmlContent
property and add the Page
property of the ObjectSettings
class.
So instead of this code:
HtmlContent = TemplateGenerator.GetHTMLString()
let's add this code:
Page = "https://code-maze.com/"
And let's inspect the result:
Enabling Download Mode
If we want to enable the download feature for the PDF document we need to modify our return
statement in our action method. All we have to do is simply add the name of the file with its extension to the return
statement:
return File(file, "application/pdf", "EmployeeReport.pdf");
As a result, we are going to have our file downloaded:
And there it is.
Everything is working as it supposed to.
Update Project For Deployment
If we want to deploy this application, we have to make some changes. Let's do that step by step.
First, in the Utility
folder we are going to add a new classCustomAssemblyLoadContext
and modify it:
internal class CustomAssemblyLoadContext : AssemblyLoadContext { public IntPtr LoadUnmanagedLibrary(string absolutePath) { return LoadUnmanagedDll(absolutePath); } protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { return LoadUnmanagedDllFromPath(unmanagedDllName); } protected override Assembly Load(AssemblyName assemblyName) { throw new NotImplementedException(); } }
After that, let's modify the ConfigureServices
method in the StartUp
class:
public void ConfigureServices(IServiceCollection services) { var context = new CustomAssemblyLoadContext(); context.LoadUnmanagedLibrary(Path.Combine(Directory.GetCurrentDirectory(), "libwkhtmltox.dll")); services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools())); services.AddControllers(); }
In here, we are creating an instance of the CustomAssemblyLoadContext
class and just call the LoadUnmanagedLibrary
method with the path of the libwkhtmltox.dll
file.
We need to do one more thing. When we publish our application, we need to have the libwkhtmltox.dll
file and the styles.css
file in the published directory. To ensure that, right-click on the dll
file in Solution Explorer
and choose properties
. For the Build Action
we are going to choose Content
and for the Copy to Output Directory
, we are going to choose Copy always
. We need to repeat these steps for the CSS file as well:
Now, all we have to do is to publish our application by following one of these tutorials or both of them:
.NET Core Application IIS Deployment
.NET Core Application Linux Deployment
This is the result of the IIS deployment:
Conclusion
In this article, we have used the DinkToPdf library to create PDF documents while working with the .NET Core Web API project. We have created our PDFs in different ways to show many different features of this library.
Posted by: zolazolasavae0272651.blogspot.com
Source: https://code-maze.com/create-pdf-dotnetcore/