Skip to content

Template with a Header

A letterhead template is the common case for document.OpenTemplate: the header carries the logo and address block, the footer carries the small print, and your program supplies the letter. The catch is that clearing the body also clears the section properties that point at the header, so the header part survives the open but stops being referenced. You have to attach it again before saving.

Reattaching the header

doc, err := document.OpenTemplate("letter_template.docx")
if err != nil {
    return err
}
defer doc.Close()

if len(doc.Headers()) > 0 {
    doc.BodySection().SetHeader(doc.Headers()[0], wml.ST_HdrFtrDefault)
}
if len(doc.Footers()) > 0 {
    doc.BodySection().SetFooter(doc.Footers()[0], wml.ST_HdrFtrDefault)
}

Headers() returns the header parts in the order they were stored in the file, which is not a meaningful order. A template with one header is unambiguous; a template with a first-page header and a different default one is not, and there is nothing on Header that tells you which is which. Attaching the wrong one is silent.

The type argument decides where the header appears: wml.ST_HdrFtrDefault for ordinary pages, wml.ST_HdrFtrFirst for the first page of the section, and wml.ST_HdrFtrEven for even pages. ST_HdrFtrEven also needs even and odd headers enabled in the document settings before Word will honor it, which Odd and Even Headers covers.

Generating one document per record

The example loads a JSON file of letters and calls generateDoc for each. Each call reopens the template, so every letter starts from a clean copy. That matters: a Document is mutable and there is no reset, so reusing one across records would append the second letter under the first. Reopening a small template per record is cheap next to the cost of writing the file.

Body text is styled by looking for the “Normal” style in doc.Styles.Styles() and passing it to para.SetStyle, which keeps the generated paragraphs in the template’s font rather than the library default. SetStyle wants the style ID rather than the display name; “Normal” is one of the cases where the two are identical, so passing the name works here and would not for a style like “Heading 1”. doc.Styles.SearchStyleByName is the reliable way to go from a display name to the ID.

Limitations

SetHeader appends a reference to the section rather than replacing one, so calling it twice with the same type leaves two references in the section properties. Attach each type once.

If the header cannot be resolved to a relationship ID, SetHeader logs a debug message and writes an empty reference instead of returning an error. Enable the library logger while developing against an unfamiliar template, or the header simply will not be there.

OpenTemplate also drops page size, orientation and margins along with the rest of the section properties. A letterhead built on non-default margins needs those set again through doc.BodySection().

The example writes into ./output/. SaveToFile does not create directories, so a missing output folder surfaces as a failure to generate the document.

Run the example

loadLetters reads data/letters.json into a slice of letter values, and generateDoc renders one .docx per recipient into output/, named after the receiver.

git clone https://github.com/unidoc/unioffice-examples.git
cd unioffice-examples/document/template-with-header
go run main.go

If this is your first time using UniOffice, follow the getting started guide to create an API key and set up your development environment.

View the full source
Last updated on