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

11 KiB
Raw Permalink Blame History

Textbox Showcase

Demonstrates 10 complex textbox scenarios built on the wps:wsp WordprocessingShape model (OOXML Drawing ML). The example is hybrid:

  • Scenarios 1, 4, 5, 6, 7, 8, 9, 10 use the high-level officecli add --type textbox command — fill, border, gradient, rotation, vertical text, geometry, corner radius, shadow, no-fill/no-line, wrap, positioning and z-order are --props on one add, and the inner text is formatted with set on the <textbox>/p[N] paths plus extra add … --type paragraph calls.
  • Scenarios 2 and 3 stay on officecli raw-set with pre-authored XML, because they exercise surface the high-level command does not expose (per-run mixed formatting inside one paragraph, and a nested table — see the per-scenario notes below).

Three files:

  • textbox.sh — builds the document (high-level add for all but 2/3, raw-set for 2/3).
  • textbox.py — SDK twin; ships the same items over one doc.batch(...) round-trip.
  • textbox.docx — generated output; open in Word to see each floating shape.
  • textbox.md — this file.

Regenerate

cd examples/word
bash textbox.sh          # or: python3 textbox.py
# → textbox.docx

The two insertion paths

High-level (1/4/5/7/9). One add creates the box and its first paragraph; the returned path (/body/textbox[N]) is then addressable for run formatting and more paragraphs:

TB=$(officecli add textbox.docx /body --type textbox \
  --prop text="Basic Textbox" --prop width=15cm --prop height=3.33cm \
  --prop fill=E6F3FF --prop line.color=0070C0 --prop line.width=2pt \
  --prop wrap=topAndBottom --prop textAnchor=top | grep -oE '/body/textbox\[[0-9]+\]')
officecli set textbox.docx "$TB/p[1]" --prop align=center --prop bold=true --prop color=0070C0 --prop size=14
officecli add textbox.docx "$TB" --type paragraph --prop text="… body text …"

Paragraph-level format keys are the bare forms (bold/italic/color/size/align) — each applies to every run in that paragraph. (For different formatting on different runs within one paragraph, use raw-set — see Scenario 2.)

Raw (2/3/6/8/10). The whole textbox paragraph is injected before the body sectPr:

officecli raw-set textbox.docx /document \
  --xpath "//w:body/w:sectPr" \
  --action insertbefore \
  --xml '<w:p> ... <mc:AlternateContent> ... </mc:AlternateContent> ... </w:p>'

The mc:AlternateContent wrapper follows the OOXML spec:

  • mc:Choice Requires="wps" — the modern wps:wsp WordprocessingShape (Word 2010+).
  • mc:Fallback — optional VML fallback for older renderers.

Each wps:wsp element has these children:

  • wps:cNvSpPr — marks it as a text box (txBox="1").
  • wps:spPr — geometry, fill, border, effects.
  • wps:txbxw:txbxContent — the actual paragraph/table content inside the box.
  • wps:bodyPr — text body layout: rotation, vertical flow, wrap, insets, anchor.

Scenario 1: Basic Textbox (Solid Fill + Border) — HIGH-LEVEL

A rectangle with a solid light-blue fill, a 2pt blue border, and top-and-bottom text wrapping.

Built with add --type textbox:

  • fill=E6F3FF (light blue fill)
  • line.color=0070C0 + line.width=2pt (blue border)
  • wrap=topAndBottom (body text flows above and below)
  • textAnchor=top; the centred bold-blue title is set on p[1], the body is a second add … --type paragraph.

Features: fill, line.color, line.width, wrap=topAndBottom, textAnchor, per-paragraph run formatting via set on the inner p[N].

The raw-XML original also carried a VML mc:Fallback for pre-2010 renderers; the high-level command does not emit one. If you need the legacy fallback, use raw-set (see the raw scenarios).

Scenario 2: Multi-Paragraph Rich Text Textbox

A taller box with a dashed orange border and rich mixed-format content: bold, italic, underline, strikethrough, color, highlight, and right-aligned text — all inside w:txbxContent.

Key wps:spPr attributes:

  • a:prstDash val="dash" on a:ln (dashed border)
  • Multiple paragraphs with varied w:rPr combinations inside w:txbxContent

Features: dashed border (a:prstDash val="dash|solid|dot|…"), multi-paragraph textbox content, mixed run formatting inside w:txbxContent (bold/italic/underline/strike/color/highlight)

Scenario 3: Textbox with Nested Table

A gray-bordered box containing a paragraph header followed by a w:tbl — demonstrating that w:txbxContent can hold any valid body content including tables.

Key structure inside w:txbxContent:

<w:p> ... heading ... </w:p>
<w:tbl>
  <w:tblPr><w:tblStyle w:val="TableGrid"/></w:tblPr>
  <w:tr> ... header cells with blue fill ... </w:tr>
  <w:tr> ... data cells ... </w:tr>
</w:tbl>

Features: w:tbl nested inside w:txbxContent (full table-in-textbox), w:tblStyle reference, per-cell w:shd fill

Scenario 4: Rotated Textbox (45 degrees + Gradient Fill) — HIGH-LEVEL

A box rotated 45° with a red-to-yellow gradient fill and centred white text.

Built with add --type textbox:

  • rotation=45 (degrees — the command converts to the a:xfrm rot 60000-per-degree units)
  • fill.gradient=FF6B6B,FFE66D — a comma-separated stop list. (Note: this is not the C1-C2:angle syntax used by chart fills; the dash form is rejected here.)
  • line.color=C0392B + line.width=1.5pt
  • textAnchor=center (text centred despite rotation), anchor.x=4.17cm + hRelative=column

Features: rotation, fill.gradient (comma stop list), line.color/line.width, textAnchor=center, anchor.x/hRelative

