One of the most common requirements in a web application is to generate the PDF document either for an invoice, reporting, or any other purpose. As developers are familiar with HTML it would make sense to convert HTML to PDF instead of learning the framework specific language. The open-source solution for it is using the NuGet package DinkToPdf which is a .NET Core wrapper for wkhtmltopdf. As most often the PDF documents are generated in the backend service i.e. Web API, we will explore how to implement it in the most structured way. The source code for this demo is in the GitHub CorePdfDemo repository.
In the Web API project add the “DinkToPDF” NuGet package. In the startup register the dependency with the following code.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));
}
In your service class where you want to create the PDF inject the IConverter in the constructor. In my demo project on DocumentService below is the constructor code.
public class DocumentService : IDocumentService
{
private readonly IConverter _converter;
public DocumentService(
IConverter converter)
{
_converter = converter;
}
}
Blow is the code for converting html string to a pdf document in byte array using DinkToPdf. Most of the configuration is self explanatory, change the values according to the needs.
private byte[] GeneratePdf(string htmlContent)
{
var globalSettings = new GlobalSettings
{
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
Margins = new MarginSettings { Top = 18, Bottom = 18 },
};
var objectSettings = new ObjectSettings
{
PagesCount = true,
HtmlContent = htmlContent,
WebSettings = { DefaultEncoding = "utf-8" },
HeaderSettings = { FontSize = 10, Right = "Page [page] of [toPage]", Line = true },
FooterSettings = { FontSize = 8, Center = "PDF demo from JeminPro", Line = true },
};
var htmlToPdfDocument = new HtmlToPdfDocument()
{
GlobalSettings = globalSettings,
Objects = { objectSettings },
};
return _converter.Convert(htmlToPdfDocument);
}
Call the above method after crating the html string as below.
public byte[] GeneratePdfFromString()
{
var htmlContent = $@"
<!DOCTYPE html>
<html lang=""en"">
<head>
<style>
p{{
width: 80%;
}}
</style>
</head>
<body>
<h1>Some heading</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</body>
</html>
";
return GeneratePdf(htmlContent);
}
Generating the html content in code is not very convenient. Wouldn’t it be nice to generate it using razor views similar to web pages, as you wished there is a way to do it.
Add the NuGet package “Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation” the Web Api project. In the startup register the dependency with the following code.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
}
Add a helper class with a method that can take the razor page file path and the model object. Refer to my demo project code class RazorRendererHelper.
Create a folder in your project and add the razor view file i.e. “.cshtml” file. In my demo application I have created “InvoiceDetails.cshtml” razor view in the folder “Views/PdfTemplate”. Get the necessary data to create a model object and call the RenderPartialToString with the partialName (i.e. razor view location) and model object, it returns the html string which could be passed to DinkToPdf to generate the PDF document.
public byte[] GeneratePdfFromRazorView()
{
var invoiceViewModel = GetInvoiceModel();
var partialName = "/Views/PdfTemplate/InvoiceDetails.cshtml";
var htmlContent = _razorRendererHelper.RenderPartialToString(partialName, invoiceViewModel);
return GeneratePdf(htmlContent);
}
Hope this is useful in your project, go through the source code to understand it better.
Hello. Good article. I wanted to ask you about a problem we are having: Our process is basically to load an html template from disk, we fill it by code and we pass the string with the full html to the component. The PDF is generated but blank.
On the other hand, if we pass the html from a path on the disk or URI, it renders it well. Any idea why this behavior?
Thanks for your attention
In my above example, I was doing the same i.e. generating the pdf from HTML string and calling the librarary to generate pdf. Check out the method GeneratePdf in https://github.com/jeminpro/CorePdfDemo/blob/master/Source/Services/DocumentService.cs
I have a problem, because all PDF’s which I generate are saved to C:/Windows/temp and become a name like this: wktemp-625fc00c-dd46-41b4-bd41-4c6ba2db20d1. I don’t want this, because I do many of PDF’s with this library and this costs me a lot of SSD space. How can I avoid this?
Is there a configuration about, which define where this temporary files are saved?
In my sample project, I am not saving the pdf anywhere on the disk. The API generates PDF in memory and returns it to the client. If you want to save the file on disk depending on your use case, you can use the System.IO.FileSystem.Writeallbytes method i.e. https://docs.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytes
we are using the DinkToPdf but it is taking much time, it is taking approx 5-6 minutes to render or generate the pdf file.
Please suggest for the same.
me also
For me it takes less than a second to generate pdf, not sure why it’s taking so long in your case. Have you checked which code is consuming most time, you can do it by debugging.
hello!
I´m trying to render a graph.js on the pdf. It´s not working. Can you help me on that?
As JavaScript code for chart is rendered on browser their will be a delay before html has rendered. I haven’t tried it before, you can try adding LoadSettings like this LoadSettings = new LoadSettings { JSDelay = 2000 },