#!/bin/bash # fields.sh — exercise the docx `field` and `toc` element surface # (schemas/help/docx/field.json + toc.json) using the officecli CLI directly. # # A Word FIELD is a complex fldChar run (begin / instrText / separate / result # / end) addressed as a whole at /field[N]. This builds a small report with a # title, a table of contents, several Heading1/Heading2 sections, page-number # fields in the footer, a cross-reference (REF) to a bookmark, and one of every # field-code family the handler exposes as a typed shortcut. CLI twin of # fields.py (officecli SDK); both produce an equivalent fields.docx. # # IMPORTANT — fields carry only their CACHED result until Word updates them. # officecli writes the field CODES correctly; Word computes the # live values on open, or when you press F9 / Update Field. This example sets # document updateFields=true, so Word recomputes everything on open — the TOC # fills in with real page numbers and dot leaders, PAGE/NUMPAGES resolve, etc. # officecli's own `get`/`query` still report the write-time cache (e.g. the TOC # reads back the "Update field to see..." placeholder) — that is expected; the # field CODES are what matter and Word renders the live values. # NOTE: intentionally NO `set -e`. Like the SDK twin's doc.batch, this script # tolerates forward-compat 'UNSUPPORTED props' warnings (officecli exit 2) and # keeps building so the full document is produced. FILE="$(dirname "$0")/fields.docx" echo "Building $FILE ..." rm -f "$FILE" officecli create "$FILE" officecli open "$FILE" # ============================================================ # Heading styles — REQUIRED for the TOC to collect entries. # A TOC field ( \o "1-3" ) gathers paragraphs by OUTLINE LEVEL, which comes # from the paragraph's style. A blank doc has no Heading1/Heading2 styles, so # `style=Heading1` alone leaves the TOC empty ("no entries found"). Define the # built-in heading styles with an explicit outlineLvl (0 = Heading 1) first. # Features: style element — id, built-in name, type=paragraph, outlineLvl. # ============================================================ officecli add "$FILE" /styles --type style \ --prop id=Heading1 --prop name="heading 1" --prop type=paragraph \ --prop outlineLvl=0 --prop bold=true --prop size=16 --prop color=1F3864 officecli add "$FILE" /styles --type style \ --prop id=Heading2 --prop name="heading 2" --prop type=paragraph \ --prop outlineLvl=1 --prop bold=true --prop size=13 --prop color=2E5496 # ============================================================ # Update fields on open — write so Word recomputes every # field (TOC / PAGE / NUMPAGES / REF) the moment the document opens, instead of # leaving the write-time cache (empty TOC placeholder, PAGE="1", etc.). # Features: document updateFields (settings-level). # ============================================================ officecli set "$FILE" / --prop updateFields=true # ============================================================ # Title # ============================================================ officecli add "$FILE" /body --type paragraph --prop text="Field & Table-of-Contents Showcase" --prop style=Title officecli add "$FILE" /body --type paragraph \ --prop text="Every field below shows its cached result until Word updates it (F9)." \ --prop italic=true --prop color=666666 # ============================================================ # Table of contents — references the Heading1/Heading2 paras below # Features: TOC field over heading levels 1-3, clickable, with page numbers, # captioned "Contents". Inserts a TOC complex field; Word rebuilds # the rendered entries on open. # ============================================================ officecli add "$FILE" /body --type toc \ --prop title="Contents" --prop levels=1-3 \ --prop hyperlinks=true --prop pageNumbers=true # ============================================================ # Section 1 — Introduction (bookmarked for the cross-reference below) # ============================================================ officecli add "$FILE" /body --type paragraph --prop text="1. Introduction" --prop style=Heading1 # Features: bookmark covering the section title so a REF field can point at it. officecli add "$FILE" /body --type bookmark --prop name=IntroSection --prop text="Introduction" officecli add "$FILE" /body --type paragraph \ --prop text="This report is generated by officecli. It demonstrates the full docx field surface: page numbering, dates, cross-references, conditional (IF) fields, hyperlinks, and an automatic table of contents." officecli add "$FILE" /body --type paragraph --prop text="1.1 Scope" --prop style=Heading2 officecli add "$FILE" /body --type paragraph \ --prop text="Fields are computed values Word maintains for you. officecli writes the field code; the value you see is a cached result until the next update." # ============================================================ # Section 2 — Date & time fields # ============================================================ officecli add "$FILE" /body --type paragraph --prop text="2. Date & Time Fields" --prop style=Heading1 officecli add "$FILE" /body --type paragraph --prop text="Report date (DATE, formatted yyyy-MM-dd):" # Features: DATE field with a picture switch. `format` is a bare picture string; # the handler wraps it into the OOXML \@ "..." switch automatically. officecli add "$FILE" /body --type field --prop fieldType=date --prop format="yyyy-MM-dd" officecli add "$FILE" /body --type paragraph --prop text="Generated at (TIME, formatted HH:mm):" # Features: TIME field with a time picture switch. officecli add "$FILE" /body --type field --prop fieldType=time --prop format="HH:mm" # ============================================================ # Section 3 — Cross-reference (REF) to the Introduction bookmark # ============================================================ officecli add "$FILE" /body --type paragraph --prop text="3. Cross-References" --prop style=Heading1 officecli add "$FILE" /body --type paragraph --prop text="See the section titled:" # Features: REF field pointing at the IntroSection bookmark, \h switch makes the # inserted reference a clickable hyperlink to the target. officecli add "$FILE" /body --type field --prop fieldType=ref --prop bookmarkName=IntroSection --prop hyperlink=true # ============================================================ # Section 4 — Conditional (IF) field # ============================================================ officecli add "$FILE" /body --type paragraph --prop text="4. Conditional Fields" --prop style=Heading1 officecli add "$FILE" /body --type paragraph --prop text="An IF field picks one of two texts from a logical expression:" # Features: IF field — expression + trueText/falseText. All three fold into the # instruction (IF "" ""); they are Add/Set-only and # read back inside `instruction`, not as their own Format keys. officecli add "$FILE" /body --type field \ --prop fieldType=if --prop expression='1 = 1' \ --prop trueText="Condition is TRUE" --prop falseText="Condition is FALSE" # ============================================================ # Section 5 — Hyperlink & document-property fields # ============================================================ officecli add "$FILE" /body --type paragraph --prop text="5. Hyperlink & Property Fields" --prop style=Heading1 officecli add "$FILE" /body --type paragraph --prop text="A HYPERLINK field (raw instruction — no typed shortcut for the URL form):" # Features: HYPERLINK field built from a raw instruction. `instruction` bypasses # the fieldType helpers for arbitrary codes the typed shortcuts miss. officecli add "$FILE" /body --type field --prop instruction=' HYPERLINK "https://example.com" \o "Visit example.com" ' officecli add "$FILE" /body --type paragraph --prop text="The document title, pulled from file metadata (TITLE field):" # Features: TITLE field — a document-property field. Reads back the doc's title. officecli add "$FILE" /body --type field --prop fieldType=title # ============================================================ # Section 6 — A locked field (Word will NOT update it on F9) # ============================================================ officecli add "$FILE" /body --type paragraph --prop text="6. Locked Fields" --prop style=Heading1 officecli add "$FILE" /body --type paragraph --prop text="A locked PAGE field keeps its cached result even on Update Field:" # Features: PAGE field with fldLock=true — Word does NOT recalc it on F9; the # cached result stays put. fldLock persists in OOXML and is now # reported on get (fldLock=true, only when the field is locked). officecli add "$FILE" /body --type field --prop fieldType=page --prop fldLock=true # ============================================================ # Footer — composite "Page X of Y" (PAGE + literal + NUMPAGES) # Built in steps: create the footer with the literal lead-in, then Add the # fields and the joining run to its paragraph one by one. # ============================================================ # Features: footer paragraph carrying two page-number fields and literal text. officecli add "$FILE" / --type footer --prop text="Page " --prop align=center # Features: PAGE field inline in the footer paragraph. officecli add "$FILE" "/footer[1]/p[1]" --type field --prop fieldType=page # Features: literal " of " joining run. officecli add "$FILE" "/footer[1]/p[1]" --type run --prop text=" of " # Features: NUMPAGES field — the document's total page count. officecli add "$FILE" "/footer[1]/p[1]" --type field --prop fieldType=numpages # ============================================================ # Set after create — retarget the DATE field's format switch # ============================================================ # Features: Set on an existing field — swap the DATE picture to include the day # name. fieldType + format both round-trip on the field path. officecli set "$FILE" '/field[2]' --prop format="dddd, MMMM d, yyyy" officecli close "$FILE" officecli validate "$FILE" echo "" echo "Done. Output: $FILE" echo "" echo "Fields:" officecli query "$FILE" field echo "" echo "Table of contents:" officecli query "$FILE" toc