Scenario 5: Vertical Text Textbox — HIGH-LEVEL

A narrow tall box with East-Asian vertical text flow, where characters read top-to-bottom.

Built with add --type textbox:

  • textDirection=eaVert (alias: vert; emits wps:bodyPr vert="eaVert"; other values horz, vert, vert270, wordArtVert)
  • fill=FFF0F5, line.color=8B0000 + line.width=1pt; the bold dark-red text is set on p[1].

Features: textDirection=eaVert (vertical text orientation), fill, line.*

Scenario 6: Rounded Rectangle Textbox + Drop Shadow — HIGH-LEVEL

A rounded rectangle with a soft outer drop shadow.

Built with add --type textbox:

  • geometry=roundRect + cornerRadius=16667 (the adjust-handle guide value; 0-100 is read as a percent ×1000, a value >100 as a raw guide value)
  • shadow=true — emits the standard outer drop shadow (blur 50800 / dist 38100 / dir 5400000 / black / 40% alpha). A compact shadow=blur;dist;dir;color;alpha form is also accepted for a custom shadow.
  • fill=E8F5E9, line.color=2E7D32 + line.width=2.25pt, textAnchor=center; the three paragraphs (bold-green title / body / italic-grey note) are set on p[1..3].

Features: geometry=roundRect, cornerRadius (adjust handle), shadow, line.width, textAnchor

Scenario 7: Side-by-Side Textboxes (Dashboard Cards) — HIGH-LEVEL

Three rounded metric cards floating side-by-side, each with wrap=none so they don't push body text.

Built with three add --type textbox calls (one per card):

  • geometry=roundRect (card shape)
  • wrap=none (boxes float freely)
  • hRelative=column + anchor.x=0cm / 5.28cm / 10.56cm (horizontal offsets across the column)
  • each card's accent title / big number / grey label are set on p[1]/p[2]/p[3].

Features: geometry=roundRect, wrap=none, anchor.x/hRelative (horizontal positioning)

Known limitation: the high-level add places each textbox in its own host paragraph, so the three cards sit at a slight vertical stagger rather than a single shared baseline. The raw-XML original packed all three wp:anchor into one paragraph for a perfectly aligned row — if you need pixel-exact co-baseline cards, use raw-set.

Scenario 8: Borderless Transparent Textbox — HIGH-LEVEL

A completely invisible container — no fill, no border — so only the text shows (a watermark-style overlay).

Built with add --type textbox:

  • fill=none and line.color=none — both sentinels emit a:noFill (fill and outline respectively). (none/transparent were previously rejected by the color parser, so this box needed raw-set.)
  • hRelative=column + anchor.x=1.39cm, textAnchor=center; the single italic light-grey line is set on p[1].

Features: fill=none, line.color=none (fully borderless/transparent), inner italic run formatting

Scenario 9: Text Overflow Textbox — HIGH-LEVEL

A short fixed-height box holding six paragraphs — more text than fits — to show overflow clipping.

Built with add --type textbox:

  • height=1.67cm with autoFit omitted → the box stays a fixed height and clips overflow. (Passing autoFit=true would emit a:spAutoFit and grow the box to fit instead.)
  • textAnchor=top anchors content to the top so the overflow clips at the bottom; Line 1 is set bold-red, the rest are plain add … --type paragraph calls.

Features: fixed-height textbox (autoFit omitted), overflow clipping, textAnchor=top

Scenario 10: Textbox Z-Order Stacking (behindDoc) — HIGH-LEVEL

Two overlapping boxes demonstrating Z-order, built with two add --type textbox calls:

  • Bottom layer: behindDoc=true — sits behind the body text; relativeHeight=251670528.
  • Top layer: relativeHeight=251671552 (higher = front) + fill.opacity=80 — a translucent (80%) fill so the bottom box shows through the overlap.

Both use wrap=none with hRelative=column/anchor.x (and the top box vRelative=paragraph/anchor.y) to overlap.

Features: behindDoc (push behind body text), relativeHeight/zorder (stacking order; higher = front), fill.opacity (translucent fill), wrap=none + anchor.x/anchor.y overlap

Complete Feature Coverage

Feature Scenario
Solid fill (a:solidFill) 1, 3, 7, 8, 10
Gradient fill (a:gradFill/a:gsLst) 4
No fill (a:noFill) 8
Border width (a:ln w) + solid color 1, 2, 3, 6, 7
Dashed border (a:prstDash) 2
No border (a:ln/a:noFill) 8
Preset geometry: rect 1, 8, 9, 10
Preset geometry: roundRect + corner radius 6, 7
Shape rotation (a:xfrm rot) 4
Drop shadow (a:outerShdw) 6
Color transparency (a:alpha) 10
Vertical text (wps:bodyPr vert="eaVert") 5
Body text anchor (anchor="t"/"ctr") 1, 4, 6, 9
Text wrap: wp:wrapTopAndBottom 1, 2, 3, 4, 5, 6
Text wrap: wp:wrapNone (float freely) 7, 10 (bottom)
Z-order: relativeHeight, behindDoc 10
Horizontal positioning: wp:positionH/posOffset 7
Nested table in textbox 3
Rich mixed-format content (w:rPr variants) 2, 3
VML fallback (mc:Fallback / v:shape) — (raw-set only; high-level add does not emit one)
mc:AlternateContent/mc:Choice Requires="wps" 2, 3 (raw scenarios)
Build path high-level add: 1, 4, 5, 6, 7, 8, 9, 10 · raw-set: 2, 3

Inspect the Generated File

# View the document outline (headings and textbox scenario labels)
officecli view textbox.docx outline

# Query all drawing anchors (each textbox is a wp:anchor drawing)
officecli query textbox.docx drawing

# Validate the generated file
officecli validate textbox.docx