Merge tag reference

Every merge tag you can use in an uploaded Word template: answers, submission and linked-record details, conditions, repeating rows, photos, and all the modifiers.

This page lists the tags you can put in a Word template for a form or a workpack. How to upload the template and check it is in Upload a Word template.

Word templates use {$field_name}. The in-app editor uses a different style, ${field_name} (see Customize print formatting); the two can't be mixed in one file.

How a broken tag prints

A tag never stops a PDF being made.

  • A tag that can't be read, uses a modifier that doesn't exist, has a modifier setting that can't be read or is in curled quotes, is missing its $, or is an {if} or {foreach} without its close prints exactly as it is written in the Word file.
  • An {if} condition that can't be read counts as false: its text is left out, and the {else} text prints instead.
  • A tag naming something the submission doesn't have prints empty.
  • Only a damaged Word file, or the conversion service failing, stops the PDF.

The tag check after each upload tells you which tags will print as written.

Write a tag

A tag starts with { followed straight away by a character ({ site}, with a space, is ordinary text) and ends at the matching }. A tag with no closing } is left as ordinary text.

{$site_location}
{$crew[0].name}
{$textarea-6c5}
  • The $ is required. {site_location} prints as written.
  • Use the field's Name from the form builder, not its label. Automatic names with hyphens, such as textarea-6c5, work.
  • Reach inside an answer with dots ({$worker.name}) or a row number in brackets ({$crew[0].name}).
  • A table's rows are a list. Each column is read by its Name (see Name a table column). A column without a Name is read by its header in lower case, with underscores for spaces: Used for whole shift becomes used_for_whole_shift, and a header starting with a digit gets an underscore in front (1st Shift becomes _1st_shift). Two headers that come out the same get _2, _3 and so on.
  • Tags use the form as it is now. A submission made before you renamed a field or column prints under its current Name, so one template prints old and new submissions alike.
  • Inside a tag, straight " and ' hold text, so a } inside straight quotes doesn't end the tag (inside curly quotes it does). Word turns quotes curly as you type. Curly quotes work in an {if} condition, but not in a modifier setting: {$id|pad:5:”0”} prints as written. Turn off smart quotes in Word before you type tags.
  • Keep each tag in one style. A tag with part bold or in another font is stored in pieces and may print in fragments.

Change how a value prints

Add a modifier after a |. Settings follow :, and can be quoted text, numbers, true, false or another $ value. Modifiers run left to right.

{$docket_no|pad:5:"0"}
{$name|trim|upper|truncate:20}
{$amount|currency}

Modifier names are case-sensitive: {$name|Upper} prints as written and the tag check reports UNKNOWN_MODIFIER. All the modifiers are listed under Modifiers.

Submission details

TagPrints
{$id}The submission ID
{$title}The form's title
{$submission_id}, {$form_title}, {$submitted_by}, {$version}The submission ID, the form's title, who submitted it and the version
{$submission_time}, {$submission_datetime}The time, or date and time, submitted
{$date} or {$submission_date}The date submitted, ready to print
{$submitted_at}The date and time submitted, for use with date_format: {$submitted_at|date_format:"%d/%m/%Y"}
{$project_name}, {$project_number}The linked project's name and code
{$client_name}, {$client_number}The linked client's name and number
{$workpack_name}, {$workpack_number}The linked workpack's name and document number

{$date} and {$submission_date} are already formatted, so date_format returns nothing for them; use {$submitted_at} to format the date your way. Date answers inside tables are kept in a form date_format can read. Don't give a form field any of these names, such as id, date, title or submitted_at.

The project, client and workpack come from the workpack the submission was filled in on, or else from a Project or Client field at the top level of the form. A Project field also fills in its client, so you only need a Client field when the form's client isn't the project's. A Project or Client field that allows several choices uses the first one. A field inside a table doesn't count, because it holds one value per row; reach those values with {foreach}. When nothing is linked, these print -.

Details of a linked record

A Resource field's own tag prints the option the person picked: {$siteProject} gives "Harbour Bridge". For the rest of the record, start with linked, then the field's name, then the detail:

