Files
wehub-resource-sync 8cb1f9f479
Publish SDK (PyPI) / publish (push) Has been cancelled
Publish SDK (npm) / publish (@aionui/officecli-sdk) (push) Has been cancelled
SDK smoke / smoke (windows-latest) (push) Has been cancelled
Publish SDK (npm) / publish (@officecli/officecli-sdk) (push) Has been cancelled
Publish SDK (npm) / publish (@officecli/sdk) (push) Has been cancelled
Publish SDK (npm) / publish (officecli-sdk) (push) Has been cancelled
SDK smoke / smoke (macos-latest) (push) Has been cancelled
SDK smoke / smoke (ubuntu-latest) (push) Has been cancelled
Skill parity / diff (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:09:29 +08:00

208 lines
8.5 KiB
Markdown

# Field & Table-of-Contents Showcase
Exercises the docx `field` and `toc` element surface — Word's computed values
(page numbers, dates, cross-references, conditionals) and the automatic table of
contents that indexes the document's headings. Three files work together:
- **fields.py** — builds the doc via the **officecli Python SDK**.
- **fields.docx** — the generated document.
- **fields.md** — this file.
## What a field is
A Word FIELD is a *complex field*: a run sequence of field characters —
`begin` / `instrText` (the code) / `separate` / `result` (the cached value) /
`end`. officecli addresses the whole thing as one node at `/field[N]`:
```bash
officecli add file.docx /body --type field --prop fieldType=date --prop format="yyyy-MM-dd"
officecli get file.docx /field[1] # → instruction=DATE \@ "yyyy-MM-dd" fieldType=date
officecli query file.docx field # list every field
```
Addressing `/body/p[N]/r[M]` returns the inner fieldChar *run*, not the field —
use `/field[N]`.
> **Fields show their cached result until Word updates them (F9).** officecli
> writes the field **code** correctly; the live value is computed by Word on
> open, or when you select the field and press **F9** / *Update
> Field*. That is why a freshly built `PAGE` reads back `"1"`, a `TOC` reads back
> `"Update field to see table of contents"`, and a `TITLE` reads back empty. The
> codes are what matter — the values are Word's job.
Built on the [`officecli-sdk`](../../sdk/python) (one resident, writes shipped
over the pipe); falls back to the in-repo SDK copy when the package isn't
pip-installed.
## Regenerate
```bash
cd examples/word
pip install officecli-sdk
python3 fields.py
# → fields.docx
```
The CLI twin, `fields.sh`, builds the same document with `officecli` directly.
## Field codes demonstrated
Every field is added with `--type field`. The typed `fieldType` shortcut builds
the instruction for you; `instruction` lets you write any raw code.
| Field | How it's built | Instruction produced |
|---|---|---|
| **DATE** | `fieldType=date format="yyyy-MM-dd"` | `DATE \@ "yyyy-MM-dd"` |
| **TIME** | `fieldType=time format="HH:mm"` | `TIME \@ "HH:mm"` |
| **REF** | `fieldType=ref bookmarkName=IntroSection hyperlink=true` | `REF IntroSection \h` |
| **IF** | `fieldType=if expression='1 = 1' trueText=… falseText=…` | `IF 1 = 1 "…" "…"` |
| **HYPERLINK** | `instruction=' HYPERLINK "https://example.com" \o "…" '` | `HYPERLINK "…" \o "…"` |
| **TITLE** | `fieldType=title` | `TITLE` |
| **PAGE** (locked) | `fieldType=page fldLock=true` | `PAGE` (won't recalc on F9) |
| **NUMPAGES** | `fieldType=page` / `fieldType=numpages` (in footer) | `PAGE`, `NUMPAGES` |
### Picture switches (`format`)
`format` is a **bare** picture string — the handler wraps it into the OOXML
`\@ "..."` switch. Do **not** pass the `\@` yourself:
```bash
officecli add file.docx /body --type field --prop fieldType=date --prop format="yyyy-MM-dd"
officecli add file.docx /body --type field --prop fieldType=time --prop format="HH:mm"
# numeric fields take a numeric picture, e.g. format="0.00%"
```
### Cross-reference (REF) to a bookmark
A REF field points at a bookmark by name; `hyperlink=true` appends the `\h`
switch so the inserted reference is a clickable link to the target. Bookmark
first, then reference it:
```bash
officecli add file.docx /body --type bookmark --prop name=IntroSection --prop text="Introduction"
officecli add file.docx /body --type field --prop fieldType=ref \
--prop bookmarkName=IntroSection --prop hyperlink=true
```
`bookmarkName` is an alias of `name`; the same `name` prop feeds `mergefield`
(field name), `styleref` (style name) and `docproperty` (property name).
### Conditional (IF) field
`expression`, `trueText`, and `falseText` fold into the instruction — they are
**Add/Set-only** and surface back inside `instruction`, not as their own Format
keys:
```bash
officecli add file.docx /body --type field --prop fieldType=if \
--prop expression='1 = 1' \
--prop trueText="Condition is TRUE" --prop falseText="Condition is FALSE"
# → IF 1 = 1 "Condition is TRUE" "Condition is FALSE"
```
### Raw instruction (arbitrary codes)
For codes without a typed shortcut (e.g. HYPERLINK's URL form), pass the whole
instruction:
```bash
officecli add file.docx /body --type field \
--prop instruction=' HYPERLINK "https://example.com" \o "Visit example.com" '
```
### Locked fields (`fldLock`)
`fldLock=true` rides on the begin fldChar; Word then does **not** update the
field on F9 / recalc — the cached result stays put:
```bash
officecli add file.docx /body --type field --prop fieldType=page --prop fldLock=true
```
> **Round-trip note:** `fldLock=true` is consumed by Add, **persists in the
> OOXML** (`w:fldChar/@w:fldLock="true"`), and is **surfaced on `get`** —
> `get`/`query` of the locked field reports `fldLock=true`. The key is emitted
> only when the field is locked; an unlocked field has no `fldLock` key.
## Composite footer — "Page X of Y"
A single `add` supports at most one text + one field. For a composite footer
(two fields + literal text) create the footer first, then Add the fields and the
joining run to its paragraph one by one:
```bash
officecli add file.docx / --type footer --prop text="Page " --prop align=center
officecli add file.docx "/footer[1]/p[1]" --type field --prop fieldType=page
officecli add file.docx "/footer[1]/p[1]" --type run --prop text=" of "
officecli add file.docx "/footer[1]/p[1]" --type field --prop fieldType=numpages
```
## Table of contents (`toc`)
A TOC is itself a complex field (`TOC \o "1-3" \h \u`), but it has its own
element type with friendly props.
> **A TOC collects paragraphs by _outline level_, which comes from their
> paragraph style.** A blank document has no `Heading1`/`Heading2` styles, so
> tagging paragraphs `style=Heading1` alone leaves the TOC **empty** ("no
> entries found"). Define the built-in heading styles first, each with an
> explicit `outlineLvl` (`0` = Heading 1):
>
> ```bash
> officecli add file.docx /styles --type style \
> --prop id=Heading1 --prop name="heading 1" --prop type=paragraph --prop outlineLvl=0
> officecli add file.docx /styles --type style \
> --prop id=Heading2 --prop name="heading 2" --prop type=paragraph --prop outlineLvl=1
> ```
Then add `Heading1`/`Heading2` paragraphs and insert the TOC:
```bash
officecli add file.docx /body --type toc \
--prop title="Contents" --prop levels=1-3 \
--prop hyperlinks=true --prop pageNumbers=true
officecli get file.docx /toc[1]
# → levels=1-3 hyperlinks=true pageNumbers=true title=Contents
```
| Prop | Meaning | Switch |
|---|---|---|
| `levels` | heading range indexed, e.g. `1-3` | `\o "1-3"` |
| `hyperlinks` | entries are clickable links | `\h` |
| `pageNumbers` | include trailing page numbers | (drops `\n` when true) |
| `title` | optional caption above the TOC | — |
> Word **rebuilds the rendered entries on open**. This example sets document
> `updateFields=true` (`officecli set file.docx / --prop updateFields=true`), so
> Word recomputes the TOC — and every other field — the moment the document
> opens: the entries fill in with real page numbers and dot leaders, no manual
> F9 needed. officecli's own `get`/`query` still report the write-time cache
> (the TOC reads back its "Update field to see..." placeholder) — that is
> expected; the field *codes* are correct and Word renders the live values.
Note the TOC is also enumerated by `query field` (it *is* a field) — in this
document it is `/field[1]`, so the first typed field (DATE) is `/field[2]`.
## Complete feature coverage
| Element | Keys | Notes |
|---|---|---|
| `field` | `fieldType`, `format`, `instruction`, `name`/`bookmarkName`, `expression`, `trueText`, `falseText`, `hyperlink`, `fldLock`, `id`, `vertAlign` | `fieldType` values: page, numpages, date, time, ref, if, title, mergefield, seq, styleref, docproperty, … (`officecli help docx field`) |
| `toc` | `levels`, `title`, `hyperlinks`, `pageNumbers` | `officecli help docx toc` |
## Set → Get round-trip
The scripts end by retargeting the DATE field's picture switch, then reading
fields and the TOC back:
```
/field[2]: date instruction=DATE \@ "dddd, MMMM d, yyyy"
/field[4]: ref instruction=REF IntroSection \h
/field[5]: if instruction=IF 1 = 1 "Condition is TRUE" "Condition is FALSE"
/toc[1]: toc levels=1-3 hyperlinks=true pageNumbers=true title=Contents
```
`fieldType`, `instruction` (with all its switches and IF true/false text), and
every TOC prop round-trip. `fldLock` persists in the OOXML and is surfaced on
`get` (`fldLock=true`, only when the field is locked — see above).