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

131 lines
6.6 KiB
Markdown

# Nested Elements — Building, Navigating & Fully Exercising a pptx Table
The other `tables-*` examples showcase *styling*. This one teaches what a flat
property example can't, and the schema doesn't spell out — **how to build a
nested element level by level, address a deep node by path, and exercise the
full property surface of every level.** A pptx table is the vehicle: the deepest
common pptx tree, `slide → table → tr → tc`.
Four files: **tables-nested.sh** (CLI — this walkthrough explains it),
**tables-nested.py** (the SDK twin, one `doc.send()` per command),
**tables-nested.pptx** (4-slide result), **tables-nested.md** (this file).
## The two things to learn
### 1. Path addressing — element name ≠ path token
A child is reached by extending its parent's path, but the **path token differs
from the element name**:
| Element (in `help`) | Path token | Example path |
|---|---|---|
| `table` | `table` | `/slide[1]/table[1]` |
| `table-row` | **`tr`** | `/slide[1]/table[1]/tr[2]` |
| `table-cell` | **`tc`** | `/slide[1]/table[1]/tr[2]/tc[3]` |
So row 2 / column 3 is `/slide[1]/table[1]/tr[2]/tc[3]`. Indices are 1-based;
`last()` works (`…/tr[last()]`).
### 2. Property ownership — which level owns which property
| Level | Path | Owns |
|---|---|---|
| **table** | `/slide[1]/table[1]` | structure (`rows`, `cols`, `colWidths`, `data`) + table-wide style (`style`, `firstRow/lastRow/firstCol/lastCol`, `bandedRows/bandedCols`, `headerFill`, `bodyFill`, `border.*` incl. `horizontal`/`vertical`, `rowHeight`, `name`, `zorder`) |
| **row** (`tr`) | `…/tr[R]` | `height` (and `cols`) |
| **cell** (`tc`) | `…/tr[R]/tc[C]` | the **box** (`fill`, `opacity`, `bevel`, `image`, all `border.*` incl. diagonals `tl2br`/`tr2bl`, `padding`/`padding.bottom`, `valign`, `wrap`, `textdirection`, `direction`, `colspan`, `merge.right`, `merge.down`) **and** the **text** (`text`, `font`, `size`, `bold`, `italic`, `underline`, `strike`, `color`, `align`, `linespacing`, `spacebefore`, `spaceafter`) |
> pptx **flattens text onto the cell** — no separate run level inside a table
> cell, so `bold`/`color`/`align` go straight on the `tc`. (docx tables nest one
> deeper: `tc → paragraph → run`.)
## The 4 slides (full property surface, spread out)
One table can't show 30+ cell properties legibly, so the surface is split across
slides — **100% of the settable props on `table` / `table-row` / `table-cell`**:
| Slide | Teaches |
|---|---|
| **1 · Structure & ownership** | levels, path tokens, property ownership, `colspan`, and the **navigation** pass (`get`/`set` a deep cell after building) |
| **2 · Table level** | `data=` bulk fill, `headerFill`/`bodyFill`, `firstRow/lastRow/firstCol/lastCol`, `bandedRows/bandedCols`, every `border.*` edge, `rowHeight`/`colWidths`, `name`, `zorder` |
| **3 · Cell box** | all `border.*` (per-side, full, diagonals), `padding`/`padding.bottom`, `valign`, `wrap`, `textdirection`, `direction`, `bevel`, `opacity`, image fill, `merge.right`/`merge.down` |
| **4 · Cell text** | `font`, `size`, `bold`, `italic`, `underline`, `strike`, `color`, `align`, `linespacing`, `spacebefore`, `spaceafter` |
> `id` is intentionally **not** demonstrated: table ids are auto-assigned and
> must stay unique, so hardcoding one risks a collision. It's settable for
> round-trip fidelity, never something to set by hand.
## Build it, level by level (slide 1)
```bash
officecli add deck.pptx / --type slide # blank pptx has no slides
# Level 1 — the table; rows/cols/colWidths are add-time structure → /slide[1]/table[1]
officecli add deck.pptx /slide[1] --type table --prop rows=5 --prop cols=3 \
--prop x=2.5cm --prop y=2.4cm --prop width=28cm --prop height=9cm --prop colWidths=12cm,8cm,8cm
officecli set deck.pptx /slide[1]/table[1] --prop style=medium2-accent1 \
--prop firstRow=true --prop bandedRows=true # ← table owns style + banding
# Level 2 — a row owns only its height
officecli set deck.pptx /slide[1]/table[1]/tr[1] --prop height=2cm
# Level 3 — a cell owns box + text together
officecli set deck.pptx /slide[1]/table[1]/tr[1]/tc[1] \
--prop text=Region --prop bold=true --prop color=FFFFFF \
--prop align=center --prop valign=middle --prop fill=1F6FEB
```
`style` accepts `medium1..4` / `light1..3` / `dark1..2` / `none`, optionally
`-accentN` (e.g. `medium2-accent1`).
## Nesting-only operations
These exist *only* because cells sit in a grid — no flat property has an
equivalent. They consume a neighbour, so place them where the swallowed cell is
intentionally blank:
```bash
officecli set deck.pptx /slide[1]/table[1]/tr[5]/tc[1] --prop colspan=3 --prop text="TOTAL …" # span 3 cols (alias: gridspan)
officecli set deck.pptx /slide[3]/table[1]/tr[4]/tc[4] --prop merge.down=1 --prop text="↓" # swallow the cell below
officecli set deck.pptx /slide[3]/table[1]/tr[5]/tc[1] --prop merge.right=2 --prop text="→" # swallow the cell to the right
```
`colspan` is the canonical key (matches docx, round-trips on `get`); `gridspan`
is accepted as an input alias.
## Add-time vs settable
A few `table` props are **add-only** — pass them on `add`, not `set`:
`id` (auto-assigned identity), `headerFill`/`bodyFill` (creation-time bulk fills),
and the structural `cols`, `rows`, `data`. (`data` defines the grid, so it's
mutually exclusive with `rows`/`cols`.) Everything else — including `colWidths`,
`rowHeight`, and `zorder` — is settable on the live table too.
## Navigate — address a deep node *after* building
The same path that built a cell reaches it later:
```bash
officecli get deck.pptx /slide[1]/table[1]/tr[4]/tc[3] # read the East/Revenue cell
officecli set deck.pptx /slide[1]/table[1]/tr[4]/tc[3] --prop fill=FFF2CC # re-style in place (amber highlight)
officecli query deck.pptx "tc" # or query every cell
```
## Regenerate
```bash
cd examples/ppt/tables
bash tables-nested.sh # via the CLI
# — or —
pip install officecli-sdk # the SDK (officecli binary still required)
python3 tables-nested.py # via the SDK, same result
# → tables-nested.pptx (4 slides)
```
## Why this matters (vs flat property examples)
`presentation-settings` and friends set flat `key=value` on one container. Real
documents are **trees** — the hard part isn't "what properties exist" (the schema
lists those), it's **where each property lives and how to reach a node three
levels down**. Apply the same level-by-level + navigate + full-surface pattern to
docx tables (`tr → tc → paragraph → run`) and xlsx charts (`chart → series + axis`).