{$linked.siteProject.name}
{$linked.siteProject.number}
{$linked.siteProject.external_id}
{$linked.siteProject.client.name}
  • Every record has display (what the picker showed), name, id and external_id.
  • A single-choice Projects field also has number (the project code) and client with its name and number. That is the project's own client, which isn't always the form's when the form has a Client field as well. A single-choice Clients field has number.
  • A multiple-choice field gives a list, so repeat over it: {foreach $linked.sites as $item}{$item.name}{/foreach}. List items have only display, name, external_id and id.

Available variables → Linked records lists these for your form. A form field actually named linked keeps its own tag, and the linked details aren't added for it.

Show text only when a condition is met

{if $hours > 0}
  Hours: {$hours}
{elseif $hours == 0}
  No hours
{else}
  Not recorded
{/if}
  • Compare with >, <, >=, <=, ==, !=, === and !==, and combine with && (and) and || (or).
  • A value counts as empty when it's missing, false, 0, blank or an empty list. Anything else counts as set, including the words "false" and "no".
  • Checkboxes, switches and ticked table cells arrive as Yes or empty, never No. Test them with {if $field}, not {if $field == "No"}.
  • Numbers typed as text compare as numbers, so {if $hours > 0} is true for an answer of "30".
  • Other text compares exactly, capitals included: {if $status == "Open"} doesn't match open. Put the text in quotes, either straight ones or Word's curly ones ({if $status == “Open”}), but not one of each.
  • Curly and straight apostrophes inside the text count as the same, so {if $q == "Don’t know"} matches Don't know whichever apostrophe Word or a phone typed. Put text that contains an apostrophe in double quotes.

A condition Pay can't read counts as false, so its {else} text prints, and the tag check reports BAD_CONDITION: The condition could not be read, so it always counts as false. The usual causes are text without quotes ({if $status == Open}), a single = instead of ==, a field name without $, and a modifier inside the condition ({if $status|lower == "open"}).

What a false condition removes

An {if} can start in one paragraph, table cell or row and end in another, with other text beside its tags. A branch that isn't used takes everything between its tag and the next one, in reading order: the rest of the paragraph or cell after the tag, any paragraphs, rows and tables in between, and the start of the paragraph or cell that holds the next tag.

  • Text outside the tags stays where it is. Label {if $x}A on one line and {else}B{/if} on the next prints Label A when $x is set, or Label and B on their own lines when it isn't.
  • A table cell between the tags is emptied, not removed, so the table keeps its columns. A cell always keeps at least one line.
  • That lets one condition fill a tick column: put {if $q == "Yes"}✓ in the YES cell and {else}✓{/if} in the NO cell. With no {else} and nothing printed, the cells between are emptied and the row keeps its question. A false {tableif} still removes its whole row.
  • A line left with no text is removed, and so is a table row with no text in any cell, and a table with no rows left. Pictures, icons, text boxes, symbols, page breaks, equations and page numbers count as content and stay: a logo beside a company name that a false {if} removes keeps its place, and a text box whose only text was conditional stays as an empty box.
  • The same holds in a table inside a cell: a false {tableif} removes the inner row, and an inner table left with no rows goes, while its cell keeps a line.
  • A bookmark, comment or field such as a page number that a false branch cuts through keeps working.

Compare a choice answer

A Select, Radio, Multi select or Toggle answer prints as the option's label, and a condition compares that label exactly. Take a Radio field named q1EntrancesSecured with the options Yes, No and N/A. Put one of these in each of the YES, NO and N/A cells of its table row, and the tick lands in the column that matches the answer:

{if $q1EntrancesSecured == "Yes"}✓{else}-{/if}
{if $q1EntrancesSecured == "No"}✓{else}-{/if}
{if $q1EntrancesSecured == "N/A"}✓{else}-{/if}

{if $q1EntrancesSecured == "yes"} never matches, because the answer is the label Yes.

