Generate a PDF from a DOCX Template in C#

This guide explains how to create a PDF document by merging data into a Word template.

General Principles

Word templating consists of the following elements:

  1. A DOCX file that will be used as the template.

  2. A template model that contains the placeholder values to replace in the DOCX template.

  3. Configuration for the template/model.

Template Model

The template model can be an external JSON file or created programmatically. It must contain:

  1. A configuration containing a start and end delimiter.

  2. At least one placeholder-value pair. The placeholder name must correspond to the placeholder defined in the DOCX template.

Classes, Methods, and Properties

Populating a Word Template

PSPDFKit GdPicture.NET supports replacing placeholder text strings, loops, and dynamic tables. As the amount of content changes, the DOCX will seamlessly adjust and reflow across pages as needed. Placeholders maintain their initial formatting from the DOCX template.

Generating a PDF from a Word Template in C#

The first step is to prepare the DOCX template with placeholders. This example uses {{ and }} as the delimiters to define the boundaries of placeholders.

Word Template with placeholders

Load the DOCX template:

GdPictureOfficeTemplater templater = new GdPictureOfficeTemplater();
templater.LoadFromFile(@"C:\template.docx");

Now, prepare the data and configuration. For this example, use the following data model:

{
	"config": {
		"delimiter": {
			"start": "{{",
			"end": "}}"
		}
	},
	"model": {
		"name": "Petey Eff",
		"text": "Hello World!",
		"amount": "$420.69"
	}
}

The data model can be defined in a JSON file or loaded programmatically. The following snippet demonstrates how to define the model in a JSON file:

GdPictureStatus status = templater.SetTemplate(System.IO.File.ReadAllText("Model.json"));

The following snippet demonstrates how to define the model programmatically:

GdPictureOfficeTemplateConfiguration configuration = new GdPictureOfficeTemplateConfiguration()
{
    Delimiter = new GdPictureOfficeTemplateDelimiter
    {
        Start = "{{",
        End = "}}"
    }
};

GdPictureOfficeTemplate template = GdPictureOfficeDefaultTemplateBuilder.CreateTemplate()
                .AddPlaceholderReplacement(placeholder: "name", replacementValue: "Petey Eff")
                .AddPlaceholderReplacement(placeholder: "text", replacementValue: "Hello World!")
                .AddPlaceholderReplacement(placeholder: "amount", replacementValue: "$420.69");


GdPictureStatus status = templater.SetTemplate(template, configuration);

Next, replace the placeholders in the DOCX template with the data and save the result to a DOCX file:

GdPictureStatus status = templater.Process();

if (status == GdPictureStatus.OK)
{
    status = templater.SaveToFile(@"C:\result.docx");
}

The following image shows what the DOCX file looks like.

Word Template populated with JSON data and saved to DOCX

Lastly, convert the DOCX file into a PDF. The following example converts it to PDF/A using the PDF/A-1a PdfConformance level. However, you can choose whichever conformance enumeration you prefer:

using (GdPictureDocumentConverter oConverter = new GdPictureDocumentConverter())
{
    GdPictureStatus status = oConverter.LoadFromFile(@"C:\result.docx", GdPicture14.DocumentFormat.DocumentFormatDOCX);
    if (status == GdPictureStatus.OK)
    {
        status = oConverter.SaveAsPDF("output.pdf", PdfConformance.PDF_A_1a);
    }
}

Using Loops

To create a loop, add an array of objects to the data model. For every object within the array, the loop’s body is replicated during each cycle of the loop when the DOCX and data model are processed. Here’s an example of such a JSON file:

{
	"config": {
		"delimiter": {
			"start": "{{",
			"end": "}}"
		}
	},
	"model": {
		"loop1": [
			{ "loopDesc1": "Monday", "loopDesc2": "Tuesday" },
			{ "loopDesc1": "Wednesday", "loopDesc2": "Thursday" },
			{ "loopDesc1": "Friday", "loopDesc2": "Saturday" }
		],
		"loop2": [
			{ "loopDesc": "Red" },
			{ "loopDesc": "Orange" },
			{ "loopDesc": "Green" }
		]
	}
}

