6.6 KiB
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/aligngo straight on thetc. (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 |
idis 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)
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:
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:
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
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).