To compare the value stored behind the label instead (the option's Value in the form builder), use values. {$values.q1EntrancesSecured} gives yes, no or na:

{if $values.q1EntrancesSecured == "na"}✓{else}-{/if}
  • A Multi select, a Toggle set to multiple choices, or a field with Allow multiple responses on gives a list of stored values, and a list never equals one piece of text. Loop over it and test each value: {foreach $values.hazards as $value}{if $value == "hot"}✓{/if}{/foreach}. A Multi select's own tag prints the chosen labels separated by commas.
  • An Other answer gives the Other option's stored value. What the person typed prints with the field's own tag, as Other: hail.
  • In a table row, a choice column's stored value is {$row.values.<column>}, beside {$row.<column>}.
  • A form field actually named values keeps its own tag, and values isn't added for it.

Available variables → Form fields shows the labels each choice field prints, and its stored-value tag. Preview answers each choice field with its first option, so in the grid above every row previews with its tick under YES.

When a condition compares a choice field with text that isn't one of its labels (or, under values, one of its stored values), the tag check warns you with CHOICE_VALUE_MISMATCH, lists the options and suggests the one to use. It checks against the form as it is now, after upload, when you open the template again, and under the preview. It covers Select, Radio and single-choice Toggle fields, their table columns inside a {foreach} over the table, and sub-table columns inside a loop over the sub-table. Answers that are lists aren't checked.

Repeat for each item in a list

{foreach $crew as $row}
  {$row.worker}
{foreachelse}
  No crew listed
{/foreach}

{foreach from=$crew item=row} works the same. Add a key with {foreach $crew as $key => $row} or key=key. {foreachelse} prints when the list is empty.

Inside the loop, {$row@index} (counting from 0), {$row@iteration} (from 1), {$row@first} and {$row@last} tell you where you are.

Each column tag uses the column's Name, or its header in lower case with underscores (see Write a tag). Available variables lists a table's column tags, ready to copy.

A table inside a table repeats the same way:

{foreach $worker_time as $row}
  {$row.worker}, {$row.role}
  {foreach $row.equipment as $eq}
    {$eq.asset} ({$eq.hours})
  {/foreach}
{/foreach}

Available variables lists a sub-table as a loop inside its table's row, then its columns, and the hint names every loop the tag has to sit inside, for example Inside {foreach $worker_time as $row}{foreach $row.equipment as $item}. The list calls the rows $row, then $item, then $sub; any name works, as long as the tags inside use the same one.

To sort first, add a modifier to the list: {foreach $crew|sort:"worker_name" as $row}. A modifier on a list that doesn't exist makes the loop print nothing, and the tag check reports UNKNOWN_FOREACH_MODIFIER.

Name a table column

A column without a Name takes its tag from its header, so correcting a header changes the tag your template needs. Give the column a Name, and its tag stays the same whatever you do to the header.

  1. Hover the table and select its settings button. Customize field attributes opens.
  2. Under Table Columns, select the settings icon beside the column.
  3. Type the tag in Name. Under the box you see the tag the column prints with, such as {$row.crew_member}.
  4. Select Save, then Save the form.

A Name is used exactly as you type it, capitals included: CrewMember fills {$row.CrewMember}, not {$row.crewmember}. Use letters, numbers and underscores, and start with a letter or an underscore. Leave Name blank to use the header; the empty box shows that tag.

Pay won't keep a Name that another column in the table already uses, whatever its capitals, and it refuses files and values, because a row uses them for photos and stored values. The reason shows under Name, for example Column "Hours" already uses "hours". Choose a different name. If you select Save anyway, Fix the name of column "Worker" first. appears and the dialog stays open.

In a table with more than one photo column, a column named photo prints its first photo with {$row.files.photo} and every photo with {$row.files.photo_all}. So no other photo column can be named photo_all, and the message reads Photo column "Photo" already prints photos under "files.photo_all". Choose a different name.

If your template stopped printing a column after its header changed, type the old tag into the column's Name: it prints again for old and new submissions.

Repeat a table row

Put {foreach} in the first cell of a Word table row and {/foreach} in its last cell. The whole row is copied once per item. A loop outside a table repeats the text or paragraph it wraps.

  • In a table inside a cell, put both tags in the inner table to repeat its rows, or one tag in each table to repeat the outer row.
  • A loop inside a row loop, with both its tags in that row, repeats the row once per inner item.
  • A line that ends one loop and starts the next ({/foreach}{foreach $plant as $p}) is split in two, so each loop repeats its own lines. Text between the two tags prints once, on its own line.
  • A loop written inside one line repeats its text, line breaks, tabs and symbols, but leaves out any picture inside it, and a text box inside it loses its box. To repeat a picture or a text box, put the loop around whole paragraphs or a table row.

Text boxes

Tags work on every line of a text box: values, conditions, loops and images. A {foreach} that opens in the body and closes inside a text box on that line prints as written (UNBALANCED_BLOCK). Put both tags inside the box, or both outside it.

Table shortcuts

{tablerow} works like a table-row {foreach} ({tablerow from=$products item=_p} works too), and {tableif} removes the whole row when its condition isn't met:

{tablerow $products as $_p}
  {tableif $_p.Type == 'shirt'}
    {$_p.Name} {$_p.Quantity} {$_p.Price}
  {/tableif}
{/tablerow}

To drop rows with no value, use {tableif $Product1}. empty() isn't supported: {tableif !empty($Product1)} doesn't work.

Photos, signatures and drawings

Signature and Draw on image answers are pictures. Print them with insert_image and a width and height in pixels:

{$supervisor_signature|insert_image:200:80}

Without insert_image, the tag prints nothing and the tag check reports IMAGE_WITHOUT_INSERT_IMAGE.

Size a picture

A 0 in place of a number means "work it out from the picture".

You writeWhat prints
insert_image:200:80Exactly 200 × 80 pixels. Giving both numbers doesn't keep the picture's proportions, so a picture of a different shape is stretched to fit
insert_image:300:0300 pixels wide, as tall as the picture's proportions make it
insert_image:0:150150 pixels tall, as wide as the picture's proportions make it
insert_image or insert_image:0:0The picture's own size, shrunk to fit the page's margins if it's bigger. A smaller picture isn't enlarged

For photos, give a width and a 0 height, such as insert_image:300:0. Photos come in both landscape and portrait, and the 0 keeps each one the right shape. Photos taken on a phone print the right way up.

Photos and files

A photo or file upload tag prints the file name: {$sitePhoto} gives IMG_2841.jpg. For the picture, use files:

You wantTag
The file name{$sitePhoto}
The photo{$files.sitePhoto|insert_image:300:0}
A photo column in a table row{$row.files.photo|insert_image:200:0}
Every photo, when an answer has several{foreach $files.sitePhoto_all as $photo}{$photo|insert_image:300:0}{/foreach}
  • Only images get a files entry. Other attachments print their file name only.
  • {$files.<field>} is the first image. {$files.<field>_all} exists only when the answer has more than one; in a table row it's {$row.files.<column>_all}.
  • A form field actually named files keeps its own tag, and files isn't added for it.
  • Photos are shrunk for print. The originals on the submission are unchanged.

An image from an https:// web address works too: {$logo_url|insert_image:250:50}. If an image can't be loaded, the tag prints as written and the tag check warns you.

Everything between {literal} and {/literal} prints exactly as written, tags included. Use it when the PDF must show a tag, or a { that would otherwise start one:

{literal}{$this_is_not_a_tag}{/literal}

{assign}, {math}, {include}, barcodes and QR codes aren't supported and print as written.

Modifiers

These are all the modifiers there are. Any other name used as a modifier prints the whole tag as written (UNKNOWN_MODIFIER).

Text

ModifierSettingsExampleResult
upper, lower{$name|upper}ALEX MORGAN
ucfirst{$name|ucfirst}First letter in capitals
ucwords, capitalizecapitalize also takes: capitalize words that contain digits (false), lower-case the rest of each word first (false){$site|capitalize}Harbour Bridge Maintenance
trimCharacters to remove (optional, ranges like 0..9){$code|trim:"0..9"}Spaces, or the listed characters, removed from both ends
stripReplacement (default a space){$notes|strip}Runs of spaces and line breaks collapsed
truncateLength (80), ending (...), break words (false), cut the middle (false){$notes|truncate:40:"…"}Cut to 40 characters, ending included, at a word break unless break words is true. A length of 0 prints nothing
wordwrapWidth (80), line break, cut long words (false){$notes|wordwrap:40}Lines wrapped at 40 characters. Long words stay whole unless cut is true
indentCharacters (4), fill (space){$notes|indent:4}Every line indented
catText or values to add on{$first|cat:" "|cat:$last}Alex Morgan
replaceFind, replace with (nothing){$name|replace:"Inc":"Pty Ltd"}Literal find and replace
regex_replaceA /pattern/flags and what to replace it with (nothing){$id|regex_replace:"/[^0-9]/":""}Only the digits. A pattern that isn't valid leaves the text unchanged
substrStart, length (negative counts from the end){$id|substr:-4}Last four characters
spacifySpacer (space){$code|spacify:"-"}A-B-C
string_formatA format such as %d, %s, %f or %04d{$qty|string_format:"%04d"}0042. A missing or invalid format prints the value unchanged
defaultOne or more fallbacks{$nickname|default:"Not provided"}The first fallback that isn't empty, when the value is empty
nl2brSelf-closing line breaks (true){$notes|nl2br}Line breaks kept
escapeType (html), double-encode (true){$url|escape:"url"}Encoded text. Types: html, htmlall, url, urlpathinfo, quotes, hex, hexentity, decentity, javascript, mail, nonstd; any other type leaves the text unchanged
unescapeType (html){$html|unescape}Decoded text. Types: html, htmlall, entity, url
strip_tagsReplace with a space (true){$html|strip_tags}HTML tags removed
count_charactersInclude spaces (false){$notes|count_characters}A number
count_words, count_sentences, count_paragraphs{$notes|count_words}A number. Sentences end in ., ? or !; paragraphs are separated by line breaks

Numbers, dates and ticks

ModifierSettingsExampleResult
number_formatDecimals (0), decimal point (.), thousands (,){$hours|number_format:2}1,234.50. Halves round away from zero; text that isn't a number prints 0
currencyAs number_format, 2 decimals by default{$amount|currency}$1,234.50, and -$1,234.50 for a negative amount. Always a $ sign
padWidth, padding character (space), added on the left{$docket_no|pad:5:"0"}00042. No change when the value is already as wide
phone_formatPattern ((%3) %3-%4); each %N takes the next N digits{$phone|phone_format:"%2 %4 %4"}02 9876 5432. Too few digits prints the number unchanged
date_formatFormat (%b %e, %Y), fallback date{$submitted_at|date_format:"%d/%m/%Y"}27/09/2026. An empty or zero date prints nothing
age{$date_of_birth|age}Whole years to today. A future date gives 0; an unreadable one prints nothing
checkbox{$ppe_ok|checkbox}☑ when set, ☐ when not
checkmark{$ppe_ok|checkmark}✓ when set, nothing when not
insert_imageWidth, height in pixels; 0 works it out from the picture (see Size a picture){$signature|insert_image:200:80}The picture, from a signature, drawing, files photo or https:// address

date_format takes % codes (%d, %m, %Y, %b, %e, %H, %M) or letter codes (d/m/Y, j M Y), and shows times in your organization's time zone. "now" as the fallback prints the time the PDF was made.

Lists

ModifierSettingsExample
sortColumn (optional), asc (default) or desc{foreach $crew|sort:"worker_name":"desc" as $row}
multisortPairs of column and direction{foreach $crew|multisort:"role":"asc":"worker_name":"asc" as $row}

Both keep items that compare equal in their original order, and leave anything that isn't a list unchanged.

Turn off smart quotes in Word

Word replaces the straight quotes you type with curly ones, and a modifier setting in curly quotes prints as written. Turn the replacement off before you type tags:

  1. In Word, select File > Options > Proofing, then AutoCorrect Options.
  2. On the AutoFormat As You Type tab, under Replace as you type, clear "Straight quotes" with “smart quotes”.
  3. Select OK.

In Word for Mac the same check box is under Word > Preferences > AutoCorrect > AutoFormat As You Type.

This only changes quotes you type from then on. To straighten the quotes already in the template, leave the check box cleared, select Replace on the Home tab, type " in both Find what and Replace with, and select Replace All. Then do the same with '. This straightens every quote and apostrophe in the document, not only the ones in tags.