Fill a Word template in C# without Office

Somebody made invoice.docx in Word with {{customer}} where the name goes. This guide shows how to fill it from C#: text, repeating table rows and images, and how to check the result.

byte[] template = await HtmlToDocxConverter.ConvertAsync("<p>Customer: {{customer}}</p>");
byte[] filled = DocxEditor.ReplaceText(template, new Dictionary<string, string>
{
    ["{{customer}}"] = "Contoso Ltd",
});

Placeholders that Word splits

  • Word often splits a placeholder such as {{customer}} into three or four separate <w:t> elements, for example after a typo fix or a spell check.
  • A find-and-replace over the XML then finds nothing and reports success.
  • ReplaceText splices the pieces back together before it matches.
  • ReplaceText also reaches headers and footers, so a name in a letterhead is replaced too.
  • A key with no placeholder leaves the document unchanged. A placeholder with no key stays as literal text, such as {{unknown}}.

One row per record

  • Put one template row in the table and FillRows clones it once for each record.
  • Each new row keeps the template row's borders, shading, fonts and column widths.
  • Expand rows first, then fill single values. FillRows clones the row, so a placeholder already inside it is copied into every line.
  • The collection name in the placeholder, {{item.Desc}}, matches the collection argument, "item".

Swap in an image

  • ReplaceImage swaps a text placeholder for image bytes.
  • Sizes are in points. Give one size and the other scales to keep the aspect ratio.
  • The format is decided by the bytes, never by a filename. A file called logo.png that holds JPEG bytes is read as the JPEG it is.

Check the fill

  • ExtractText returns the document's text as a string, so a test can assert that the fill worked.
  • By default it leaves out headers and footers. Pass includeHeadersAndFooters: true to include them.

What it will not do

  • Per-section headers and odd/even (mirrored) pages are not supported. A distinct first page is.
  • DocxBlock has four factories, Heading, Paragraph, Table and Image. It is deliberately not a document model; for real layout control, use a template.
  • PDF fonts depend on the machine doing the conversion. With no fonts installed, the PDF falls back to the base-14 standard fonts. Both render, but they are not byte-identical.