If you have ever had to create dynamic PDF documents (such as invoices, reports, or contracts) within a .NET application, you know how challenging this can be.
A common traditional approach involves using Microsoft.Office.Interop.Word. While this may work fine on a developer’s local machine, things often fall apart once the application is deployed to a Linux server or a Docker container. This method is slow, requires the installation of the resource-heavy Office suite, and is notoriously unstable in server environments that lack a graphical user interface (GUI).
Another option is to construct PDFs "pixel-by-pixel" using low-level libraries - but maintaining such code is a nightmare. Every time the marketing department decides to tweak an invoice layout or add a logo, you end up having to rewrite the code.
There is a better way: performing a "mail merge" by combining data from a JSON file with a .docx template.
In this article, I will demonstrate how to automate the creation of PDF invoices in C# using a lightweight solution that requires no third-party dependencies. We will take a standard Word template, populate it with actual data from a JSON object, and export the result as a print-ready PDF file.
Note: For this example, we’ll use Document .Net by SautinSoft, a library specifically designed to handle document manipulation in .NET without requiring MS Office or Adobe dependencies.
The Concept:
- The Template: A standard
.docxfile created in Microsoft Word. We use merge fields (e.g.,«Title»,«Description») where the data should go. - The Data: A JSON object containing the dynamic information (e.g., from your database or API).
- The Engine: A .NET library that merges the two and saves the result as a PDF.
Step 1: The JSON Payload
Imagine our backend just processed an order. We have a JSON object representing the invoice:
[ { "Title":"Australian Mist", "Description":"The Australian Mist (formerly known as the Spotted Mist) is a breed of cat developed in Australia.", "PictUrl":"australian-mist.jpg", "Weight": { "Item1":8, "Item2":15 } }, { "Title":"Maine Coon", "Description":"The Maine Coon is a large domesticated cat breed. It has a distinctive physical appearance and valuable hunting skills.", "PictUrl":"maine-coon.png", "Weight": { "Item1":13,"Item2":18 } }, { "Title":"Scottish Fold", "Description":"The original Scottish Fold was a white barn cat named Susie, who was found at a farm near Coupar Angus in Perthshire, Scotland, in 1961.", "PictUrl":"scottish-fold.jpg", "Weight": { "Item1":9, "Item2":13 } }, ] Step 2: The C# Implementation
First, ensure you have the library installed (via NuGet or direct reference).
Here is the core logic to load the template, map the JSON data, and generate the PDF. We’ll use System.Text.Json for deserialization.
using System; using System.Collections.Generic; using System.Text.Json; using SautinSoft.Document; // The document processing library public class InvoiceGenerator { public static void GeneratePdfReport(string json) { // Get data from json. var cats = JsonConvert.DeserializeObject<List<CatBreed>>(json); // Load the template document. string templatePath = @"..\..\..\cats-template.docx"; DocumentCore dc = DocumentCore.Load(templatePath); // To be able to mail merge from your own data source, it must be wrapped into an object that implements the IMailMergeDataSource interface. CustomMailMergeDataSource customDataSource = new CustomMailMergeDataSource(cats); // Decorate each cat beed by by appropriate picture. // Set picture width to 80 mm, height to Auto. dc.MailMerge.FieldMerging += (senderFM, eFM) => { // Insert an icon before the product name if (eFM.RangeName == "CatBreed" && eFM.FieldName == "PictUrl") { eFM.Inlines.Clear(); string pictPath = eFM.Value.ToString(); Picture pict = new Picture(dc, pictPath); double kWH = 1f; double desiredWidthMm = 80; if (pict.Layout.Size.Width > 0 && pict.Layout.Size.Height > 0) kWH = pict.Layout.Size.Width / pict.Layout.Size.Height; pict.Layout = new InlineLayout(new Size(desiredWidthMm, desiredWidthMm / kWH, LengthUnit.Millimeter)); eFM.Inlines.Add(pict); eFM.Cancel = false; } }; // Execute the mail merge. dc.MailMerge.Execute(customDataSource); string resultPath = "CatBreeds.pdf"; // Save the output to file PdfSaveOptions so = new PdfSaveOptions() { Compliance = PdfCompliance.PDF_A1a }; dc.Save(resultPath, so); // Open the result for demonstration purposes. System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(resultPath) { UseShellExecute = true }); } } In this way, document creation need not become a "bottleneck" in your .NET application. By separating the layout (.docx) from the logic (JSON + C#), you gain a robust, scalable, and easily maintainable solution. If you would like to delve deeper into the implementation process-including how to handle dynamic images (such as company logos) and complex nested tables - take a look at the detailed, step-by-step guide in the SautinSoft developer documentation.
This demonstrates how the system will work. If you need more, please note that implementing this code example requires a test file and some images; I have included a link below where you can obtain these materials.


