Skip to main content

Basics

Passing Data

Pass your data to the template as a Map<String, Object>. You can use nested maps and classes in any combination:

import com.docstencil.core.api.OfficeTemplate

data class Address(val street: String, val city: String)
data class Person(val name: String, val address: Address)

fun main() {
val template = OfficeTemplate.fromFile("Template.docx")

val data = mapOf(
"person" to Person(
name = "Alice",
address = Address(street = "123 Main St", city = "Springfield")
)
)
val result = template.render(data)

result.writeToFile("Output.docx")
}

Property Access

Use dot notation to access nested properties in your data:

WTemplate.docx
{person.address.street}

DocStencil resolves properties uniformly regardless of how your data is structured. All of these work the same way:

  • Maps: mapOf("person" to mapOf("address" to mapOf("street" to "123 Main St")))
  • Data classes: A Person data class with an address property
  • Java records: A Person record with an address() accessor
  • POJOs with getters: A Person class with getAddress() returning an object with getStreet()

For Loops

Use {for ... in ...}...{end} to repeat content for each item in an Iterable.

WTemplate_Paragraphs.docx
{for section in report.sections}

{section.title}

{section.text}
{end}

for loops also work in tables, where the loop repeats the entire table row:

WTemplate_Table.docx

Invoice #1234

DescriptionAmount
{for pos in invoice.positions}{pos.description}{pos.amount}{end}

Conditionals

Use {if ...}...{end} to show content only when a condition is true. You can use comparison operators (==, !=, <, >, <=, >=) and logical operators (and, or, !).

WTemplate.docx
{if invoice.subtotal >= 100 and !user.registered}

Use code COUPON10 to get 10% off your next order.

{end}

if statements can also be used inside table rows to conditionally render rows the same way for loops can be used there.

Builtin Functions

DocStencil provides several builtin helper functions. All builtin functions are prefixed with $.

Formatting Dates & Numbers

Use $format to format dates using Java's DateTimeFormatter patterns:

WTemplate.docx

Invoice Date: {$format(invoice.date, "MMMM dd, yyyy")}

You can use the same $format function to format numbers using Java's DecimalFormat patterns:

WTemplate.docx

Average: {$formatNumber(stats.average, "0.0")} points

Common patterns:

  • "0.0": one decimal place (e.g., 3.1)
  • "0.00": two decimal places (e.g., 3.14)
  • "#,##0.00": thousands separator with two decimals (e.g., 1,234.56)

Enumerating Items

Use $enumerate to get index and position information while iterating. Each item is wrapped with the following properties:

  • value: the original item
  • index: zero-based position (0, 1, 2, ...)
  • isFirst / isLast: boolean flags for first and last items
  • isEven / isOdd: boolean flags based on index
WTemplate.docx

Invoice #1234

#DescriptionAmount
{for pos in $enumerate(invoice.positions)}{pos.index + 1}{pos.value.description}{pos.value.amount}{end}

Putting It All Together

Here's a complete invoice template that combines property access, loops, conditionals, and formatting:

WInvoice_Template.docx

INVOICE

From: {company.name}

{company.address}

Bill To: {customer.name}

{customer.address}

Invoice Date: {$formatDate(invoice.date, "MMMM dd, yyyy")}
Invoice #: {invoice.number}

#DescriptionQtyUnit PriceAmount
{for item in $enumerate(invoice.items)}{item.index + 1}{item.value.description}{item.value.quantity}{$formatNumber(item.value.unitPrice, "0.00")}{$formatNumber(item.value.quantity * item.value.unitPrice, "0.00")}{end}

Subtotal: {$formatNumber(invoice.subtotal, "#,##0.00")}
{if invoice.discount > 0}Discount: -{$formatNumber(invoice.discount, "#,##0.00")}
{end} Total: {$formatNumber(invoice.total, "#,##0.00")}

{if invoice.notes}Notes: {invoice.notes}{end}

val data = mapOf(
"company" to mapOf(
"name" to "Acme Corp",
"address" to "123 Business Ave, Suite 100"
),
"customer" to mapOf(
"name" to "John Smith",
"address" to "456 Customer Lane"
),
"invoice" to mapOf(
"number" to "INV-2024-001",
"date" to LocalDate.of(2024, 3, 15),
"items" to listOf(
mapOf("description" to "Web Development", "quantity" to 40, "unitPrice" to 150.00),
mapOf("description" to "UI Design", "quantity" to 20, "unitPrice" to 125.00),
mapOf("description" to "Hosting (Annual)", "quantity" to 1, "unitPrice" to 299.99)
),
"subtotal" to 8799.99,
"discount" to 500.00,
"total" to 8299.99,
"notes" to "Payment due within 30 days. Thank you for your business!"
)
)
WInvoice_Output.docx

INVOICE

From: Acme Corp
123 Business Ave, Suite 100

Bill To: John Smith
456 Customer Lane

Invoice Date: March 15, 2024
Invoice #: INV-2024-001

#DescriptionQtyUnit PriceAmount
1Web Development40$150.00$6,000.00
2UI Design20$125.00$2,500.00
3Hosting (Annual)1$299.99$299.99

Subtotal: $8,799.99
Discount: -$500.00
Total: $8,299.99

Notes: Payment due within 30 days. Thank you for your business!