Fill a Template from Data
Once a template has the layouts you need, producing a deck per customer, region
or period is a loop: unmarshal the data, clear the slides, add the layouts you
want, write the values into the placeholders and save under a name derived from
the record. The example does this with a sales report, generating one .pptx
per area from a JSON payload.
The API is the same as Use a Template. What is worth saying here is how the loop is arranged, because the presentation object is reused across records rather than reopened.
The loop
ppt, err := presentation.OpenTemplate("template.pptx")
if err != nil {
log.Fatalf("unable to open template: %s", err)
}
defer ppt.Close()
for _, data := range saleData {
for _, s := range ppt.Slides() {
if err := ppt.RemoveSlide(s); err != nil {
log.Fatalf("error removing slide: %s", err)
}
}
// add slides from layouts and fill their placeholders
if err := ppt.SaveToFile(fmt.Sprintf("%s.pptx", data.Area)); err != nil {
log.Fatalf("error saving presentation: %s", err)
}
}The clear-then-build step at the top of each iteration is what makes one template serve every record. On the first pass it removes the template’s own example slides; on later passes it removes the slides built for the previous record. Skip it and each file accumulates every record that came before.
SaveToFile can be called repeatedly on the same presentation. It writes the
current state and leaves the object usable, so there is no need to reopen the
template between records.
Writing values in
ph, err := sld.GetPlaceholder(pml.ST_PlaceholderTypeTitle)
if err != nil {
log.Fatalf("error getting placeholder type title: %s", err)
}
ph.SetText(fmt.Sprintf("Sale Data For: %s", data.Area))
ph, err = sld.GetPlaceholderByIndex(1)
if err != nil {
log.Fatalf("error getting placeholder by index: %s", err)
}
ph.ClearAll()
para := ph.AddParagraph()
para.AddRun().SetText(fmt.Sprintf("Number of Customers: %d", data.Customers))SetText suits a title: one paragraph, one run, no formatting decisions. A body
placeholder holding several lines wants ClearAll followed by one
AddParagraph per line, since each paragraph becomes its own bullet under the
layout’s list style.
Every placeholder lookup here checks its error. In a generator that runs unattended over data you have not seen, a layout that lacks the placeholder you expected is the failure you want reported rather than a nil dereference two lines later.
Limitations
Slides() builds a new slice on every call, so ranging over it while
RemoveSlide shortens the presentation’s own list is safe. Hold on to the
result across an add or a remove and it goes stale instead: the values in it
still point at real slides, but the positions they were taken from have moved.
Output filenames come straight from the data in the example, which is fine for a fixed payload and unsafe for anything user-supplied. Sanitize the value before using it as a path.
Placeholder indices are per layout. GetPlaceholderByIndex(1) finds the content
placeholder in this template’s “Title and Content” layout; another template can
number them differently. If you are targeting templates you do not control,
iterate PlaceHolders() and match on Type() instead.
Run the example
The example parses an embedded JSON document with three sales records and writes
Michigan.pptx, Cincinnati.pptx and Washington.pptx, each with a caption
slide and a content slide. It prints the template’s layouts first.
git clone https://github.com/unidoc/unioffice-examples.git
cd unioffice-examples/presentation/use-template-sales
go run main.goIf this is your first time using UniOffice, follow the getting started guide to create an API key and set up your development environment.