Below is an example of such a DOCX template.

Word Template with placeholders for creating loops

For bulleted or numbered lists, the opening placeholder must immediately precede the start of the list, and the closing placeholder must immediately follow the end of the list.

Combining the JSON with the DOCX produces the following output.

Word Template with loops that is populated with JSON data

For an example of combining loops with tables, refer to the Dynamic Table Loop Example below.

Using Conditionals

Conditionals allow a template to dynamically insert and format data based on conditions defined within the template itself. Conditionals can be combined with loops for more complex dynamic documents.

The template syntax for implementing conditionals is straightforward. It uses special markers to denote the beginning and end of a conditional block, as well as to indicate the condition being tested:

  • #condition — Marks the beginning of a block that should be rendered if the condition is true.

  • ^condition — Marks the beginning of a block that should be rendered if the condition is false (the else part).

  • /condition — Marks the end of a conditional block.

Here’s a simple example showing this syntax for an isBlue condition:

{{#isBlue}}Blue{{/isBlue}}{{^isBlue}}Red{{/isBlue}}
{
	"config": {
		"delimiter": {
			"start": "{{",
			"end": "}}"
		}
	},
	"model": {
		"isBlue": true
	}
}

Here’s a more complex example with conditionals.

Word Template with placeholders for using conditionals

It’s possible to combine the template above with the following JSON file:

{
	"config": {
		"delimiter": {
			"start": "{{",
			"end": "}}"
		}
	},
	"model": {
		"isConsulting": false,
		"consultingField": "N/A",
		"scopeExhibit": "Exhibit A",
		"startDate": "March 1, 2024",
		"endDate": "February 28, 2025",
		"autoRenew": true
	}
}

In this case, the output will be the following DOCX, which can then be converted to PDF.

Word Template with placeholders for using conditionals

Dynamic Table Loop Example

This example shows three different approaches to using loops for dynamically inserting rows into a table. You can download the DOCX template used in this example.

Generating a PDF from a Word Template in C#

Option 1 — Using a Model Loaded from a JSON File

In this first approach, you’ll populate the DOCX template with data from a JSON file. You can download the JSON file used in this example:

using (GdPictureOfficeTemplater templater = new GdPictureOfficeTemplater())
{
    GdPictureStatus status = templater.SetTemplate(System.IO.File.ReadAllText("table.json"));

    if (status == GdPictureStatus.OK)
    {
        status = templater.LoadFromFile("table.docx");

        if (status == GdPictureStatus.OK)
        {
            status = templater.Process();

            if (status == GdPictureStatus.OK)
            {
                // Replace placeholders with actual values.
                status = templater.SaveToFile("result.docx");

                if (status == GdPictureStatus.OK)
                {
                    // Create PDF document.
                    using (GdPictureDocumentConverter oConverter = new GdPictureDocumentConverter())
                    {
                        status = oConverter.LoadFromFile("result.docx", GdPicture14.DocumentFormat.DocumentFormatDOCX);

                        if (status == GdPictureStatus.OK)
                        {
                            status = oConverter.SaveAsPDF("output.pdf", PdfConformance.PDF_A_1a);
                        }
                    }
                }
            }
        }
    }
}

Option 2 — Creating a Model Programmatically

In this second approach, you’ll populate the DOCX template by programmatically creating the model.

First, create the template model:

static GdPictureOfficeTemplate CreateTemplate()
{
    return GdPictureOfficeDefaultTemplateBuilder.CreateTemplate()
        .AddSection<GdPictureOfficeTemplate>(sectionName: "products",
            product1 =>
            {
                product1
                    .AddPlaceholderReplacement("product_name", "T-shirt (white)")
                    .AddPlaceholderReplacement("qty", "1")
                    .AddPlaceholderReplacement("cost", "$48.00");
            },
            product2 =>
            {
                product2
                    .AddPlaceholderReplacement("product_name", "Pants")
                    .AddPlaceholderReplacement("qty", "1")
                    .AddPlaceholderReplacement("cost", "$85.00");
            },
            product3 =>
            {
                product3
                    .AddPlaceholderReplacement("product_name", "Leather Jacket (red))")
                    .AddPlaceholderReplacement("qty", "1")
                    .AddPlaceholderReplacement("cost", "$478.00");
            },
            product4 =>
            {
                product4
                    .AddPlaceholderReplacement("product_name", "Fedora")
                    .AddPlaceholderReplacement("qty", "1")
                    .AddPlaceholderReplacement("cost", "$79.00");
            },
            product5 =>
            {
                product5
                    .AddPlaceholderReplacement("product_name", "Glove")
                    .AddPlaceholderReplacement("qty", "1")
                    .AddPlaceholderReplacement("cost", "$34.00");
            })
        .AddPlaceholderReplacement(placeholder: "subtotal", replacementValue: "$724.00")
        .AddPlaceholderReplacement(placeholder: "tax", replacementValue: "$72.40")
        .AddPlaceholderReplacement(placeholder: "total", replacementValue: "$796.40");
}

Next, set the configuration for the model:

static GdPictureOfficeTemplateConfiguration GetConfiguration()
{
    return new GdPictureOfficeTemplateConfiguration()
    {
        Delimiter = new GdPictureOfficeTemplateDelimiter
        {
            Start = "{{",
            End = "}}"
        }
    };
}

Now, process the file, replacing the placeholders with actual values and creating a PDF:

using (GdPictureOfficeTemplater templater = new GdPictureOfficeTemplater())
{
    GdPictureStatus status = templater.SetTemplate(CreateTemplate(), GetConfiguration());

    if (status == GdPictureStatus.OK)
    {
        status = templater.LoadFromFile("table.docx");

        if (status == GdPictureStatus.OK)
        {
            // Replace placeholders with actual values.
            status = templater.Process();

            if (status == GdPictureStatus.OK)
            {
                status = templater.SaveToFile("result.docx");

                if (status == GdPictureStatus.OK)
                {
                    // Create PDF document.
                    using (GdPictureDocumentConverter oConverter = new GdPictureDocumentConverter())
                    {
                        status = oConverter.LoadFromFile("result.docx", GdPicture14.DocumentFormat.DocumentFormatDOCX);

                        if (status == GdPictureStatus.OK)
                        {
                            status = oConverter.SaveAsPDF("output.pdf", PdfConformance.PDF_A_1a);
                        }
                    }
                }
            }
        }
    }
}

Option 3 — Creating a Model Programmatically Using a Custom Builder

In this third approach, you’ll use a custom builder to populate the DOCX template.

First, create the custom builder:

class TableIntelligentLoopTemplate : GdPictureOfficeTemplateBuilder<TableIntelligentLoopTemplate>
{
    public TableIntelligentLoopTemplate AddProductName(string value)
    {
        return AddPlaceholderReplacement("product_name", value);
    }

    public TableIntelligentLoopTemplate AddQty(string value)
    {
        return AddPlaceholderReplacement("qty", value);
    }

    public TableIntelligentLoopTemplate AddCost(string value)
    {
        return AddPlaceholderReplacement("cost", value);
    }
}

Next, create the template/model using the custom builder:

static GdPictureOfficeTemplate CreateTemplateUsingCustomBuilder()
{
    return GdPictureOfficeDefaultTemplateBuilder.CreateTemplate()
        .AddSection<TableIntelligentLoopTemplate>(sectionName: "products",
            product1 =>
            {
                product1
                    .AddProductName("T-shirt (white)")
                    .AddQty("1")
                    .AddCost("$48.00");
            },
            product2 =>
            {
                product2
                    .AddProductName("Pants")
                    .AddQty("1")
                    .AddCost("$85.00");
            },
            product3 =>
            {
                product3
                    .AddProductName("Leather Jacket (red)")
                    .AddQty("1")
                    .AddCost("$478.00");
            },
            product4 =>
            {
                product4
                    .AddProductName("Fedora")
                    .AddQty("1")
                    .AddCost("$79.00");
            },
            product5 =>
            {
                product5
                    .AddProductName("Glove")
                    .AddQty("1")
                    .AddCost("$34.00");
            })
        .AddPlaceholderReplacement(placeholder: "subtotal", replacementValue: "$724.00")
        .AddPlaceholderReplacement(placeholder: "tax", replacementValue: "$72.40")
        .AddPlaceholderReplacement(placeholder: "total", replacementValue: "$796.40");
}

Set the configuration for the model:

static GdPictureOfficeTemplateConfiguration GetConfiguration()
{
    return new GdPictureOfficeTemplateConfiguration()
    {
        Delimiter = new GdPictureOfficeTemplateDelimiter
        {
            Start = "{{",
            End = "}}"
        }
    };
}

Now, process the file, replacing the placeholders with actual values and creating a PDF:

using (GdPictureOfficeTemplater templater = new GdPictureOfficeTemplater())
{
    GdPictureStatus status = templater.SetTemplate(CreateTemplateUsingCustomBuilder(), GetConfiguration());

    if (status == GdPictureStatus.OK)
    {
        status = templater.LoadFromFile("table.docx");

        if (status == GdPictureStatus.OK)
        {
            // Replace placeholders with actual values.
            status = templater.Process();

            if (status == GdPictureStatus.OK)
            {
                status = templater.SaveToFile("result.docx");

                if (status == GdPictureStatus.OK)
                {
                    // Create PDF document.
                    using (GdPictureDocumentConverter oConverter = new GdPictureDocumentConverter())
                    {
                        status = oConverter.LoadFromFile("result.docx", GdPicture14.DocumentFormat.DocumentFormatDOCX);

                        if (status == GdPictureStatus.OK)
                        {
                            status = oConverter.SaveAsPDF("output.pdf", PdfConformance.PDF_A_1a);
                        }
                    }
                }
            }
        }
    }
}

Automatic Reflow Example

Text reflow refers to the automatic adjustment of text within a document or display area to ensure it fits within the given space without overflowing or leaving excessive empty space. This process involves moving words and characters to new lines, pages, or columns as needed based on the available width and height of the display area, the font size, and other formatting settings.

Word Template with text reflowing

Here’s the JSON you’ll use:

{
	"config": {
		"delimiter": {
			"start": "{{",
			"end": "}}"
		}
	},
	"model": {
		"clauses": [
			{
				"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
			},
			{
				"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
			},
			{
				"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
			},
			{
				"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
			},
			{
				"clause": "Lorem ipsum dolor sit amet, consectetur adipiscing elit..."
			}
		],
		"title": "Purchase and Sale Agreement"
	}
}

The example below shows how to generate a PDF from a DOCX template where the content is reflowed into another column by using a data model loaded from a JSON file. For an example of how to create the model programmatically, refer to the Dynamic Table Loop Example above:

using (GdPictureOfficeTemplater templater = new GdPictureOfficeTemplater())
{
    GdPictureStatus status = templater.SetTemplate(System.IO.File.ReadAllText("model.json"));

    if (status == GdPictureStatus.OK)
    {
        status = templater.LoadFromFile("template.docx");

        if (status == GdPictureStatus.OK)
        {
            status = templater.Process();

            if (status == GdPictureStatus.OK)
            {
                // Replace placeholders with actual values.
                status = templater.SaveToFile("result.docx");

                if (status == GdPictureStatus.OK)
                {
                    // Create PDF document.
                    using (GdPictureDocumentConverter oConverter = new GdPictureDocumentConverter())
                    {
                        status = oConverter.LoadFromFile("result.docx", GdPicture14.DocumentFormat.DocumentFormatDOCX);

                        if (status == GdPictureStatus.OK)
                        {
                            status = oConverter.SaveAsPDF("output.pdf", PdfConformance.PDF_A_1a);
                        }
                    }
                }
            }
        }
    }
}