Generating PDFs from dynamic HTML can be a daunting task. However, with the appropriate tools, it can be hassle free. The Syncfusion HTML-to-PDF converter library, in combination with ASP.NET Core Minimal Web API, offers a straightforward approach for dynamically generating PDFs from HTML templates.
This tutorial will teach us how to create an ASP.NET Core Minimal Web API that dynamically generates a PDF document from an HTML template using the Syncfusion HTML-to-PDF converter library.
Here are the steps to create a job application from an HTML template using ASP.NET Core Minimal API in C#,
In an HTML file, define your page’s structure, including the head and body sections and any other elements you’d like to include, such as image, header, footer, etc.
Also, it contains placeholders with {{mustache}} syntax, and it is used to bind the actual data to the HTML template. We’ll use the Scriban scripting language to create the placeholders which is a lightweight scripting language and engine for .NET
Note: To learn more about the Scriban scripting language, refer to the documentation.
HTML
<html lang="en"> <head> <meta charset="UTF-8" /> <title>JobOfferLetter</title> <link rel="stylesheet" href="style.css" media="all" /> </head> <body> <div class="grid-container "> <img class="logo" src="logo.png" style="width:70px;height:70px;margin-left: 50px;" /> <div style="margin-left:70px; margin-top:55px"> <b> AMAZE <br /> FOX </b> </div> <div style="font-size: 12px; margin-left: 300px; margin-top: 55px"> <div><b>{{company_details.street}} </b></div> <div><b>{{company_details.city}} </b></div> <div><b>Phone: {{company_details.phone}} </b></div> <div><b>{{company_details.website}} </b></div> </div> </div> <br /> <div class="body-content"
style="margin-left: 50px;"> <p><b>Dear {{employee_details.name}},</b></p> <p>We are pleased to offer you the position of Accountant at {{company_details.company_name}}. We are confident that you will contribute your skills and experience towards the growth of our organization.</p> <p>As per the discussion, your starting date will be {{employee_details.joining_date}}. Please find the employee handbook enclosed herewith which contains the medical and retirement benefits offered by our organizations.</p> <p>Please confirm your acceptance of this offer by signing and returning a copy of this offer letter.</p> <p>If you have any questions regarding the same, contact the manager or me via email or phone: {{company_details.phone}}</p> <p>We look forward to welcoming you on board.</p> </div> <div class="body-content"
style="margin-left: 50px;"> <p><b>Sincerely,<br /> {{company_details.company_name}} <br />{{company_details.employer_name}} <br />(Managing Director)</b></p> </div> <div id="footer"> <div class="footer-columns"
style="margin-left: 50px;"> <p>{{company_details.company_name}}</p> <p>{{company_details.website}}</p> </div> </div> </body> </html>
By default, the properties and methods of .NET objects are automatically exposed with lowercase and _ names. This means that a property like CompanyName will be exposed as company_name, and while performing the conversion, the values can be imported from the respective JSON files.
Create a CSS file to control the appearance of your HTML template. In this CSS file, you’ll define styles for your page elements, such as font sizes, colors, and images.
You can get this HTML template with CSS and fonts from this GitHub repository.
The following screenshot shows the output of the HTML template with styled CSS.
Furthermore, any additional resources, such as fonts, images, JSON files, etc., should be in the same folder as the HTML file. Please refer to the screenshot below for visual reference.
Minimal APIs are architected to create HTTP APIs with minimal dependencies. They are ideal for microservices and apps that want to include only the minimum files, features, and dependencies in ASP.NET Core.
Kindly refer to this tutorial to create a minimal API with ASP.NET Core using Visual Studio 2022.
The program.cs file involves receiving files, such as HTML, JSON, and assets (such as fonts, images, etc.), from a client request.
Finally, the HTML text, JSON data, and assets are combined and converted into a PDF document using HtmlToPdfConverter.
app.MapPost("/api/convertToPDF", async (HttpContext context) => { try { var html = await context.Request.ReadFormAsync(); var value = html.AsQueryable().ToList().Where(x => x.Key == "application/json").FirstOrDefault().Value.ToString(); var options = JsonConvert.DeserializeObject<ConversionOptions>(value); string htmlText = ""; string jsonData = ""; if (options != null) { htmlText = ReadText(html.Files[options.Index].OpenReadStream()); jsonData = ReadText(html.Files[options.Data].OpenReadStream()); CopyAssets(options.Assets, html.Files); } String path = Path.GetFullPath("template/"); var converter = new HtmlToPdfConversion(); var pdfDocument = converter.ConvertToPDF(htmlText, path, jsonData, options); context.Response.ContentType = "application/pdf"; await context.Response.Body.WriteAsync(pdfDocument); } catch (Exception exception) { } }); app.UseCors("AllowBlazorClient"); app.Run(); void CopyAssets(List<string> assets, IFormFileCollection files) { if (Directory.Exists("template/")) { System.IO.DirectoryInfo di = new DirectoryInfo("template/"); foreach (FileInfo file in di.GetFiles()) { file.Delete(); } } else Directory.CreateDirectory("template/"); var formFiles = files.ToList(); foreach (var asset in assets) { Stream stream = formFiles.FirstOrDefault(x => x.FileName == asset).OpenReadStream(); if (stream != null) { var fileStream = new FileStream("template/" + asset, FileMode.Create); stream.CopyTo(fileStream); fileStream.Close(); stream.Close(); } } } string ReadText(Stream stream) { StreamReader reader = new StreamReader(stream); string text = reader.ReadToEnd(); reader.Close(); strem.Dispose(); return text; }
Next, the HtmlToPdfConverter converts the HTML string, accompanied by the assets, into a PDF document via the blink rendering engine within the HtmlToPdfConversion.cs file. During this conversion, we established the size and margin of the PDF page and the viewport size using the BlinkConverterSettings class. Please refer to the accompanying code example for further information.
//Fill the template with real job offer letter data and assets var expando = JsonConvert.DeserializeObject<ExpandoObject>(modelData); var sObject = BuildScriptObject(expando); var templateCtx = new Scriban.TemplateContext(); templateCtx.PushGlobal(sObject); var template = Scriban.Template.Parse(pageContent); var result = template.Render(templateCtx); //Initialize HTML to PDF converter with Blink rendering engine HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter(); //Initialize blink converter settings to set the page size BlinkConverterSettings blinkConverterSettings = new BlinkConverterSettings(); if (options.Width != 0 && options.Height != 0) { blinkConverterSettings.PdfPageSize = new Syncfusion.Drawing.SizeF(options.Width, options.Height); } else { blinkConverterSettings.ViewPortSize = new Syncfusion.Drawing.Size(595, 842); } blinkConverterSettings.Margin = new PdfMargins() { All = options.Margin }; htmlConverter.ConverterSettings = blinkConverterSettings; //Convert HTML string to PDF PdfDocument document = htmlConverter.Convert(result, path); MemoryStream output = new MemoryStream(); //Save and close the PDF document document.Save(output); document.Close(true);
The client application in this implementation is a Blazor WebAssembly application built with .NET version 7.0. To create a new ASP.NET Core Blazor WebAssembly application using Visual Studio 2022, please follow this documentation. Within the application, we utilize the HttpClient.PostAsync method to send a POST request to the specified URI as an asynchronous operation.
//Send a request to the server var response = await client.PostAsync("https://localhost:7045/api/convertToPDF", content);
The inclusion of the HTML and CSS files in the HttpClient is necessary. Additionally, supplementary assets such as fonts, images, and PDF size specifications should be sent with the request using the MultipartFormDataContent.
private async Task ConvertToPDF() { //Create an http client to send both files and json data using (var client = new HttpClient()) { //Create multipart form data content using (var content = new MultipartFormDataContent()) { var html = await Http.GetByteArrayAsync(" JobOfferLetter /index.html"); var css = await Http.GetByteArrayAsync(" JobOfferLetter /style.css"); var data = await Http.GetByteArrayAsync(" JobOfferLetter /Data.json"); var logo = await Http.GetByteArrayAsync(" JobOfferLetter /logo.png"); var font = await Http.GetByteArrayAsync(" JobOfferLetter / OpenSans-Regular.ttf"); //Add file to content content.Add(CreateContent("index.html", "index.html", html)); content.Add(CreateContent("style.css", "style.css", css)); content.Add(CreateContent("Data.json", "Data.json", data)); content.Add(CreateContent("logo.png", "logo.png", logo)); content.Add(CreateContent(" OpenSans-Regular.ttf", " OpenSans-Regular.ttf", font)); var json = new JsonObject { ["index"] = "index.html", ["data"] = "Data.json", ["width"] = 0, ["height"] = 0, ["margin"] = 40, ["assets"] = new JsonArray { "style.css", "logo.png", " OpenSans-Regular.ttf" } }; //Add json data to content content.Add(new StringContent(json.ToString()), "application/json"); //Send a request to the server var response = await client.PostAsync("https://localhost:7045/api/convertToPDF", content); if (response.StatusCode == HttpStatusCode.OK) { var responseContent = await response.Content.ReadAsStreamAsync(); using var Content = new DotNetStreamReference(stream: responseContent); await JS.InvokeVoidAsync("SubmitHTML", "HTMLToPDF.pdf", Content); } } } }
Once the requested response status code is OK, invoke the JavaScript (JS) function in the index.html file to save the PDF document.
<script> window.SubmitHTML = async (fileName, contentStreamReference) => { const arrayBuffer = await contentStreamReference.arrayBuffer(); const blob = new Blob([arrayBuffer]); const url = URL.createObjectURL(blob); const anchorElement = document.createElement('a'); anchorElement.href = url; anchorElement.download = fileName ?? ''; anchorElement.click(); anchorElement.remove(); URL.revokeObjectURL(url); } </script>
While building and running the application, the website will open in your default browser.
Here are the steps to launching the server and invoking the PDF generation API from the client application.
Step 1: Run the Web API application to launch the published web API in the browser.
Step 2: To generate a PDF document using the client application, send an asynchronous POST request to the specified URI (e.g., https://localhost:7094/api/convertToPDF) on the localhost. This will send the request to the server application, which will convert the HTML to PDF and send the response back to the client. After running the client application, click the Convert to PDF button to initiate the HTML to PDF conversion process and generate a PDF document named HTMLToPDF.pdf in the designated folder.
The following screenshot illustrates that you will receive a PDF document upon successful conversion.
For better understanding, we have committed the source for this project in the Generate PDF from the HTML C# GitHub repository.
In this blog post, we have learned how to generate PDF documents from an HTML template using Syncfusion HTML to PDF library in combination with ASP.NET Core minimal API.
Take a moment to look at the documentation, where you will find other options and features, all with accompanying code samples.
Please let us know in the comments below if you have any questions about these features. You can also contact us through our support forum, support portal, or feedback portal. We are happy to assist you!
If you liked this article, we think you would also like the following articles about PDF Library: