chore: import upstream snapshot with attribution
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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:09:29 +08:00
commit 8cb1f9f479
1189 changed files with 386007 additions and 0 deletions
Binary file not shown.
Binary file not shown.
+356
View File
@@ -0,0 +1,356 @@
# OfficeCLI Examples
Comprehensive examples demonstrating OfficeCLI capabilities for Word, Excel, and PowerPoint automation.
## 📂 Directory Structure
Every example below ships all four files — `.{md,sh,py,<ext>}` — even where the
tree abbreviates. Only `ppt/templates/styles/*/build*.sh` are `.sh`-only.
```
examples/
├── README.md # This file
├── word/ # 📄 Word examples — *.{md,sh,py,docx}
│ ├── formulas.{md,sh,py,docx} # LaTeX math/chemistry/physics formulas
│ ├── tables.{md,sh,py,docx} # styled tables
│ ├── textbox.{md,sh,py,docx} # formatted text boxes
│ ├── charts.{md,sh,py,docx} # inline charts (14 types incl. treemap/waterfall)
│ ├── run-formatting.{md,sh,py,docx} # run/character property surface
│ ├── paragraph-formatting.{md,sh,py,docx} # paragraph property surface
│ ├── document-formatting.{md,sh,py,docx} # document-level property surface
│ ├── sections.{md,sh,py,docx} # section layout — multi-column, footnote/endnote props, per-section page setup
│ ├── content-controls.{md,sh,py,docx} # SDT content controls — text/dropdown/combobox/date/picture/group intake form
│ ├── fields.{md,sh,py,docx} # field codes (PAGE/DATE/REF/IF/HYPERLINK) + auto-populating table of contents
│ ├── pictures.{md,sh,py,docx} # inline/floating images — crop, alt, wrap, behind-text, absolute position
│ ├── numbering.{md,sh,py,docx} # list/numbering styles
│ ├── diagram.{md,sh,py,docx} # Mermaid diagrams — native editable shapes + full-fidelity PNG
│ └── revisions.{md,sh,py,docx} # tracked-change (revision) API
├── excel/ # 📊 Excel examples — *.{md,sh,py,xlsx}
│ ├── cell-formatting.{md,sh,py,xlsx} # full cell property surface (fonts/fills/borders/numFmt/data)
│ ├── conditional-formatting.{md,sh,py,xlsx}
│ ├── data-validation.{md,sh,py,xlsx} # dropdown lists, number/date/text/custom rules, input & error messages
│ ├── sheet-settings.{md,sh,py,xlsx} # freeze panes, print area/titles, headers/footers, display & protection
│ ├── sparklines.{md,sh,py,xlsx} # in-cell line/column/win-loss mini charts + point markers
│ ├── workbook-settings.{md,sh,py,xlsx}
│ ├── pivot-tables.{md,sh,py,xlsx}
│ ├── slicers.{md,sh,py,xlsx} # pivot-table slicers (field/caption/columnCount/rowHeight)
│ ├── shapes.{md,sh,py,xlsx} # drawing shapes — geometry, flip, glow, gradient, reflection, outline
│ ├── charts.{md,sh,py,xlsx} # master chart showcase
│ └── charts/ # per-type chart scripts — charts-<type>.{md,sh,py,xlsx}
│ (demo, basic, advanced, extended, area, bar, boxwhisker,
│ bubble, column, combo, histogram, line, pie, radar,
│ scatter, stock, waterfall)
└── ppt/ # 🎨 PowerPoint examples — *.{md,sh,py,pptx}
├── presentation.{md,sh,py,pptx}
├── presentation-settings.{md,sh,py,pptx}
├── diagram.{md,sh,py,pptx} # Mermaid diagrams — native editable shapes + full-fidelity PNG
├── animations.{md,sh,py,pptx}
├── video.{md,sh,py,pptx}
├── 3d-model.{md,sh,py,pptx}
├── charts/ # charts-<type>.{md,sh,py,pptx}
│ (column, bar, line, pie, doughnut, area, scatter,
│ bubble, radar, stock, combo, waterfall, 3d, advanced)
├── tables/ # tables-<topic>.{md,sh,py,pptx}
│ (basic, styled, merged, borders, rows-cols, financial, nested)
├── transitions/ # transitions-<topic>.{md,sh,py,pptx}
│ (basic, directional, shapes, bands, dynamic, modern, random, timing, morph)
├── shapes/ # shapes-<topic>.{md,sh,py,pptx}
│ (basic, connectors, effects, typography)
├── textboxes/ # textboxes-<topic>.{md,sh,py,pptx}
│ (basic, advanced)
├── pictures/ # pictures-basic.{md,sh,py,pptx}
├── ole/ # ole-embed.{md,sh,py,pptx} — embedded Excel/Word OLE objects
└── templates/styles/*/build*.sh # full-deck template generators (.sh only)
```
Each example ships the same **four-file set**:
| File | Role |
|------|------|
| `<name>.md` | Walkthrough — what the example demonstrates and the key techniques |
| `<name>.sh` | **CLI** build script — drives the `officecli` binary directly (`officecli create / add / set / close / validate`) |
| `<name>.py` | **SDK** build script — drives the [`officecli` Python SDK](../sdk/python/) (`import officecli`; `with officecli.create(...) as doc: doc.batch([...])`) |
| `<name>.<ext>` | Pre-generated output (`.docx` / `.xlsx` / `.pptx`) |
The `.sh` and `.py` are **equivalent twins** — both regenerate the same output document, one via the command line and one via the Python SDK. Run whichever fits your workflow.
> The full-deck template generators under `ppt/templates/styles/*/build*.sh` are
> standalone deck builders, not four-file API examples, so they ship as `.sh` only.
---
## 🚀 Quick Start
Every example runs **two equivalent ways** — pick one:
```bash
bash <name>.sh # via the officecli CLI binary
python3 <name>.py # via the officecli Python SDK (pip install officecli-sdk)
```
Both regenerate the same output document. The commands below show one form per
example; swap `bash …​.sh``python3 …​.py` freely.
### By Document Type
**Word (.docx):**
```bash
cd word
bash run-formatting.sh # Run/character formatting: bold/underline/strike/caps/super-sub/fonts/effects
bash paragraph-formatting.sh # Paragraph formatting: align/indent/spacing/pagination/shading/markRPr
bash formulas.sh # LaTeX math formulas
bash tables.sh # Styled tables
bash textbox.sh # Formatted text boxes
bash numbering.sh # List/numbering styles
bash revisions.sh # Tracked-change (revision) API — ins/del/format/move/cellChange
```
**Excel (.xlsx):**
```bash
cd excel
python cell-formatting.py # Full cell property surface: fonts, fills, borders, number formats, formulas/links
bash charts.sh # Master chart showcase (8 chart types in one workbook)
bash charts/charts-basic.sh # Per-type high-level examples (any charts/charts-<type>.sh)
python charts/charts-line.py # Single-type example (any charts/charts-<type>.py)
python pivot-tables.py # Pivot tables
```
**PowerPoint (.pptx):**
```bash
cd ppt
bash presentation.sh # Morph transitions / full deck
bash animations.sh # Animation effects
python video.py # Video embedding
bash 3d-model.sh # 3D model embedding
bash diagram.sh # Mermaid diagrams — render=native (editable shapes) + render=image (PNG)
python charts/charts-column.py # PowerPoint chart examples (any charts/charts-<type>.py)
bash tables/tables-basic.sh # Tables — minimal create + populate
bash tables/tables-styled.sh # 9 built-in styles + banding flags + rowHeight/name=
bash tables/tables-merged.sh # gridSpan horizontal merge
bash tables/tables-borders.sh # Per-side / per-cell borders
bash tables/tables-rows-cols.sh # add row/column, per-row height, gridSpan + merge.down
bash tables/tables-financial.sh # End-to-end financial deck
bash transitions/transitions-basic.sh # cut/fade/dissolve/flash + 'none' clear
bash transitions/transitions-directional.sh # push/wipe/cover/uncover × direction matrix
bash transitions/transitions-shapes.sh # circle/diamond/wedge/wheel/zoom
bash transitions/transitions-bands.sh # blinds/strips/split/checker
bash transitions/transitions-dynamic.sh # 2010+ Exciting gallery (vortex/flip/...)
bash transitions/transitions-modern.sh # 2013+ Exciting gallery (pageCurl/airplane/origami/...)
bash transitions/transitions-random.sh # newsflash / random
bash transitions/transitions-timing.sh # speed, duration, advanceTime, advanceClick
bash transitions/transitions-morph.sh # 2016+ Morph tweening
bash shapes/shapes-basic.sh # geometries, fills, outlines, rotation, basic effects
bash shapes/shapes-connectors.sh # straight/elbow/curve connectors + groups
bash shapes/shapes-effects.sh # autoFit, flip, image fill, 3D, softEdge, links, zorder
bash shapes/shapes-typography.sh # spacing, kern, case, RTL direction, font.cs, lang
bash textboxes/textboxes-basic.sh # alignment, bullets, runs, per-script fonts
bash textboxes/textboxes-advanced.sh # per-paragraph overrides, indents, per-run typography
python pictures/pictures-basic.py # picture src/crop/rotation/links (needs Pillow)
```
---
## 📚 Documentation by Type
### 📄 [Word Examples →](word/)
- Run / character formatting — weight, underline variants, strike/dstrike, caps/smallCaps, super/subscript, color/size/highlight, per-script fonts, text effects, character spacing, language
- Paragraph formatting — alignment, indentation, spacing, pagination flags, paragraph-level run formatting, shading, paragraph-mark (markRPr) formatting, outline level
- Mathematical formulas (LaTeX)
- Complex tables
- Text boxes and styling
- Numbering / list showcases
- Mermaid diagrams — `render=native` (editable flowchart / sequence shapes) and `render=image` (inline full-fidelity PNG of every mermaid type)
### 📊 [Excel Examples →](excel/)
- Cell formatting — the full `cell` property surface across 5 sheets: fonts (name/size/bold/italic/color/underline/strike), fills (hex/named/rgb) + alignment (h/v/wrap/RTL), borders (shorthand/all/per-side/color), number formats (thousands/%/currency/date/scientific/accounting), and data (value/type/formula/link/locked/merge)
- Master and per-type chart scripts (line, bar, pie, scatter, stock, waterfall, …)
- Pivot tables
- Number formatting and styling
### 🎨 [PowerPoint Examples →](ppt/)
- Slide / shape construction
- Morph transitions and animations
- Video and 3D model embedding
- Mermaid diagrams — `render=native` (editable flowchart / sequence shapes + connectors) and `render=image` (full-fidelity PNG via mermaid.js, covering every mermaid type); `mermaid` / `text` / `dsl` / `src` source, placement box, `get` / `set` / `remove` the whole diagram as one group
- Native chart examples (column, bar, line, pie, doughnut, area, scatter, bubble, radar, stock, combo, waterfall, 3D, advanced)
- Tables — basic, built-in styles, merged cells, borders, row/column ops, real-world financial deck
- Slide transitions — all 59 schema tokens covered across 9 trios: basic, directional, shape, band, dynamic 3D (p14), modern (p15 — Page Curl, Airplane, Origami, …), random, timing, and Morph
- Shapes — full pptx/shape property surface across 4 trios: geometries + fills + outlines + rotation + basic effects (basic), straight/elbow/curve connectors + groups (connectors), autoFit + flip + image-fill + 3D scene + softEdge + click links + zorder (effects), paragraph/char spacing + kerning + smallCaps + RTL + complex-script font + BCP-47 lang (typography)
- Textboxes — alignment, bulleted/numbered lists, run-by-run rich text (bold/italic/color/super/sub/strike), per-script fonts (Latin/EastAsian), vertical alignment and padding
- Pictures — file path / URL / data-URI / `name=` for `src=`, all crop forms (symmetric, V,H, per-edge L/T/R/B), rotation, clickable links (URL / slide jump / named action)
---
## 🔧 Common Patterns
### Create and Populate
```bash
#!/bin/bash
set -e
FILE="document.docx"
officecli create "$FILE"
officecli add "$FILE" /body --type paragraph --prop text="Hello World"
officecli validate "$FILE"
```
### Batch Operations
```bash
cat << 'EOF' > commands.json
[
{"command":"add","parent":"/body","type":"paragraph","props":{"text":"Para 1"}},
{"command":"set","path":"/body/p[1]","props":{"bold":"true","size":"24"}}
]
EOF
officecli batch document.docx < commands.json
```
### Resident Mode (3+ operations)
```bash
officecli open document.docx
officecli add document.docx /body --type paragraph --prop text="Fast operation"
officecli set document.docx /body/p[1] --prop bold=true
officecli close document.docx
```
### Query and Modify
```bash
# Find all Heading1 paragraphs
officecli query report.docx "paragraph[style=Heading1]" --json
# Change their color
officecli set report.docx /body/p[1] --prop color=FF0000
```
---
## 📊 Quick Reference
### Document Types
| Format | Extension | Create | View | Modify |
|--------|-----------|--------|------|--------|
| Word | .docx | ✓ | ✓ | ✓ |
| Excel | .xlsx | ✓ | ✓ | ✓ |
| PowerPoint | .pptx | ✓ | ✓ | ✓ |
### Common Commands
| Command | Purpose | Example |
|---------|---------|---------|
| `create` | Create blank document | `officecli create file.docx` |
| `view` | View content | `officecli view file.docx text` |
| `get` | Get element | `officecli get file.docx /body/p[1]` |
| `set` | Modify element | `officecli set file.docx /body/p[1] --prop bold=true` |
| `add` | Add element | `officecli add file.docx /body --type paragraph` |
| `remove` | Remove element | `officecli remove file.docx /body/p[5]` |
| `query` | CSS-like query | `officecli query file.docx "paragraph[style=Normal]"` |
| `batch` | Multiple operations | `officecli batch file.docx < commands.json` |
| `validate` | Check schema | `officecli validate file.docx` |
### View Modes
| Mode | Description | Usage |
|------|-------------|-------|
| `text` | Plain text | `officecli view file.docx text` |
| `annotated` | Text with formatting | `officecli view file.docx annotated` |
| `outline` | Structure | `officecli view file.docx outline` |
| `stats` | Statistics | `officecli view file.docx stats` |
| `issues` | Problems | `officecli view file.docx issues` |
| `html` | HTML preview | `officecli view file.docx html` |
| `svg` | SVG preview | `officecli view file.docx svg` |
| `forms` | Form fields | `officecli view file.docx forms` |
---
## 💡 Tips
1. **Explore before modifying:**
```bash
officecli view document.docx outline
officecli get document.docx /body --depth 2
```
2. **Use `--json` for automation:**
```bash
officecli query data.xlsx "cell[formula~=SUM]" --json | jq
```
3. **Check help for properties** (schema reference is under the `help` verb):
```bash
officecli help docx set paragraph
officecli help xlsx set cell
officecli help pptx set shape
```
4. **Validate after changes:**
```bash
officecli validate document.docx
```
5. **Use resident mode for performance** (3+ operations on same file):
```bash
officecli open file.pptx
# ... multiple commands ...
officecli close file.pptx
```
---
## 🤝 Contributing Examples
1. **Create script** with clear comments
2. **Test and verify** output
3. **Add to appropriate directory** (word/excel/ppt)
4. **Update directory README**
5. **Submit PR**
**Example format:**
```bash
#!/bin/bash
# Brief description of what this demonstrates
# Key techniques: list them here
set -e
FILE="output.docx"
officecli create "$FILE"
# ... your commands ...
officecli validate "$FILE"
echo "Created: $FILE"
```
---
## 📖 More Resources
- **[SKILL.md](../SKILL.md)** - Complete command reference for AI agents
- **[README.md](../README.md)** - Project overview and installation
---
## 🆘 Getting Help
**Top-level help:**
```bash
officecli --help # CLI usage
officecli help # Schema reference entry point
officecli help docx # All docx elements
officecli help docx set # Elements that support `set` for docx
officecli help docx set paragraph # Settable properties on paragraph
officecli help docx paragraph --json # Raw schema JSON
officecli help all # Flat dump of every (format, element, property)
```
Format aliases: `word→docx`, `excel→xlsx`, `ppt`/`powerpoint→pptx`.
Verbs: `add`, `set`, `get`, `query`, `remove`.
---
**Happy automating! 🚀**
For questions or issues, visit [GitHub Issues](https://github.com/iOfficeAI/OfficeCLI/issues).
Binary file not shown.
+186
View File
@@ -0,0 +1,186 @@
# Cell Formatting Showcase
Exercises the full xlsx `cell` property surface — the single most-used Excel
element. Three files work together:
- **cell-formatting.py** — Python script that drives `officecli` to build the workbook.
- **cell-formatting.xlsx** — The generated 6-sheet workbook.
- **cell-formatting.md** — This file.
## Regenerate
```bash
cd examples/excel
python3 cell-formatting.py
# → cell-formatting.xlsx
```
`set` auto-creates the target cell, so no per-cell `add` is needed. The script
uses resident mode (`open``close`) for speed and registers an `atexit` close
so the resident process is never left dangling on error.
> The `cell()` helper wraps each `--prop k=v` in `shlex.quote`. This matters:
> a currency format like `numberformat=$#,##0.00` contains `$#`, which a shell
> would otherwise expand to the positional-arg count. Quoting keeps the code
> literal.
## Sheets
### Sheet1 — Fonts
Each row pairs a property label (column A) with a rendered sample (column B):
`font.name`, `font.size`, `font.bold`, `font.italic`, `font.color`,
`underline=single`, `underline=double`, `strike`, and a combined run.
```bash
officecli set file.xlsx /Sheet1/B11 \
--prop value="Bold + italic + blue + 14pt" \
--prop font.bold=true --prop font.italic=true \
--prop font.color=2E75B6 --prop font.size=14
```
### Sheet2 — Fills & alignment
| Feature | Spec |
|---|---|
| Solid hex fill | `fill=E63946` |
| Named color | `fill=gold` |
| `rgb()` form | `fill="rgb(46,157,182)"` |
| Horizontal align | `alignment.horizontal=left\|center\|right` (alias `halign`) |
| Vertical align | `alignment.vertical=top\|center\|bottom` (alias `valign`) |
| Wrap text | `alignment.wrapText=true` (aliases `wrap`, `wrapText`) |
| Reading order | `alignment.readingOrder=rtl` |
Vertical alignment only shows visibly when the row is taller than the text, so
the script bumps `row[6..8]` height to 34pt via `/Fills/row[6] --prop height=34`.
The sheet also shows three alignment properties set directly (canonical keys):
| Feature | Spec |
|---|---|
| Text rotation | `alignment.textRotation=45` (0-90 up / 91-180 down / 255 stacked; alias `rotation`) |
| Indent | `alignment.indent=3` (alias `indent`) |
| Shrink to fit | `alignment.shrinkToFit=true` (alias `shrink`) |
### Sheet3 — Borders
```bash
officecli set file.xlsx /Borders/B3 --prop border=thin # shorthand: all four sides
officecli set file.xlsx /Borders/B5 --prop border.all=medium # explicit "all" form
officecli set file.xlsx /Borders/B7 --prop border=thick --prop border.color=C00000
officecli set file.xlsx /Borders/B9 --prop border.bottom=double # single side
officecli set file.xlsx /Borders/B13 --prop border.left=thick --prop border.top=thin \
--prop border.right=medium --prop border.bottom=double
# Diagonal borders — direction via diagonalUp/Down; color requires a diagonal line.
officecli set file.xlsx /Borders/B15 --prop border.diagonal=thin --prop border.diagonalUp=true
officecli set file.xlsx /Borders/B17 --prop border.diagonal=medium --prop border.diagonalDown=true \
--prop border.diagonal.color=C00000
```
Styles accepted: `thin`, `medium`, `thick`, `double`, `dashed`, … (full list in
`schemas/help/xlsx/cell.json``border.*`). `border.diagonal.color` requires a
`border.diagonal` line to attach to.
### Sheet4 — Number formats
The label column is the format **code**; column B is the same kind of value
with that `numberformat` applied:
| `numberformat=` | Value → Display |
|---|---|
| `#,##0` | `1234567``1,234,567` |
| `#,##0.00` | `1234.5``1,234.50` |
| `0.00%` | `0.1834``18.34%` |
| `$#,##0.00` | `29999.9``$29,999.90` |
| `yyyy-mm-dd` | `45413``2024-05-01` |
| `0.00E+00` | `602214``6.02E+05` |
| `_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_)` | `-4250``(4,250.00)` |
> The `0.00E+00` **label** cell is written with `type=string`, otherwise Excel
> parses the literal text `0.00E+00` as the number `0`.
### Sheet5 — Values, formulas, links
```bash
officecli set file.xlsx /Data/B5 --prop formula="B3*B4" --prop numberformat="$#,##0.00" # 12 × 4.50 = $54.00
officecli set file.xlsx /Data/B7 --prop value=007 --prop type=string # keep leading zeros
officecli set file.xlsx /Data/A9 --prop value="OfficeCLI on GitHub" \
--prop link="https://github.com/iOfficeAI/OfficeCLI" --prop tooltip="Open the repo"
officecli set file.xlsx /Data/A11 --prop value="locked cell" --prop locked=true # effective once sheet is protected
officecli set file.xlsx /Data/A13 --prop value="Merged title" --prop merge="A13:C13" \
--prop alignment.horizontal=center
officecli set file.xlsx /Data/B15 --prop arrayformula="B3*2" # dynamic-array spill
```
### Sheet6 — Rich-text runs
`runs` is an add-time property (requires `--type cell` and `type=richtext`). Each run is a JSON
object with `"text"` plus optional font props (`bold`, `italic`, `color`, `size`, `underline`,
`strike`, `superscript`, `subscript`). `set` does not support rich-text; use `add`.
```bash
# Bold+red / italic+blue / normal run in one cell
officecli add file.xlsx /RichText --type cell --prop ref=A3 \
--prop type=richtext \
--prop 'runs=[{"text":"Bold + Red ","bold":true,"color":"C00000"},{"text":"Italic + Blue","italic":true,"color":"2E75B6"},{"text":" Normal"}]'
# Chemical formula with superscript: H₂O
officecli add file.xlsx /RichText --type cell --prop ref=A5 \
--prop type=richtext \
--prop 'runs=[{"text":"H","bold":true,"color":"1F4E79","size":18},{"text":"2","superscript":true,"size":10},{"text":"O water formula","color":"1F4E79"}]'
# strike / underline / different size in one cell
officecli add file.xlsx /RichText --type cell --prop ref=A7 \
--prop type=richtext \
--prop 'runs=[{"text":"Strike","strike":true},{"text":" | "},{"text":"underline","underline":"single"},{"text":" | "},{"text":"size 14pt","size":14}]'
```
**Features:** `type=richtext`, `runs` (JSON array of run objects), per-run: `text`, `bold`, `italic`, `color`, `size`, `underline`, `strike`, `superscript`, `subscript`
## Complete Feature Coverage
| Feature | Sheet |
|---------|-------|
| `font.name`, `font.size`, `font.bold`, `font.italic`, `font.color` | Sheet1 |
| `underline=single`, `underline=double` | Sheet1 |
| `strike=true` | Sheet1 |
| `superscript=true`, `subscript=true` | Sheet1 |
| `fill` (hex, named, rgb) | Sheet2 |
| `alignment.horizontal` (left/center/right) | Sheet2 |
| `alignment.vertical` (top/center/bottom) | Sheet2 |
| `alignment.wrapText` | Sheet2 |
| `alignment.readingOrder` (rtl) | Sheet2 |
| `alignment.textRotation` (0-255) | Sheet2 |
| `alignment.indent` | Sheet2 |
| `alignment.shrinkToFit` | Sheet2 |
| `border` (shorthand all sides) | Sheet3 |
| `border.all`, `border.top/bottom/left/right` | Sheet3 |
| `border.color`, `border.diagonal`, `border.diagonalUp`, `border.diagonalDown`, `border.diagonal.color` | Sheet3 |
| `numberformat` (thousands, %, currency, date, scientific, accounting) | Sheet4 |
| `value`, `type=string` | Sheet5 |
| `formula`, `arrayformula` | Sheet5 |
| `link`, `tooltip` | Sheet5 |
| `locked` | Sheet5 |
| `merge` | Sheet5 |
| `type=richtext`, `runs` (per-run: bold/italic/color/size/strike/underline/superscript/subscript) | Sheet6 |
## Set → Get round-trip
The script ends by reading three cells back with `get … --json` and printing the
canonical keys, proving the values survive the write and normalize on read:
```
/Sheet1/B11: {'font.bold': True, 'font.italic': True, 'font.color': '#2E75B6', 'font.size': '14pt'}
/Numbers/B6: {'numberformat': '$#,##0.00'}
/Borders/B9: {'border.bottom': 'double'}
```
Note the normalization on `get`: colors gain a `#` prefix (`#2E75B6`) and font
sizes become unit-qualified (`14pt`) — the canonical output forms.
## Inspect the Generated File
```bash
officecli query cell-formatting.xlsx sheet
officecli get cell-formatting.xlsx "/RichText/A3"
```
+255
View File
@@ -0,0 +1,255 @@
#!/usr/bin/env python3
"""
Cell Formatting Showcase — generates cell-formatting.xlsx exercising the full
xlsx `cell` property surface (schemas/help/xlsx/cell.json).
SDK twin of cell-formatting.sh (officecli CLI). Both produce an equivalent
cell-formatting.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every cell write
ships over the named pipe in `doc.batch(...)` round-trips — the same
`{"command","parent","type","props"}` / `{"command","path","props"}` dicts
you'd put in an `officecli batch` list.
6 sheets, one property group each:
Fonts — font.name/size/bold/italic/color, underline, strike, super/subscript
Fills — fill (hex/named/rgb), alignment.horizontal/vertical/wrapText/readingOrder
+ textRotation/indent/shrinkToFit
Borders — border shorthand, border.all, per-side styles, border.color, diagonals
Numbers — numberformat codes (thousands, %, currency, date, scientific, accounting)
Data — value/type, formula, link + tooltip, locked, merge, arrayformula
RichText — runs (multi-format text within one cell; add-time only)
`set` auto-creates the target cell, so no explicit `add` is needed per cell.
Closes with a Set -> Get round-trip readback proving the canonical keys come back.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 cell-formatting.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cell-formatting.xlsx")
def cell(path, **props):
"""One `set <path> --prop k=v ...` item in batch-shape. `set` auto-creates
the target cell, so no explicit `add` is needed. Props pass through verbatim
(canonical keys like `font.bold`, `alignment.horizontal`, `numberformat`)."""
return {"command": "set", "path": f"/{path}" if not path.startswith("/") else path,
"props": props}
def add_sheet(name):
"""One `add sheet --prop name=...` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def add_cell(parent, **props):
"""One `add cell` item in batch-shape — used for rich-text `runs`, which is an
add-time property (requires --type cell + type=richtext); `set` can't do it."""
return {"command": "add", "parent": parent, "type": "cell", "props": props}
print("\n==========================================")
print(f"Generating cell formatting showcase: {FILE}")
print("==========================================")
with officecli.create(FILE, "--force") as doc:
# ==========================================================================
# Sheet1: Fonts — font.* family + underline/strike
# ==========================================================================
print("\n--- Sheet1: Fonts ---")
items = [
cell("Sheet1/A1", value="Cell font properties", **{"font.bold": "true", "font.size": "14", "fill": "1F4E79", "font.color": "FFFFFF"}),
cell("Sheet1/A2", value="Property", **{"font.bold": "true", "fill": "D9E1F2"}),
cell("Sheet1/B2", value="Rendered sample", **{"font.bold": "true", "fill": "D9E1F2"}),
]
# (label, sample-text, {props applied to the sample cell})
FONT_ROWS = [
("font.name=Georgia", "Georgia serif", {"font.name": "Georgia"}),
("font.size=18", "18pt text", {"font.size": "18"}),
("font.bold=true", "Bold text", {"font.bold": "true"}),
("font.italic=true", "Italic text", {"font.italic": "true"}),
("font.color=C00000", "Red text", {"font.color": "C00000"}),
("underline=single", "Underlined", {"underline": "single"}),
("underline=double", "Double underline", {"underline": "double"}),
("strike=true", "Struck out", {"strike": "true"}),
("superscript=true", "Superscript cell", {"superscript": "true"}),
("subscript=true", "Subscript cell", {"subscript": "true"}),
("combined", "Bold + italic + blue + 14pt", {"font.bold": "true", "font.italic": "true", "font.color": "2E75B6", "font.size": "14"}),
]
for i, (label, sample, props) in enumerate(FONT_ROWS, start=3):
items.append(cell(f"Sheet1/A{i}", value=label))
items.append(cell(f"Sheet1/B{i}", value=sample, **props))
items.append(cell("Sheet1/col[1]", width="22"))
items.append(cell("Sheet1/col[2]", width="32"))
doc.batch(items)
# ==========================================================================
# Sheet2: Fills & alignment
# ==========================================================================
print("--- Sheet2: Fills & alignment ---")
items = [add_sheet("Fills")]
items.append(cell("Fills/A1", value="Fills & alignment", **{"font.bold": "true", "font.size": "14", "fill": "548235", "font.color": "FFFFFF"}))
items.append(cell("Fills/A2", value="fill=E63946 (hex)", fill="E63946", **{"font.color": "FFFFFF"}))
items.append(cell("Fills/A3", value="fill=gold (named)", fill="gold"))
items.append(cell("Fills/A4", value="fill=rgb(46,157,182)", fill="rgb(46,157,182)", **{"font.color": "FFFFFF"}))
for i, h in zip((6, 7, 8), ("left", "center", "right")):
items.append(cell(f"Fills/A{i}", value=h, fill="F2F2F2", **{"alignment.horizontal": h}))
for i, v in zip((6, 7, 8), ("top", "center", "bottom")):
items.append(cell(f"Fills/C{i}", value={"center": "middle"}.get(v, v), fill="FCE4D6", **{"alignment.vertical": v}))
items.append(cell(f"Fills/row[{i}]", height="34"))
items.append(cell("Fills/A10", value="This is a long sentence that wraps inside one cell via alignment.wrapText.", fill="E2EFDA", **{"alignment.wrapText": "true"}))
items.append(cell("Fills/A12", value="RTL reading order", fill="DDEBF7", **{"alignment.readingOrder": "rtl"}))
# textRotation / indent / shrinkToFit — set directly on alignment (canonical keys).
items.append(cell("Fills/A14", value="rotated 45deg", fill="FFF2CC", **{"alignment.textRotation": "45"}))
items.append(cell("Fills/row[14]", height="40"))
items.append(cell("Fills/A16", value="indented 3", fill="F2F2F2", **{"alignment.indent": "3"}))
items.append(cell("Fills/A18", value="ThisLongLabelShrinksToFit", fill="E2EFDA", **{"alignment.shrinkToFit": "true"}))
items.append(cell("Fills/col[1]", width="30"))
items.append(cell("Fills/col[3]", width="14"))
doc.batch(items)
# ==========================================================================
# Sheet3: Borders
# ==========================================================================
print("--- Sheet3: Borders ---")
items = [add_sheet("Borders")]
items.append(cell("Borders/A1", value="Border styles", **{"font.bold": "true", "font.size": "14", "fill": "7030A0", "font.color": "FFFFFF"}))
items.append(cell("Borders/B3", value="border=thin (all)", border="thin"))
items.append(cell("Borders/B5", value="border.all=medium", **{"border.all": "medium"}))
items.append(cell("Borders/B7", value="border + color", border="thick", **{"border.color": "C00000"}))
items.append(cell("Borders/B9", value="double bottom", **{"border.bottom": "double"}))
items.append(cell("Borders/B11", value="dashed box", **{"border.top": "dashed", "border.bottom": "dashed", "border.left": "dashed", "border.right": "dashed"}))
items.append(cell("Borders/B13", value="mixed sides", **{"border.left": "thick", "border.top": "thin", "border.right": "medium", "border.bottom": "double"}))
# Diagonal borders — direction via diagonalUp/Down, color requires a diagonal line.
items.append(cell("Borders/B15", value="diagonal up", **{"border.diagonal": "thin", "border.diagonalUp": "true"}))
items.append(cell("Borders/B17", value="diagonal down + color", **{"border.diagonal": "medium", "border.diagonalDown": "true", "border.diagonal.color": "C00000"}))
items.append(cell("Borders/col[1]", width="18"))
items.append(cell("Borders/col[2]", width="24"))
doc.batch(items)
# ==========================================================================
# Sheet4: Number formats
# ==========================================================================
print("--- Sheet4: Number formats ---")
items = [add_sheet("Numbers")]
items.append(cell("Numbers/A1", value="numberformat codes", **{"font.bold": "true", "font.size": "14", "fill": "C55A11", "font.color": "FFFFFF"}))
items.append(cell("Numbers/A2", value="Format code", **{"font.bold": "true", "fill": "FCE4D6"}))
items.append(cell("Numbers/B2", value="Result", **{"font.bold": "true", "fill": "FCE4D6"}))
# (format code, raw value); A-label is the code itself, B-cell carries the format
NUM_ROWS = [
("#,##0", "1234567"),
("#,##0.00", "1234.5"),
("0.00%", "0.1834"),
("$#,##0.00", "29999.9"),
("yyyy-mm-dd", "45413"),
("0.00E+00", "602214"),
('_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_)', "-4250"),
]
for i, (code, val) in enumerate(NUM_ROWS, start=3):
# label cell: show the (short) code as literal text — type=string keeps
# codes like "0.00E+00" from being parsed as a scientific-notation number.
items.append(cell(f"Numbers/A{i}", value=code.split(";")[0], type="string"))
items.append(cell(f"Numbers/B{i}", value=val, numberformat=code))
items.append(cell("Numbers/col[1]", width="28"))
items.append(cell("Numbers/col[2]", width="18"))
doc.batch(items)
# ==========================================================================
# Sheet5: Data — value/type, formula, link, locked, merge
# ==========================================================================
print("--- Sheet5: Data, formulas & links ---")
items = [add_sheet("Data")]
items.append(cell("Data/A1", value="Values, formulas, links", **{"font.bold": "true", "font.size": "14", "fill": "2E75B6", "font.color": "FFFFFF"}))
items.append(cell("Data/A3", value="Qty")); items.append(cell("Data/B3", value="12"))
items.append(cell("Data/A4", value="Price")); items.append(cell("Data/B4", value="4.5", numberformat="$#,##0.00"))
items.append(cell("Data/A5", value="Total", **{"font.bold": "true"}))
items.append(cell("Data/B5", formula="B3*B4", numberformat="$#,##0.00", **{"font.bold": "true"}))
items.append(cell("Data/A7", value="type=string on a numeric value", type="string"))
items.append(cell("Data/B7", value="007", type="string"))
items.append(cell("Data/A9", value="OfficeCLI on GitHub", link="https://github.com/iOfficeAI/OfficeCLI",
tooltip="Open the repo", underline="single", **{"font.color": "0563C1"}))
items.append(cell("Data/A11", value="locked cell (effective when sheet is protected)", locked="true"))
items.append(cell("Data/A13", value="Merged title across A13:C13", merge="A13:C13", fill="DDEBF7",
**{"alignment.horizontal": "center", "font.bold": "true"}))
# Dynamic-array formula — spills the result across the ref range.
items.append(cell("Data/A15", value="arrayformula = B3*2", **{"font.italic": "true"}))
items.append(cell("Data/B15", arrayformula="B3*2"))
items.append(cell("Data/col[1]", width="40"))
items.append(cell("Data/col[2]", width="16"))
doc.batch(items)
# ==========================================================================
# Sheet6: Rich-text — runs (multi-format text within one cell)
# ==========================================================================
# `runs` is an add-time property (requires type=cell + type=richtext). Each
# run is a JSON object with "text" plus any font props (bold, italic, color,
# size, underline). `set` does not support rich-text; use `add`.
print("--- Sheet6: Rich-text runs ---")
items = [add_sheet("RichText")]
# Label
items.append(cell("RichText/A1", value="runs — rich-text within one cell", **{"font.bold": "true", "font.size": "14", "fill": "5B2C8B", "font.color": "FFFFFF"}))
# Each add creates the cell with multi-format text in a single SST entry.
items.append(add_cell("/RichText", ref="A3", type="richtext",
runs='[{"text":"Bold + Red ","bold":true,"color":"C00000"},{"text":"Italic + Blue","italic":true,"color":"2E75B6"},{"text":" Normal"}]'))
items.append(add_cell("/RichText", ref="A5", type="richtext",
runs='[{"text":"H","bold":true,"color":"1F4E79","size":18},{"text":"2","superscript":true,"size":10},{"text":"O water formula","color":"1F4E79"}]'))
items.append(add_cell("/RichText", ref="A7", type="richtext",
runs='[{"text":"Strike","strike":true},{"text":" | "},{"text":"underline","underline":"single"},{"text":" | "},{"text":"size 14pt","size":14}]'))
items.append(cell("RichText/col[1]", width="50"))
doc.batch(items)
# ==========================================================================
# Set -> Get round-trip: confirm canonical keys read back (in-session, pipe)
# ==========================================================================
print("\n--- Round-trip readback (Set then Get) ---")
for path, keys in [
("/Sheet1/B11", ("font.bold", "font.italic", "font.color", "font.size")),
("/Numbers/B6", ("value", "numberformat")),
("/Borders/B9", ("border.bottom",)),
]:
node = doc.send({"command": "get", "path": path})
try:
fmt = node["data"]["results"][0]["format"]
except Exception:
fmt = {}
shown = {k: fmt.get(k) for k in keys if k in fmt}
print(f" {path}: {shown}")
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nCreated: {FILE}")
+200
View File
@@ -0,0 +1,200 @@
#!/bin/bash
# Cell Formatting Showcase — generates cell-formatting.xlsx exercising the full
# xlsx `cell` property surface (schemas/help/xlsx/cell.json).
#
# CLI twin of cell-formatting.py (officecli Python SDK). Both produce an
# equivalent cell-formatting.xlsx.
#
# 6 sheets, one property group each:
# Fonts — font.name/size/bold/italic/color, underline, strike, super/subscript
# Fills — fill (hex/named/rgb), alignment.* + textRotation/indent/shrinkToFit
# Borders — border shorthand, border.all, per-side styles, border.color, diagonals
# Numbers — numberformat codes (thousands, %, currency, date, scientific, accounting)
# Data — value/type, formula, link + tooltip, locked, merge, arrayformula
# RichText — runs (multi-format text within one cell; add-time only)
#
# `set` auto-creates the target cell, so no explicit `add` is needed per cell.
#
# Usage: ./cell-formatting.sh [officecli path]
# 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.
CLI="${1:-officecli}"
FILE="$(dirname "$0")/cell-formatting.xlsx"
rm -f "$FILE"
$CLI create "$FILE"
$CLI open "$FILE"
# ==========================================================================
# Sheet1: Fonts — font.* family + underline/strike
# ==========================================================================
$CLI set "$FILE" /Sheet1/A1 --prop value="Cell font properties" --prop font.bold=true --prop font.size=14 --prop fill=1F4E79 --prop font.color=FFFFFF
$CLI set "$FILE" /Sheet1/A2 --prop value="Property" --prop font.bold=true --prop fill=D9E1F2
$CLI set "$FILE" /Sheet1/B2 --prop value="Rendered sample" --prop font.bold=true --prop fill=D9E1F2
# (A = label, B = rendered sample)
$CLI set "$FILE" /Sheet1/A3 --prop value="font.name=Georgia"
$CLI set "$FILE" /Sheet1/B3 --prop value="Georgia serif" --prop font.name=Georgia
$CLI set "$FILE" /Sheet1/A4 --prop value="font.size=18"
$CLI set "$FILE" /Sheet1/B4 --prop value="18pt text" --prop font.size=18
$CLI set "$FILE" /Sheet1/A5 --prop value="font.bold=true"
$CLI set "$FILE" /Sheet1/B5 --prop value="Bold text" --prop font.bold=true
$CLI set "$FILE" /Sheet1/A6 --prop value="font.italic=true"
$CLI set "$FILE" /Sheet1/B6 --prop value="Italic text" --prop font.italic=true
$CLI set "$FILE" /Sheet1/A7 --prop value="font.color=C00000"
$CLI set "$FILE" /Sheet1/B7 --prop value="Red text" --prop font.color=C00000
$CLI set "$FILE" /Sheet1/A8 --prop value="underline=single"
$CLI set "$FILE" /Sheet1/B8 --prop value="Underlined" --prop underline=single
$CLI set "$FILE" /Sheet1/A9 --prop value="underline=double"
$CLI set "$FILE" /Sheet1/B9 --prop value="Double underline" --prop underline=double
$CLI set "$FILE" /Sheet1/A10 --prop value="strike=true"
$CLI set "$FILE" /Sheet1/B10 --prop value="Struck out" --prop strike=true
$CLI set "$FILE" /Sheet1/A11 --prop value="superscript=true"
$CLI set "$FILE" /Sheet1/B11 --prop value="Superscript cell" --prop superscript=true
$CLI set "$FILE" /Sheet1/A12 --prop value="subscript=true"
$CLI set "$FILE" /Sheet1/B12 --prop value="Subscript cell" --prop subscript=true
$CLI set "$FILE" /Sheet1/A13 --prop value="combined"
$CLI set "$FILE" /Sheet1/B13 --prop value="Bold + italic + blue + 14pt" --prop font.bold=true --prop font.italic=true --prop font.color=2E75B6 --prop font.size=14
$CLI set "$FILE" "/Sheet1/col[1]" --prop width=22
$CLI set "$FILE" "/Sheet1/col[2]" --prop width=32
# ==========================================================================
# Sheet2: Fills & alignment
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name=Fills
$CLI set "$FILE" /Fills/A1 --prop value="Fills & alignment" --prop font.bold=true --prop font.size=14 --prop fill=548235 --prop font.color=FFFFFF
$CLI set "$FILE" /Fills/A2 --prop value="fill=E63946 (hex)" --prop fill=E63946 --prop font.color=FFFFFF
$CLI set "$FILE" /Fills/A3 --prop value="fill=gold (named)" --prop fill=gold
$CLI set "$FILE" /Fills/A4 --prop value="fill=rgb(46,157,182)" --prop fill="rgb(46,157,182)" --prop font.color=FFFFFF
$CLI set "$FILE" /Fills/A6 --prop value="left" --prop fill=F2F2F2 --prop alignment.horizontal=left
$CLI set "$FILE" /Fills/A7 --prop value="center" --prop fill=F2F2F2 --prop alignment.horizontal=center
$CLI set "$FILE" /Fills/A8 --prop value="right" --prop fill=F2F2F2 --prop alignment.horizontal=right
$CLI set "$FILE" /Fills/C6 --prop value="top" --prop fill=FCE4D6 --prop alignment.vertical=top
$CLI set "$FILE" "/Fills/row[6]" --prop height=34
$CLI set "$FILE" /Fills/C7 --prop value="middle" --prop fill=FCE4D6 --prop alignment.vertical=center
$CLI set "$FILE" "/Fills/row[7]" --prop height=34
$CLI set "$FILE" /Fills/C8 --prop value="bottom" --prop fill=FCE4D6 --prop alignment.vertical=bottom
$CLI set "$FILE" "/Fills/row[8]" --prop height=34
$CLI set "$FILE" /Fills/A10 --prop value="This is a long sentence that wraps inside one cell via alignment.wrapText." --prop fill=E2EFDA --prop alignment.wrapText=true
$CLI set "$FILE" /Fills/A12 --prop value="RTL reading order" --prop fill=DDEBF7 --prop alignment.readingOrder=rtl
# textRotation / indent / shrinkToFit — set directly on alignment (canonical keys).
$CLI set "$FILE" /Fills/A14 --prop value="rotated 45deg" --prop fill=FFF2CC --prop alignment.textRotation=45
$CLI set "$FILE" "/Fills/row[14]" --prop height=40
$CLI set "$FILE" /Fills/A16 --prop value="indented 3" --prop fill=F2F2F2 --prop alignment.indent=3
$CLI set "$FILE" /Fills/A18 --prop value="ThisLongLabelShrinksToFit" --prop fill=E2EFDA --prop alignment.shrinkToFit=true
$CLI set "$FILE" "/Fills/col[1]" --prop width=30
$CLI set "$FILE" "/Fills/col[3]" --prop width=14
# ==========================================================================
# Sheet3: Borders
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name=Borders
$CLI set "$FILE" /Borders/A1 --prop value="Border styles" --prop font.bold=true --prop font.size=14 --prop fill=7030A0 --prop font.color=FFFFFF
$CLI set "$FILE" /Borders/B3 --prop value="border=thin (all)" --prop border=thin
$CLI set "$FILE" /Borders/B5 --prop value="border.all=medium" --prop border.all=medium
$CLI set "$FILE" /Borders/B7 --prop value="border + color" --prop border=thick --prop border.color=C00000
$CLI set "$FILE" /Borders/B9 --prop value="double bottom" --prop border.bottom=double
$CLI set "$FILE" /Borders/B11 --prop value="dashed box" --prop border.top=dashed --prop border.bottom=dashed --prop border.left=dashed --prop border.right=dashed
$CLI set "$FILE" /Borders/B13 --prop value="mixed sides" --prop border.left=thick --prop border.top=thin --prop border.right=medium --prop border.bottom=double
# Diagonal borders — direction via diagonalUp/Down, color requires a diagonal line.
$CLI set "$FILE" /Borders/B15 --prop value="diagonal up" --prop border.diagonal=thin --prop border.diagonalUp=true
$CLI set "$FILE" /Borders/B17 --prop value="diagonal down + color" --prop border.diagonal=medium --prop border.diagonalDown=true --prop border.diagonal.color=C00000
$CLI set "$FILE" "/Borders/col[1]" --prop width=18
$CLI set "$FILE" "/Borders/col[2]" --prop width=24
# ==========================================================================
# Sheet4: Number formats
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name=Numbers
$CLI set "$FILE" /Numbers/A1 --prop value="numberformat codes" --prop font.bold=true --prop font.size=14 --prop fill=C55A11 --prop font.color=FFFFFF
$CLI set "$FILE" /Numbers/A2 --prop value="Format code" --prop font.bold=true --prop fill=FCE4D6
$CLI set "$FILE" /Numbers/B2 --prop value="Result" --prop font.bold=true --prop fill=FCE4D6
# label cell: show the (short) code as literal text — type=string keeps codes
# like "0.00E+00" from being parsed as a scientific-notation number.
$CLI set "$FILE" /Numbers/A3 --prop value="#,##0" --prop type=string
$CLI set "$FILE" /Numbers/B3 --prop value=1234567 --prop numberformat="#,##0"
$CLI set "$FILE" /Numbers/A4 --prop value="#,##0.00" --prop type=string
$CLI set "$FILE" /Numbers/B4 --prop value=1234.5 --prop numberformat="#,##0.00"
$CLI set "$FILE" /Numbers/A5 --prop value="0.00%" --prop type=string
$CLI set "$FILE" /Numbers/B5 --prop value=0.1834 --prop numberformat="0.00%"
$CLI set "$FILE" /Numbers/A6 --prop value='$#,##0.00' --prop type=string
$CLI set "$FILE" /Numbers/B6 --prop value=29999.9 --prop numberformat='$#,##0.00'
$CLI set "$FILE" /Numbers/A7 --prop value="yyyy-mm-dd" --prop type=string
$CLI set "$FILE" /Numbers/B7 --prop value=45413 --prop numberformat="yyyy-mm-dd"
$CLI set "$FILE" /Numbers/A8 --prop value="0.00E+00" --prop type=string
$CLI set "$FILE" /Numbers/B8 --prop value=602214 --prop numberformat="0.00E+00"
$CLI set "$FILE" /Numbers/A9 --prop value='_(* #,##0.00_)' --prop type=string
$CLI set "$FILE" /Numbers/B9 --prop value=-4250 --prop numberformat='_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_)'
$CLI set "$FILE" "/Numbers/col[1]" --prop width=28
$CLI set "$FILE" "/Numbers/col[2]" --prop width=18
# ==========================================================================
# Sheet5: Data — value/type, formula, link, locked, merge
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name=Data
$CLI set "$FILE" /Data/A1 --prop value="Values, formulas, links" --prop font.bold=true --prop font.size=14 --prop fill=2E75B6 --prop font.color=FFFFFF
$CLI set "$FILE" /Data/A3 --prop value="Qty"
$CLI set "$FILE" /Data/B3 --prop value=12
$CLI set "$FILE" /Data/A4 --prop value="Price"
$CLI set "$FILE" /Data/B4 --prop value=4.5 --prop numberformat='$#,##0.00'
$CLI set "$FILE" /Data/A5 --prop value="Total" --prop font.bold=true
$CLI set "$FILE" /Data/B5 --prop formula="B3*B4" --prop numberformat='$#,##0.00' --prop font.bold=true
$CLI set "$FILE" /Data/A7 --prop value="type=string on a numeric value" --prop type=string
$CLI set "$FILE" /Data/B7 --prop value=007 --prop type=string
$CLI set "$FILE" /Data/A9 --prop value="OfficeCLI on GitHub" --prop link="https://github.com/iOfficeAI/OfficeCLI" --prop tooltip="Open the repo" --prop underline=single --prop font.color=0563C1
$CLI set "$FILE" /Data/A11 --prop value="locked cell (effective when sheet is protected)" --prop locked=true
$CLI set "$FILE" /Data/A13 --prop value="Merged title across A13:C13" --prop merge=A13:C13 --prop fill=DDEBF7 --prop alignment.horizontal=center --prop font.bold=true
# Dynamic-array formula — spills the result across the ref range.
$CLI set "$FILE" /Data/A15 --prop value="arrayformula = B3*2" --prop font.italic=true
$CLI set "$FILE" /Data/B15 --prop arrayformula="B3*2"
$CLI set "$FILE" "/Data/col[1]" --prop width=40
$CLI set "$FILE" "/Data/col[2]" --prop width=16
# ==========================================================================
# Sheet6: Rich-text — runs (multi-format text within one cell)
# ==========================================================================
# `runs` is an add-time property (requires --type cell + type=richtext). Each
# run is a JSON object with "text" plus any font props (bold, italic, color,
# size, underline). `set` does not support rich-text; use `add`.
$CLI add "$FILE" / --type sheet --prop name=RichText
$CLI set "$FILE" /RichText/A1 --prop value="runs — rich-text within one cell" --prop font.bold=true --prop font.size=14 --prop fill=5B2C8B --prop font.color=FFFFFF
# Each add creates the cell with multi-format text in a single SST entry.
$CLI add "$FILE" /RichText --type cell --prop ref=A3 --prop type=richtext \
--prop 'runs=[{"text":"Bold + Red ","bold":true,"color":"C00000"},{"text":"Italic + Blue","italic":true,"color":"2E75B6"},{"text":" Normal"}]'
$CLI add "$FILE" /RichText --type cell --prop ref=A5 --prop type=richtext \
--prop 'runs=[{"text":"H","bold":true,"color":"1F4E79","size":18},{"text":"2","superscript":true,"size":10},{"text":"O water formula","color":"1F4E79"}]'
$CLI add "$FILE" /RichText --type cell --prop ref=A7 --prop type=richtext \
--prop 'runs=[{"text":"Strike","strike":true},{"text":" | "},{"text":"underline","underline":"single"},{"text":" | "},{"text":"size 14pt","size":14}]'
$CLI set "$FILE" "/RichText/col[1]" --prop width=50
$CLI close "$FILE"
# ==========================================================================
# Set -> Get round-trip: confirm canonical keys read back (fresh, from disk)
# ==========================================================================
$CLI get "$FILE" /Sheet1/B11 --json
$CLI get "$FILE" /Numbers/B6 --json
$CLI get "$FILE" /Borders/B9 --json
$CLI validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+55
View File
@@ -0,0 +1,55 @@
# charts — Master chart showcase
Generates **charts.xlsx**: eight chart types across four data sheets, built with
the high-level `officecli add --type chart` command (one `add` per chart). Each
chart references its sheet data by cell range (`dataRange`) — or, where the source
cells aren't contiguous, inline `series`/`categories`.
Three files (the usual four-set, minus a hand-written `.xlsx` — it's generated):
- **charts.sh** — CLI script (`officecli add --type chart …`).
- **charts.py** — SDK twin (same commands over one resident).
- **charts.xlsx** — generated output.
```bash
cd examples/excel
bash charts.sh # or: python3 charts.py
```
For per-type deep dives (every option of a single chart kind) see the
[`charts/`](charts/) subdirectory (`charts-column.sh`, `charts-combo.sh`,
`charts-stock.sh`, …).
## The eight charts
| # | Sheet | Type | Key props |
|---|-------|------|-----------|
| 1 | Sheet1 | Combo | `combotypes=column,column,column,column,line` + `secondaryaxis=5` (YoY-growth line on a right-hand axis) |
| 2 | Sheet1 | 3D column | `chartType=column3d` + `view3d=15,20,30` |
| 3 | Analysis | Scatter | `chartType=scatter` + `trendline=linear` (equation + R² shown) |
| 4 | Sheet1 | 3D pie (exploded) | `chartType=pie3d` + `explosion=10` + `view3d=30,70,30` + `dataLabels=percent` |
| 5 | Analysis | Bubble | **raw-set** — see note below |
| 6 | StockData | Stock OHLC | `chartType=stock` + `hilowlines=true` + `updownbars=100:FF0000:00B050` (red up / green down) |
| 7 | Assessment | Filled radar | `chartType=radar` + `radarStyle=filled` |
| 8 | Sheet1 | Multi-ring doughnut | `chartType=doughnut` + two inline series (two rings) |
Common props on every chart: `title`, `colors` (comma-separated palette),
`legend=b`, and `x`/`y`/`width`/`height` (position and size in cell units).
## Why Chart 5 (bubble) stays on raw-set
A bubble point needs three coordinates — x, y and size. The high-level
`add --type chart --prop chartType=bubble` reads a multi-column `dataRange` as
several y-series sharing column A as x; it cannot map three columns to a single
x / y / size series (a multi-point bubble). Until that mapping exists, the
faithful single-series bubble is authored with `raw-set`. Every other chart here
uses the high-level command.
## Inspect
```bash
officecli validate charts.xlsx
officecli view charts.xlsx outline
officecli query charts.xlsx chart # list all chart parts
officecli get charts.xlsx '/Sheet1/chart[1]'
```
+330
View File
@@ -0,0 +1,330 @@
#!/usr/bin/env python3
"""
Beautiful Charts Showcase — generates charts.xlsx with 8 chart types: combo
(columns + secondary-axis line), 3D bar, scatter+trendline, exploded 3D pie,
bubble, stock OHLC candlestick (red up / green down), filled radar, and a
multi-ring (nested) doughnut. 4 data sheets feed them: monthly sales (Sheet1),
spend/sales analysis (Analysis), OHLC stock data (StockData), and capability
assessment (Assessment).
SDK twin of charts.sh (officecli CLI). Both produce an equivalent charts.xlsx.
This one drives the **officecli Python SDK** (`pip install officecli-sdk`): one
resident is started and every command is shipped over the named pipe. Cell data
goes out per-sheet in a single `doc.batch(...)` round-trip; seven of the charts
are then a single high-level `add --type chart` send each (chartType + dataRange
or inline data + styling props). The bubble (Chart 5) stays on raw-set — the
high-level command can't map a dataRange to a single x/y/size series — so it uses
`add-part` (relId captured into the anchor) + two `raw-set` calls. Each item is
the same `{"command",...,"props"}` dict you'd put in an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts.py
"""
import os
import re
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts.xlsx")
CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart"
# ---------------------------------------------------------------- batch helpers
def cell(path, value, **props):
"""One `set <cell>` item in batch-shape."""
return {"command": "set", "path": path, "props": {"value": str(value), **props}}
def add_sheet(name):
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
HDR = {"font.bold": "true", "alignment.horizontal": "center"}
def header(path, value, fill, font_color, size=None):
p = {"value": value, "fill": fill, "font.color": font_color, **HDR}
if size is not None:
p["font.size"] = str(size)
return {"command": "set", "path": path, "props": p}
# ---------------------------------------------------------------- chart helpers
def add_chart_part(doc, parent):
"""`add-part --type chart`; return the created relationship id.
as_json=False yields the plain 'Created chart part: relId=... path=...' line
(same string the .sh greps), from which we pull relId."""
msg = doc.send({"command": "add-part", "parent": parent, "type": "chart"},
as_json=False)
m = re.search(r"relId=(\S+)", msg if isinstance(msg, str) else str(msg))
if not m:
raise RuntimeError(f"add-part did not return a relId: {msg!r}")
return m.group(1)
def set_chart_xml(doc, chart_path, xml):
"""`raw-set` the whole chartSpace (replace /c:chartSpace). raw-set's target
part is the `part` field (the CLI positional arg), not `path`."""
doc.send({"command": "raw-set", "part": chart_path,
"xpath": "/c:chartSpace", "action": "replace", "xml": xml})
def add_anchor(doc, sheet, from_col, from_row, to_col, to_row, cnvpr_id, name, rel_id):
"""`raw-set` append a twoCellAnchor graphicFrame referencing the chart."""
xml = (
'<xdr:twoCellAnchor>'
f'<xdr:from><xdr:col>{from_col}</xdr:col><xdr:colOff>0</xdr:colOff>'
f'<xdr:row>{from_row}</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:from>'
f'<xdr:to><xdr:col>{to_col}</xdr:col><xdr:colOff>0</xdr:colOff>'
f'<xdr:row>{to_row}</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:to>'
'<xdr:graphicFrame macro="">'
f'<xdr:nvGraphicFramePr><xdr:cNvPr id="{cnvpr_id}" name="{name}" />'
'<xdr:cNvGraphicFramePr /></xdr:nvGraphicFramePr>'
'<xdr:xfrm><a:off x="0" y="0" /><a:ext cx="0" cy="0" /></xdr:xfrm>'
f'<a:graphic><a:graphicData uri="{CHART_URI}">'
f'<c:chart r:id="{rel_id}" /></a:graphicData></a:graphic>'
'</xdr:graphicFrame><xdr:clientData />'
'</xdr:twoCellAnchor>'
)
doc.send({"command": "raw-set", "part": f"/{sheet}/drawing",
"xpath": "//xdr:wsDr", "action": "append", "xml": xml})
# ---------------------------------------------------------------- chart XML
CHART5_XML = '''
<c:chartSpace>
<c:chart>
<c:title>
<c:tx><c:rich><a:bodyPr /><a:lstStyle />
<a:p><a:pPr><a:defRPr sz="1600" b="1"><a:solidFill><a:srgbClr val="7030A0" /></a:solidFill></a:defRPr></a:pPr>
<a:r><a:rPr lang="en-US" sz="1600" b="1" /><a:t>Spend-Revenue-Market Share Bubble</a:t></a:r></a:p>
</c:rich></c:tx>
<c:overlay val="0" />
</c:title>
<c:plotArea>
<c:layout />
<c:bubbleChart>
<c:varyColors val="0" />
<c:ser>
<c:idx val="0" /><c:order val="0" />
<c:tx><c:strRef><c:f>Analysis!$D$1</c:f></c:strRef></c:tx>
<c:spPr>
<a:solidFill><a:srgbClr val="7030A0"><a:alpha val="60000" /></a:srgbClr></a:solidFill>
<a:ln w="19050"><a:solidFill><a:srgbClr val="7030A0" /></a:solidFill></a:ln>
<a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000"><a:srgbClr val="000000"><a:alpha val="25000" /></a:srgbClr></a:outerShdw></a:effectLst>
</c:spPr>
<c:xVal><c:numRef><c:f>Analysis!$A$2:$A$16</c:f></c:numRef></c:xVal>
<c:yVal><c:numRef><c:f>Analysis!$B$2:$B$16</c:f></c:numRef></c:yVal>
<c:bubbleSize><c:numRef><c:f>Analysis!$D$2:$D$16</c:f></c:numRef></c:bubbleSize>
<c:bubble3D val="1" />
</c:ser>
<c:axId val="300" /><c:axId val="400" />
</c:bubbleChart>
<c:valAx>
<c:axId val="300" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:delete val="0" /><c:axPos val="b" />
<c:title><c:tx><c:rich><a:bodyPr /><a:lstStyle /><a:p><a:pPr><a:defRPr sz="1000" /></a:pPr><a:r><a:rPr lang="en-US" sz="1000" /><a:t>Ad Spend (10K)</a:t></a:r></a:p></c:rich></c:tx></c:title>
<c:numFmt formatCode="#,##0" sourceLinked="0" /><c:crossAx val="400" />
</c:valAx>
<c:valAx>
<c:axId val="400" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:delete val="0" /><c:axPos val="l" />
<c:title><c:tx><c:rich><a:bodyPr rot="-5400000" /><a:lstStyle /><a:p><a:pPr><a:defRPr sz="1000" /></a:pPr><a:r><a:rPr lang="en-US" sz="1000" /><a:t>Sales (10K)</a:t></a:r></a:p></c:rich></c:tx></c:title>
<c:numFmt formatCode="#,##0" sourceLinked="0" /><c:crossAx val="300" />
</c:valAx>
</c:plotArea>
<c:legend><c:legendPos val="b" /><c:overlay val="0" /></c:legend>
<c:plotVisOnly val="1" />
</c:chart>
</c:chartSpace>'''
print("\n==========================================")
print(f"Generating beautiful charts document: {FILE}")
print("==========================================")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet1: Monthly sales data
# ======================================================================
print(" -> Populating Sheet1: Monthly sales data")
s1 = [
header("/Sheet1/A1", "Month", "1F4E79", "FFFFFF", 11),
header("/Sheet1/B1", "East Sales", "2E75B6", "FFFFFF", 11),
header("/Sheet1/C1", "South Sales", "9DC3E6", "1F4E79", 11),
header("/Sheet1/D1", "North Sales", "BDD7EE", "1F4E79", 11),
header("/Sheet1/E1", "Total", "C55A11", "FFFFFF", 11),
header("/Sheet1/F1", "YoY Growth %", "548235", "FFFFFF", 11),
]
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
east = [120, 135, 148, 162, 155, 178, 195, 210, 188, 172, 165, 198]
south = [95, 108, 115, 128, 142, 155, 168, 175, 160, 148, 135, 158]
north = [88, 92, 105, 118, 125, 138, 145, 152, 140, 130, 122, 142]
total = [303, 335, 368, 408, 422, 471, 508, 537, 488, 450, 422, 498]
growth = [5.2, 8.1, 12.3, 15.6, 10.2, 18.5, 22.1, 25.3, 16.8, 11.2, 7.5, 19.8]
for i in range(12):
r = i + 2
s1.append(cell(f"/Sheet1/A{r}", months[i], **{"alignment.horizontal": "center"}))
s1.append(cell(f"/Sheet1/B{r}", east[i], numFmt="#,##0", **{"alignment.horizontal": "center"}))
s1.append(cell(f"/Sheet1/C{r}", south[i], numFmt="#,##0", **{"alignment.horizontal": "center"}))
s1.append(cell(f"/Sheet1/D{r}", north[i], numFmt="#,##0", **{"alignment.horizontal": "center"}))
s1.append(cell(f"/Sheet1/E{r}", total[i], numFmt="#,##0",
**{"font.bold": "true", "alignment.horizontal": "center"}))
s1.append(cell(f"/Sheet1/F{r}", growth[i], numFmt='0.0"%"', **{"alignment.horizontal": "center"}))
doc.batch(s1)
print(" Done: Sheet1 data")
# ======================================================================
# Sheet2: Analysis (scatter/bubble) data
# ======================================================================
print(" -> Populating Sheet2: Analysis data")
s2 = [add_sheet("Analysis")]
for col, title in zip("ABCD", ["Ad Spend (10K)", "Sales (10K)", "Margin %", "Market Share %"]):
s2.append(header(f"/Analysis/{col}1", title, "7030A0", "FFFFFF"))
ad_spend = [10, 15, 22, 28, 35, 42, 50, 58, 65, 72, 80, 88, 95, 105, 115]
sales_rev = [45, 68, 95, 120, 155, 180, 220, 260, 290, 335, 370, 410, 445, 500, 550]
profit = [8.5, 10.2, 12.1, 14.5, 16.8, 15.2, 18.3, 20.1, 19.5, 22.3, 21.8, 24.5, 23.1, 26.8, 28.2]
mkt_share = [2.1, 3.2, 4.5, 5.8, 7.2, 8.5, 10.1, 11.8, 12.5, 14.2, 15.8, 17.5, 18.2, 20.5, 22.1]
for i in range(15):
r = i + 2
for col, vals in zip("ABCD", [ad_spend, sales_rev, profit, mkt_share]):
s2.append(cell(f"/Analysis/{col}{r}", vals[i], **{"alignment.horizontal": "center"}))
doc.batch(s2)
print(" Done: Sheet2 data")
# ======================================================================
# Sheet3: StockData (red up / green down coloring)
# ======================================================================
print(" -> Populating Sheet3: Stock data")
s3 = [add_sheet("StockData")]
for col, title in zip("ABCDEF", ["Date", "Open", "High", "Low", "Close", "Volume (10K)"]):
s3.append(header(f"/StockData/{col}1", title, "C00000", "FFFFFF"))
dates = ["3/1", "3/2", "3/3", "3/4", "3/5", "3/6", "3/7", "3/8", "3/9", "3/10",
"3/11", "3/12", "3/13", "3/14", "3/15", "3/16", "3/17", "3/18", "3/19", "3/20"]
s_open = [52.3, 53.1, 52.8, 54.2, 55.1, 54.5, 56.2, 57.8, 58.5, 57.2,
56.8, 58.3, 59.5, 60.2, 59.8, 61.5, 62.3, 61.8, 63.5, 64.2]
s_high = [53.8, 54.2, 54.5, 55.8, 56.3, 56.8, 58.1, 59.2, 59.8, 58.5,
58.2, 59.8, 61.2, 61.5, 61.8, 63.2, 63.8, 63.5, 65.2, 65.8]
s_low = [51.5, 52.2, 51.8, 53.5, 54.2, 53.8, 55.5, 56.8, 57.2, 56.1,
55.8, 57.5, 58.8, 59.2, 58.5, 60.8, 61.2, 60.5, 62.8, 63.5]
s_close = [53.1, 52.8, 54.2, 55.1, 54.5, 56.2, 57.8, 58.5, 57.2, 56.8,
58.3, 59.5, 60.2, 59.8, 61.5, 62.3, 61.8, 63.5, 64.2, 65.1]
volume = [285, 312, 268, 345, 298, 378, 425, 468, 395, 310,
352, 415, 485, 442, 368, 512, 548, 478, 562, 598]
for i in range(20):
r = i + 2
if s_close[i] > s_open[i]:
color, bg = "FF0000", "FFF2F2" # Up: red
elif s_close[i] < s_open[i]:
color, bg = "008000", "F2FFF2" # Down: green
else:
color, bg = "666666", "F5F5F5" # Flat: gray
common = {"alignment.horizontal": "center", "font.color": color, "fill": bg}
s3.append(cell(f"/StockData/A{r}", dates[i], **common))
s3.append(cell(f"/StockData/B{r}", s_open[i], numFmt="0.00", **common))
s3.append(cell(f"/StockData/C{r}", s_high[i], numFmt="0.00", **common))
s3.append(cell(f"/StockData/D{r}", s_low[i], numFmt="0.00", **common))
s3.append(cell(f"/StockData/E{r}", s_close[i], numFmt="0.00", **common))
s3.append(cell(f"/StockData/F{r}", volume[i], numFmt="#,##0", **common))
doc.batch(s3)
print(" Done: Sheet3 stock data (with red/green coloring)")
# ======================================================================
# Sheet4: Assessment (radar) data
# ======================================================================
print(" -> Populating Sheet4: Capability assessment")
s4 = [add_sheet("Assessment")]
s4.append(header("/Assessment/A1", "Dimension", "002060", "FFFFFF"))
s4.append(header("/Assessment/B1", "Product A", "0070C0", "FFFFFF"))
s4.append(header("/Assessment/C1", "Product B", "00B050", "FFFFFF"))
s4.append(header("/Assessment/D1", "Product C", "FFC000", "000000"))
dims = ["Performance", "Stability", "Usability", "Security",
"Scalability", "Value", "Ecosystem", "Docs"]
pa = [92, 88, 75, 95, 82, 70, 85, 78]
pb = [78, 92, 88, 80, 90, 85, 72, 82]
pc = [85, 76, 92, 72, 78, 92, 88, 70]
for i in range(8):
r = i + 2
for col, vals in zip("ABCD", [dims, pa, pb, pc]):
s4.append(cell(f"/Assessment/{col}{r}", vals[i], **{"alignment.horizontal": "center"}))
doc.batch(s4)
print(" Done: Sheet4 data")
# ======================================================================
# Charts — HIGH-LEVEL API. Each chart is a single `add --type chart` send
# (chartType + a cell dataRange, or inline data for the pie/doughnut whose
# source cells aren't contiguous) with styling props. Exception: Chart 5
# (bubble) stays on raw-set — the high-level command can't map a dataRange
# to a single x/y/size series (multi-point bubble). Positions use
# x/y/width/height in cell units.
# ======================================================================
def add_chart(parent, **props):
doc.send({"command": "add", "parent": parent, "type": "chart", "props": props})
print(" -> Chart 1: Combo (columns + secondary-axis line)")
add_chart("/Sheet1", chartType="combo",
title="Monthly Sales and YoY Growth Trend",
dataRange="Sheet1!A1:F13",
combotypes="column,column,column,column,line",
secondaryaxis="5", colors="2E75B6,9DC3E6,BDD7EE,C55A11,FF0000",
legend="b", axisTitle="Sales (10K)", x="7", y="0", width="11", height="18")
print(" -> Chart 2: 3D bar chart")
add_chart("/Sheet1", chartType="column3d", title="3D Regional Sales Comparison",
dataRange="Sheet1!A1:D13", view3d="15,20,30",
colors="4472C4,ED7D31,70AD47", legend="b",
x="7", y="19", width="11", height="18")
print(" -> Chart 3: Scatter plot + trendline")
add_chart("/Analysis", chartType="scatter", title="Ad Spend vs Sales Correlation",
dataRange="Analysis!A1:B16", trendline="linear", colors="7030A0",
catTitle="Ad Spend (10K)", axisTitle="Sales (10K)", legend="b",
x="5", y="0", width="11", height="18")
print(" -> Chart 4: 3D pie chart (exploded)")
add_chart("/Sheet1", chartType="pie3d", title="July Regional Sales Share (3D)",
categories="East Sales,South Sales,North Sales", series1="Jul:195,168,145",
explosion="10", view3d="30,70,30", dataLabels="percent",
colors="1F4E79,C55A11,548235", legend="b",
x="19", y="0", width="9", height="18")
# Chart 5 (bubble) — raw-set (see note above): high-level can't express a
# single multi-point x/y/size bubble series from a dataRange.
print(" -> Chart 5: Bubble chart (raw-set)")
rel = add_chart_part(doc, "/Analysis")
set_chart_xml(doc, "/Analysis/chart[2]", CHART5_XML)
add_anchor(doc, "Analysis", 5, 19, 16, 37, 3, "Chart 5", rel)
print(" -> Chart 6: Stock OHLC chart")
add_chart("/StockData", chartType="stock", title="Stock Candlestick Chart (OHLC)",
dataRange="StockData!A1:E21", hilowlines="true",
updownbars="100:FF0000:00B050", legend="b",
x="7", y="0", width="13", height="22")
print(" -> Chart 7: Filled radar chart")
add_chart("/Assessment", chartType="radar", radarStyle="filled",
title="Product Capability Radar Comparison", dataRange="Assessment!A1:D9",
colors="4472C4,00B050,FFC000", legend="b",
x="5", y="0", width="11", height="20")
print(" -> Chart 8: Multi-ring doughnut chart")
add_chart("/Sheet1", chartType="doughnut", title="Aug vs Dec Regional Sales Multi-Ring",
categories="East,South,North", series1="Aug:210,175,152",
series2="Dec:198,158,142", dataLabels="percent",
colors="1F4E79,C55A11,548235", legend="b",
x="19", y="19", width="9", height="18")
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nGenerated: {FILE}")
+318
View File
@@ -0,0 +1,318 @@
#!/bin/bash
# Generate a showcase document with beautiful charts
# Contains 8 chart types: combo chart, 3D bar, scatter+trendline, 3D pie, bubble, stock OHLC, filled radar, multi-ring doughnut
# 4 Sheets: monthly sales, analysis data, stock data, capability assessment
# 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.
XLSX="$(dirname "$0")/charts.xlsx"
echo ""
echo "=========================================="
echo "Generating beautiful charts document: $XLSX"
echo "=========================================="
rm -f "$XLSX"
officecli create "$XLSX"
officecli open "$XLSX"
###############################################################################
# Sheet1: Monthly sales data
###############################################################################
echo " -> Populating Sheet1: Monthly sales data"
officecli set "$XLSX" '/Sheet1/A1' --prop value="Month" --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF --prop font.size=11 --prop alignment.horizontal=center
officecli set "$XLSX" '/Sheet1/B1' --prop value="East Sales" --prop font.bold=true --prop fill=2E75B6 --prop font.color=FFFFFF --prop font.size=11 --prop alignment.horizontal=center
officecli set "$XLSX" '/Sheet1/C1' --prop value="South Sales" --prop font.bold=true --prop fill=9DC3E6 --prop font.color=1F4E79 --prop font.size=11 --prop alignment.horizontal=center
officecli set "$XLSX" '/Sheet1/D1' --prop value="North Sales" --prop font.bold=true --prop fill=BDD7EE --prop font.color=1F4E79 --prop font.size=11 --prop alignment.horizontal=center
officecli set "$XLSX" '/Sheet1/E1' --prop value="Total" --prop font.bold=true --prop fill=C55A11 --prop font.color=FFFFFF --prop font.size=11 --prop alignment.horizontal=center
officecli set "$XLSX" '/Sheet1/F1' --prop value="YoY Growth %" --prop font.bold=true --prop fill=548235 --prop font.color=FFFFFF --prop font.size=11 --prop alignment.horizontal=center
MONTHS=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
EAST=(120 135 148 162 155 178 195 210 188 172 165 198)
SOUTH=(95 108 115 128 142 155 168 175 160 148 135 158)
NORTH=(88 92 105 118 125 138 145 152 140 130 122 142)
TOTAL=(303 335 368 408 422 471 508 537 488 450 422 498)
GROWTH=(5.2 8.1 12.3 15.6 10.2 18.5 22.1 25.3 16.8 11.2 7.5 19.8)
for i in $(seq 0 11); do
row=$((i + 2))
officecli set "$XLSX" "/Sheet1/A${row}" --prop "value=${MONTHS[$i]}" --prop alignment.horizontal=center
officecli set "$XLSX" "/Sheet1/B${row}" --prop "value=${EAST[$i]}" --prop 'numFmt=#,##0' --prop alignment.horizontal=center
officecli set "$XLSX" "/Sheet1/C${row}" --prop "value=${SOUTH[$i]}" --prop 'numFmt=#,##0' --prop alignment.horizontal=center
officecli set "$XLSX" "/Sheet1/D${row}" --prop "value=${NORTH[$i]}" --prop 'numFmt=#,##0' --prop alignment.horizontal=center
officecli set "$XLSX" "/Sheet1/E${row}" --prop "value=${TOTAL[$i]}" --prop 'numFmt=#,##0' --prop font.bold=true --prop alignment.horizontal=center
officecli set "$XLSX" "/Sheet1/F${row}" --prop "value=${GROWTH[$i]}" --prop 'numFmt=0.0"%"' --prop alignment.horizontal=center
done
echo " Done: Sheet1 data"
###############################################################################
# Sheet2: Scatter/bubble chart data
###############################################################################
echo " -> Populating Sheet2: Analysis data"
officecli add "$XLSX" / --type sheet --prop name=Analysis
officecli set "$XLSX" '/Analysis/A1' --prop value="Ad Spend (10K)" --prop font.bold=true --prop fill=7030A0 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/Analysis/B1' --prop value="Sales (10K)" --prop font.bold=true --prop fill=7030A0 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/Analysis/C1' --prop value="Margin %" --prop font.bold=true --prop fill=7030A0 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/Analysis/D1' --prop value="Market Share %" --prop font.bold=true --prop fill=7030A0 --prop font.color=FFFFFF --prop alignment.horizontal=center
AD_SPEND=(10 15 22 28 35 42 50 58 65 72 80 88 95 105 115)
SALES_REV=(45 68 95 120 155 180 220 260 290 335 370 410 445 500 550)
PROFIT=(8.5 10.2 12.1 14.5 16.8 15.2 18.3 20.1 19.5 22.3 21.8 24.5 23.1 26.8 28.2)
MKT_SHARE=(2.1 3.2 4.5 5.8 7.2 8.5 10.1 11.8 12.5 14.2 15.8 17.5 18.2 20.5 22.1)
for i in $(seq 0 14); do
row=$((i + 2))
officecli set "$XLSX" "/Analysis/A${row}" --prop "value=${AD_SPEND[$i]}" --prop alignment.horizontal=center
officecli set "$XLSX" "/Analysis/B${row}" --prop "value=${SALES_REV[$i]}" --prop alignment.horizontal=center
officecli set "$XLSX" "/Analysis/C${row}" --prop "value=${PROFIT[$i]}" --prop alignment.horizontal=center
officecli set "$XLSX" "/Analysis/D${row}" --prop "value=${MKT_SHARE[$i]}" --prop alignment.horizontal=center
done
echo " Done: Sheet2 data"
###############################################################################
# Sheet3: Stock data (with red/green coloring)
###############################################################################
echo " -> Populating Sheet3: Stock data"
officecli add "$XLSX" / --type sheet --prop name=StockData
officecli set "$XLSX" '/StockData/A1' --prop value="Date" --prop font.bold=true --prop fill=C00000 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/StockData/B1' --prop value="Open" --prop font.bold=true --prop fill=C00000 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/StockData/C1' --prop value="High" --prop font.bold=true --prop fill=C00000 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/StockData/D1' --prop value="Low" --prop font.bold=true --prop fill=C00000 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/StockData/E1' --prop value="Close" --prop font.bold=true --prop fill=C00000 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/StockData/F1' --prop value="Volume (10K)" --prop font.bold=true --prop fill=C00000 --prop font.color=FFFFFF --prop alignment.horizontal=center
DATES=("3/1" "3/2" "3/3" "3/4" "3/5" "3/6" "3/7" "3/8" "3/9" "3/10" "3/11" "3/12" "3/13" "3/14" "3/15" "3/16" "3/17" "3/18" "3/19" "3/20")
OPEN=(52.3 53.1 52.8 54.2 55.1 54.5 56.2 57.8 58.5 57.2 56.8 58.3 59.5 60.2 59.8 61.5 62.3 61.8 63.5 64.2)
HIGH=(53.8 54.2 54.5 55.8 56.3 56.8 58.1 59.2 59.8 58.5 58.2 59.8 61.2 61.5 61.8 63.2 63.8 63.5 65.2 65.8)
LOW=(51.5 52.2 51.8 53.5 54.2 53.8 55.5 56.8 57.2 56.1 55.8 57.5 58.8 59.2 58.5 60.8 61.2 60.5 62.8 63.5)
CLOSE=(53.1 52.8 54.2 55.1 54.5 56.2 57.8 58.5 57.2 56.8 58.3 59.5 60.2 59.8 61.5 62.3 61.8 63.5 64.2 65.1)
VOLUME=(285 312 268 345 298 378 425 468 395 310 352 415 485 442 368 512 548 478 562 598)
for i in $(seq 0 19); do
row=$((i + 2))
open=${OPEN[$i]}
close=${CLOSE[$i]}
if (( $(echo "$close > $open" | bc -l) )); then
COLOR="FF0000"; BG="FFF2F2" # Up: red
elif (( $(echo "$close < $open" | bc -l) )); then
COLOR="008000"; BG="F2FFF2" # Down: green
else
COLOR="666666"; BG="F5F5F5" # Flat: gray
fi
officecli set "$XLSX" "/StockData/A${row}" --prop "value=${DATES[$i]}" --prop alignment.horizontal=center --prop "font.color=${COLOR}" --prop "fill=${BG}"
officecli set "$XLSX" "/StockData/B${row}" --prop "value=${OPEN[$i]}" --prop 'numFmt=0.00' --prop alignment.horizontal=center --prop "font.color=${COLOR}" --prop "fill=${BG}"
officecli set "$XLSX" "/StockData/C${row}" --prop "value=${HIGH[$i]}" --prop 'numFmt=0.00' --prop alignment.horizontal=center --prop "font.color=${COLOR}" --prop "fill=${BG}"
officecli set "$XLSX" "/StockData/D${row}" --prop "value=${LOW[$i]}" --prop 'numFmt=0.00' --prop alignment.horizontal=center --prop "font.color=${COLOR}" --prop "fill=${BG}"
officecli set "$XLSX" "/StockData/E${row}" --prop "value=${CLOSE[$i]}" --prop 'numFmt=0.00' --prop alignment.horizontal=center --prop "font.color=${COLOR}" --prop "fill=${BG}"
officecli set "$XLSX" "/StockData/F${row}" --prop "value=${VOLUME[$i]}" --prop 'numFmt=#,##0' --prop alignment.horizontal=center --prop "font.color=${COLOR}" --prop "fill=${BG}"
done
echo " Done: Sheet3 stock data (with red/green coloring)"
###############################################################################
# Sheet4: Capability radar chart data
###############################################################################
echo " -> Populating Sheet4: Capability assessment"
officecli add "$XLSX" / --type sheet --prop name=Assessment
officecli set "$XLSX" '/Assessment/A1' --prop value="Dimension" --prop font.bold=true --prop fill=002060 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/Assessment/B1' --prop value="Product A" --prop font.bold=true --prop fill=0070C0 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/Assessment/C1' --prop value="Product B" --prop font.bold=true --prop fill=00B050 --prop font.color=FFFFFF --prop alignment.horizontal=center
officecli set "$XLSX" '/Assessment/D1' --prop value="Product C" --prop font.bold=true --prop fill=FFC000 --prop font.color=000000 --prop alignment.horizontal=center
DIMS=("Performance" "Stability" "Usability" "Security" "Scalability" "Value" "Ecosystem" "Docs")
PA=(92 88 75 95 82 70 85 78)
PB=(78 92 88 80 90 85 72 82)
PC=(85 76 92 72 78 92 88 70)
for i in $(seq 0 7); do
row=$((i + 2))
officecli set "$XLSX" "/Assessment/A${row}" --prop "value=${DIMS[$i]}" --prop alignment.horizontal=center
officecli set "$XLSX" "/Assessment/B${row}" --prop "value=${PA[$i]}" --prop alignment.horizontal=center
officecli set "$XLSX" "/Assessment/C${row}" --prop "value=${PB[$i]}" --prop alignment.horizontal=center
officecli set "$XLSX" "/Assessment/D${row}" --prop "value=${PC[$i]}" --prop alignment.horizontal=center
done
echo " Done: Sheet4 data"
###############################################################################
# Charts 1-8 — HIGH-LEVEL API (`officecli add --type chart`).
# Each chart is one `add` call: chartType + a cell dataRange (or inline data for
# the pie/doughnut, whose source cells aren't contiguous) + styling props
# (title, colors, legend, 3D view, trendline, secondary axis, radar fill, stock
# up/down bars). Positioned with x/y/width/height in cell units.
# Exception: Chart 5 (bubble) stays on raw-set — the high-level command can't yet
# map a dataRange to a single x/y/size series (see its note below).
###############################################################################
# Chart 1: Combo — regional sales columns + YoY-growth line on a secondary axis.
echo " -> Chart 1: Combo chart (columns + secondary-axis line)"
officecli add "$XLSX" /Sheet1 --type chart \
--prop chartType=combo \
--prop title="Monthly Sales and YoY Growth Trend" \
--prop dataRange=Sheet1!A1:F13 \
--prop combotypes=column,column,column,column,line \
--prop secondaryaxis=5 \
--prop colors=2E75B6,9DC3E6,BDD7EE,C55A11,FF0000 \
--prop legend=b --prop axisTitle="Sales (10K)" \
--prop x=7 --prop y=0 --prop width=11 --prop height=18
# Chart 2: 3D clustered column — regional comparison.
echo " -> Chart 2: 3D bar chart"
officecli add "$XLSX" /Sheet1 --type chart \
--prop chartType=column3d \
--prop title="3D Regional Sales Comparison" \
--prop dataRange=Sheet1!A1:D13 \
--prop view3d=15,20,30 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop legend=b \
--prop x=7 --prop y=19 --prop width=11 --prop height=18
# Chart 3: Scatter + linear trendline — ad spend vs sales.
echo " -> Chart 3: Scatter plot + trendline"
officecli add "$XLSX" /Analysis --type chart \
--prop chartType=scatter \
--prop title="Ad Spend vs Sales Correlation" \
--prop dataRange=Analysis!A1:B16 \
--prop trendline=linear \
--prop colors=7030A0 \
--prop catTitle="Ad Spend (10K)" --prop axisTitle="Sales (10K)" \
--prop legend=b \
--prop x=5 --prop y=0 --prop width=11 --prop height=18
# Chart 4: Exploded 3D pie — July regional share. Values live in one row
# (B8:D8) with category labels in another (B1:D1), so they're passed inline.
echo " -> Chart 4: 3D pie chart (exploded)"
officecli add "$XLSX" /Sheet1 --type chart \
--prop chartType=pie3d \
--prop title="July Regional Sales Share (3D)" \
--prop categories="East Sales,South Sales,North Sales" \
--prop series1="Jul:195,168,145" \
--prop explosion=10 --prop view3d=30,70,30 \
--prop dataLabels=percent \
--prop colors=1F4E79,C55A11,548235 \
--prop legend=b \
--prop x=19 --prop y=0 --prop width=9 --prop height=18
# Chart 5: Bubble — ad spend (x) vs sales (y), bubble size = market share.
# KEPT ON raw-set: the high-level `add --type chart --prop chartType=bubble`
# reads a multi-column dataRange as several y-series sharing column A as x — it
# cannot map three columns to a single x / y / size series (multi-point bubble).
# Until that mapping exists, the faithful single-series bubble needs raw XML.
echo " -> Chart 5: Bubble chart (raw-set — see note)"
CHART5_REL=$(officecli add-part "$XLSX" /Analysis --type chart 2>&1 | grep -o 'relId=[^ ]*' | cut -d= -f2)
officecli raw-set "$XLSX" '/Analysis/chart[2]' --xpath "/c:chartSpace" --action replace --xml '
<c:chartSpace>
<c:chart>
<c:title>
<c:tx><c:rich><a:bodyPr /><a:lstStyle />
<a:p><a:pPr><a:defRPr sz="1600" b="1"><a:solidFill><a:srgbClr val="7030A0" /></a:solidFill></a:defRPr></a:pPr>
<a:r><a:rPr lang="en-US" sz="1600" b="1" /><a:t>Spend-Revenue-Market Share Bubble</a:t></a:r></a:p>
</c:rich></c:tx>
<c:overlay val="0" />
</c:title>
<c:plotArea>
<c:layout />
<c:bubbleChart>
<c:varyColors val="0" />
<c:ser>
<c:idx val="0" /><c:order val="0" />
<c:tx><c:strRef><c:f>Analysis!$D$1</c:f></c:strRef></c:tx>
<c:spPr>
<a:solidFill><a:srgbClr val="7030A0"><a:alpha val="60000" /></a:srgbClr></a:solidFill>
<a:ln w="19050"><a:solidFill><a:srgbClr val="7030A0" /></a:solidFill></a:ln>
</c:spPr>
<c:xVal><c:numRef><c:f>Analysis!$A$2:$A$16</c:f></c:numRef></c:xVal>
<c:yVal><c:numRef><c:f>Analysis!$B$2:$B$16</c:f></c:numRef></c:yVal>
<c:bubbleSize><c:numRef><c:f>Analysis!$D$2:$D$16</c:f></c:numRef></c:bubbleSize>
<c:bubble3D val="1" />
</c:ser>
<c:axId val="300" /><c:axId val="400" />
</c:bubbleChart>
<c:valAx>
<c:axId val="300" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:delete val="0" /><c:axPos val="b" />
<c:title><c:tx><c:rich><a:bodyPr /><a:lstStyle /><a:p><a:pPr><a:defRPr sz="1000" /></a:pPr><a:r><a:rPr lang="en-US" sz="1000" /><a:t>Ad Spend (10K)</a:t></a:r></a:p></c:rich></c:tx></c:title>
<c:numFmt formatCode="#,##0" sourceLinked="0" /><c:crossAx val="400" />
</c:valAx>
<c:valAx>
<c:axId val="400" /><c:scaling><c:orientation val="minMax" /></c:scaling><c:delete val="0" /><c:axPos val="l" />
<c:title><c:tx><c:rich><a:bodyPr rot="-5400000" /><a:lstStyle /><a:p><a:pPr><a:defRPr sz="1000" /></a:pPr><a:r><a:rPr lang="en-US" sz="1000" /><a:t>Sales (10K)</a:t></a:r></a:p></c:rich></c:tx></c:title>
<c:numFmt formatCode="#,##0" sourceLinked="0" /><c:crossAx val="300" />
</c:valAx>
</c:plotArea>
<c:legend><c:legendPos val="b" /><c:overlay val="0" /></c:legend>
<c:plotVisOnly val="1" />
</c:chart>
</c:chartSpace>'
officecli raw-set "$XLSX" '/Analysis/drawing' --xpath "//xdr:wsDr" --action append --xml "
<xdr:twoCellAnchor>
<xdr:from><xdr:col>5</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>19</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:from>
<xdr:to><xdr:col>16</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>37</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:to>
<xdr:graphicFrame macro=\"\">
<xdr:nvGraphicFramePr><xdr:cNvPr id=\"3\" name=\"Chart 5\" /><xdr:cNvGraphicFramePr /></xdr:nvGraphicFramePr>
<xdr:xfrm><a:off x=\"0\" y=\"0\" /><a:ext cx=\"0\" cy=\"0\" /></xdr:xfrm>
<a:graphic><a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/chart\"><c:chart r:id=\"${CHART5_REL}\" /></a:graphicData></a:graphic>
</xdr:graphicFrame>
<xdr:clientData />
</xdr:twoCellAnchor>"
# Chart 6: Stock OHLC candlestick — hi-low lines + up/down bars (red up, green down).
echo " -> Chart 6: Stock OHLC chart"
officecli add "$XLSX" /StockData --type chart \
--prop chartType=stock \
--prop title="Stock Candlestick Chart (OHLC)" \
--prop dataRange=StockData!A1:E21 \
--prop hilowlines=true \
--prop updownbars=100:FF0000:00B050 \
--prop legend=b \
--prop x=7 --prop y=0 --prop width=13 --prop height=22
# Chart 7: Filled radar — product capability comparison.
echo " -> Chart 7: Filled radar chart"
officecli add "$XLSX" /Assessment --type chart \
--prop chartType=radar --prop radarStyle=filled \
--prop title="Product Capability Radar Comparison" \
--prop dataRange=Assessment!A1:D9 \
--prop colors=4472C4,00B050,FFC000 \
--prop legend=b \
--prop x=5 --prop y=0 --prop width=11 --prop height=20
# Chart 8: Multi-ring doughnut — Aug vs Dec regional share (two rings). The two
# source rows aren't adjacent, so the ring values are passed inline.
echo " -> Chart 8: Multi-ring doughnut chart"
officecli add "$XLSX" /Sheet1 --type chart \
--prop chartType=doughnut \
--prop title="Aug vs Dec Regional Sales Multi-Ring" \
--prop categories="East,South,North" \
--prop series1="Aug:210,175,152" \
--prop series2="Dec:198,158,142" \
--prop dataLabels=percent \
--prop colors=1F4E79,C55A11,548235 \
--prop legend=b \
--prop x=19 --prop y=19 --prop width=9 --prop height=18
###############################################################################
# Validation
###############################################################################
officecli close "$XLSX"
echo ""
echo "=========================================="
echo "Validating file"
echo "=========================================="
officecli validate "$XLSX"
officecli view "$XLSX" outline
echo ""
ls -lh "$XLSX"
echo ""
echo "All done! 8 chart types generated"
Binary file not shown.
+151
View File
@@ -0,0 +1,151 @@
# Advanced Charts Showcase
This demo consists of three files that work together:
- **charts-advanced.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-advanced.xlsx** — The generated workbook with 3 sheets (12 charts total).
- **charts-advanced.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-advanced.py
# → charts-advanced.xlsx
```
## Chart Sheets
### Sheet: 1-Scatter & Bubble
Four charts covering scatter plot and bubble chart fundamentals.
```bash
# Scatter with circle markers and connecting lines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop categories=1,2,3,4,5,6 \
--prop series1="SeriesA:10,25,15,40,30,50" \
--prop series2="SeriesB:5,18,22,35,28,42" \
--prop colors=4472C4,ED7D31 \
--prop marker=circle --prop markerSize=8 \
--prop lineWidth=1.5 --prop legend=bottom
# Scatter with smooth curve and reference line
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop smooth=true --prop marker=diamond --prop markerSize=7 \
--prop referenceLine=25:FF0000:Target:dash \
--prop axisTitle=Value --prop catTitle=Period
# Scatter with per-series marker styles
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop series1.marker=square --prop series2.marker=triangle \
--prop series3.marker=star --prop markerSize=9 \
--prop lineWidth=1 --prop gridlines=D9D9D9:0.5:dot
# Bubble chart with scale control
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop bubbleScale=80 --prop legend=right \
--prop axisTitle=Revenue --prop catTitle=Market Size
```
**Features:** `scatter`, `bubble`, `marker` (circle, diamond, square, triangle, star), `markerSize`, `series{N}.marker` (per-series), `smooth`, `lineWidth`, `referenceLine`, `bubbleScale`, `catTitle`, `axisTitle`, `gridlines`, `legend`
### Sheet: 2-Combo & Radar
Four charts covering combo (bar+line) and radar (spider) charts.
```bash
# Combo chart with comboSplit (bar+line split)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop comboSplit=2 \
--prop series1="Revenue:120,145,132,168,155,180" \
--prop series2="Expenses:80,92,85,98,90,105" \
--prop series3="Growth:8,12,6,15,10,16" \
--prop legend=bottom --prop axisTitle=Amount --prop catTitle=Month
# Combo with secondary axis
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop comboSplit=1 --prop secondaryAxis=2 \
--prop series1="Volume:1200,1450,1320,1680" \
--prop series2="AvgPrice:45,52,48,58"
# Combo with per-series type control (combotypes)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop combotypes=column,column,line,area
# Radar chart with radarStyle=marker
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop categories=Speed,Strength,Stamina,Agility,Accuracy \
--prop series1="AthleteA:80,65,90,75,85" \
--prop series2="AthleteB:70,85,60,90,70"
```
**Features:** `combo`, `comboSplit` (bar/line split point), `combotypes` (per-series type: column/line/area), `secondaryAxis`, `radar`, `radarStyle` (marker/filled/standard), `categories` as spoke labels
### Sheet: 3-Stock & Radar
Four charts covering stock (OHLC) and additional radar/bubble variants.
```bash
# Stock OHLC chart with 4 series (Open/High/Low/Close)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop series1="Open:145,148,150,147,152" \
--prop series2="High:152,155,157,153,160" \
--prop series3="Low:143,146,148,144,150" \
--prop series4="Close:148,150,147,152,158" \
--prop catTitle=Day --prop axisTitle=Price
# Stock chart — weekly OHLC with gridlines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop gridlines=E0E0E0:0.75
# Radar — filled style with transparency
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop transparency=40 --prop legend=bottom
# Bubble with single series and axis titles
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop bubbleScale=100 --prop legend=none \
--prop axisTitle=Revenue --prop catTitle=Market Size
```
**Features:** `stock` (OHLC format: 4 series = Open/High/Low/Close), `radarStyle=filled`, `transparency` (fill alpha on radar), `bubbleScale=100`, `legend=none`, `gridlines` styling
## Complete Feature Coverage
| Feature | Sheet |
|---------|-------|
| **Chart types:** scatter, bubble, combo, radar, stock | 1, 2, 3 |
| **Scatter:** marker styles, smooth, lineWidth | 1 |
| **Bubble:** bubbleScale, single/multi-series | 1, 3 |
| **Combo:** comboSplit, combotypes, secondaryAxis | 2 |
| **Radar:** radarStyle (marker, filled, standard), transparency | 2, 3 |
| **Stock:** OHLC (4 series), gridlines | 3 |
| **Markers:** circle, diamond, square, triangle, star, per-series | 1 |
| **Data input:** inline series, categories | 1, 2, 3 |
| **Axis:** catTitle, axisTitle | 1, 2, 3 |
| **Legend:** position (bottom, right, none) | 1, 2, 3 |
| **Reference line:** value:color:label:dash | 1 |
| **Gridlines:** color:width:dash | 1, 3 |
## Inspect the Generated File
```bash
officecli query charts-advanced.xlsx chart
officecli get charts-advanced.xlsx "/1-Scatter & Bubble/chart[1]"
```
+310
View File
@@ -0,0 +1,310 @@
#!/usr/bin/env python3
"""
Advanced Charts Showcase — generates charts-advanced.xlsx exercising the
xlsx `chart` element's advanced chart types: scatter, bubble, combo, radar,
and stock (OHLC). 12 charts across 3 sheets.
SDK twin of charts-advanced.sh (officecli CLI). Both produce an equivalent
charts-advanced.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in `doc.batch(...)` round-trips. Each
item is the same `{"command","parent","type","props"}` dict you'd put in an
`officecli batch` list.
3 sheets, by chart family:
1-Scatter & Bubble — scatter(markers/line, smooth+trendline, per-series
markers) + bubble(market size)
2-Combo & Radar — combo(comboSplit, secondaryAxis, combotypes) +
radar(marker style)
3-Stock & Radar — stock(daily OHLC, weekly OHLC) + radar(filled) +
bubble(single series)
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-advanced.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-advanced.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(sheet, **props):
"""One `add chart` item in batch-shape (parent = the sheet)."""
return {"command": "add", "parent": f"/{sheet}", "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet: 1-Scatter & Bubble
# ======================================================================
print("--- 1-Scatter & Bubble ---")
items = [add_sheet("1-Scatter & Bubble")]
# ------------------------------------------------------------------
# Chart 1: Scatter with markers — circle markers, line connecting points
# Features: chartType=scatter, categories as X values, marker=circle,
# markerSize, lineWidth, legend=bottom
# ------------------------------------------------------------------
items.append(chart(
"1-Scatter & Bubble",
chartType="scatter",
title="Scatter: Markers & Line",
categories="1,2,3,4,5,6",
series1="SeriesA:10,25,15,40,30,50",
series2="SeriesB:5,18,22,35,28,42",
colors="4472C4,ED7D31",
x="0", y="0", width="12", height="18",
marker="circle", markerSize="8",
lineWidth="1.5",
legend="bottom",
))
# ------------------------------------------------------------------
# Chart 2: Scatter with smooth curve and trendline (reference line)
# Features: smooth=true (smooth curve), marker=diamond,
# referenceLine (trendline overlay), axisTitle, catTitle
# ------------------------------------------------------------------
items.append(chart(
"1-Scatter & Bubble",
chartType="scatter",
title="Scatter: Smooth + Trendline",
categories="1,2,3,4,5,6,7,8",
series1="Growth:3,7,12,20,28,35,40,45",
colors="70AD47",
x="13", y="0", width="12", height="18",
smooth="true",
marker="diamond", markerSize="7",
referenceLine="25:FF0000:Target:dash",
axisTitle="Value", catTitle="Period",
))
# ------------------------------------------------------------------
# Chart 3: Scatter with varied marker styles per series
# Features: per-series marker style (series{N}.marker), gridlines styling
# ------------------------------------------------------------------
items.append(chart(
"1-Scatter & Bubble",
chartType="scatter",
title="Scatter: Marker Styles",
categories="10,20,30,40,50",
series1="Squares:8,22,18,35,30",
series2="Triangles:15,10,28,20,42",
series3="Stars:5,30,12,45,25",
colors="4472C4,ED7D31,70AD47",
x="0", y="19", width="12", height="18",
**{"series1.marker": "square",
"series2.marker": "triangle",
"series3.marker": "star"},
markerSize="9",
lineWidth="1",
gridlines="D9D9D9:0.5:dot",
))
# ------------------------------------------------------------------
# Chart 4: Bubble chart with size data
# Features: chartType=bubble, categories as X, series as Y values,
# bubble sizes default to Y values, bubbleScale to control sizing
# ------------------------------------------------------------------
items.append(chart(
"1-Scatter & Bubble",
chartType="bubble",
title="Bubble: Market Size",
categories="10,25,40,60,80",
series1="ProductA:30,50,20,70,45",
series2="ProductB:15,35,55,40,60",
colors="4472C4,ED7D31",
x="13", y="19", width="12", height="18",
bubbleScale="80",
legend="right",
))
doc.batch(items)
# ======================================================================
# Sheet: 2-Combo & Radar
# ======================================================================
print("--- 2-Combo & Radar ---")
items = [add_sheet("2-Combo & Radar")]
# ------------------------------------------------------------------
# Chart 1: Combo chart — bar+line with comboSplit
# Features: chartType=combo, comboSplit=2 (first 2 series as bars,
# remaining as lines), categories as X labels
# ------------------------------------------------------------------
items.append(chart(
"2-Combo & Radar",
chartType="combo",
title="Combo: Sales (Bar) + Growth % (Line)",
categories="Jan,Feb,Mar,Apr,May,Jun",
series1="Revenue:120,145,132,168,155,180",
series2="Expenses:80,92,85,98,90,105",
series3="Growth:8,12,6,15,10,16",
colors="4472C4,ED7D31,70AD47",
x="0", y="0", width="12", height="18",
comboSplit="2",
legend="bottom",
axisTitle="Amount", catTitle="Month",
))
# ------------------------------------------------------------------
# Chart 2: Combo with secondary axis
# Features: comboSplit=1, secondaryAxis=2 (series 2 on right Y-axis)
# ------------------------------------------------------------------
items.append(chart(
"2-Combo & Radar",
chartType="combo",
title="Combo: Volume (Bar) + Price (Line, 2nd Axis)",
categories="Q1,Q2,Q3,Q4",
series1="Volume:1200,1450,1320,1680",
series2="AvgPrice:45,52,48,58",
colors="5B9BD5,FF0000",
x="13", y="0", width="12", height="18",
comboSplit="1",
secondaryAxis="2",
legend="bottom",
))
# ------------------------------------------------------------------
# Chart 3: Combo with combotypes — per-series type control
# Features: combotypes (per-series type: column, column, line, area)
# ------------------------------------------------------------------
items.append(chart(
"2-Combo & Radar",
chartType="combo",
title="Combo: Mixed Types (combotypes)",
categories="A,B,C,D,E",
series1="Bars:30,45,28,52,40",
series2="MoreBars:20,30,22,38,28",
series3="Lines:12,18,15,22,16",
series4="Area:8,12,10,15,11",
colors="4472C4,5B9BD5,ED7D31,70AD47",
x="0", y="19", width="12", height="18",
combotypes="column,column,line,area",
legend="bottom",
))
# ------------------------------------------------------------------
# Chart 4: Radar (spider) chart with multiple series
# Features: chartType=radar, categories as spoke labels,
# multiple series, radarStyle=marker
# ------------------------------------------------------------------
items.append(chart(
"2-Combo & Radar",
chartType="radar",
title="Radar: Skills Comparison",
categories="Speed,Strength,Stamina,Agility,Accuracy",
series1="AthleteA:80,65,90,75,85",
series2="AthleteB:70,85,60,90,70",
series3="AthleteC:90,70,75,65,80",
colors="4472C4,ED7D31,70AD47",
x="13", y="19", width="12", height="18",
radarStyle="marker",
legend="bottom",
))
doc.batch(items)
# ======================================================================
# Sheet: 3-Stock & Radar
# ======================================================================
print("--- 3-Stock & Radar ---")
items = [add_sheet("3-Stock & Radar")]
# ------------------------------------------------------------------
# Chart 1: Stock (OHLC) chart — Open-High-Low-Close
# Features: chartType=stock, 4 series (Open/High/Low/Close),
# categories as date labels, catTitle, axisTitle
# ------------------------------------------------------------------
items.append(chart(
"3-Stock & Radar",
chartType="stock",
title="Stock: OHLC Daily Prices",
categories="Mon,Tue,Wed,Thu,Fri",
series1="Open:145,148,150,147,152",
series2="High:152,155,157,153,160",
series3="Low:143,146,148,144,150",
series4="Close:148,150,147,152,158",
x="0", y="0", width="14", height="18",
legend="bottom",
catTitle="Day", axisTitle="Price",
))
# ------------------------------------------------------------------
# Chart 2: Stock chart — weekly OHLC with date categories
# Features: stock chart with 6 weeks of OHLC, gridlines styling
# ------------------------------------------------------------------
items.append(chart(
"3-Stock & Radar",
chartType="stock",
title="Stock: Weekly OHLC (6 Weeks)",
categories="W1,W2,W3,W4,W5,W6",
series1="Open:100,104,102,108,105,110",
series2="High:106,110,108,115,112,118",
series3="Low:98,101,100,105,103,107",
series4="Close:104,102,108,105,110,115",
x="15", y="0", width="14", height="18",
gridlines="E0E0E0:0.75",
legend="bottom",
))
# ------------------------------------------------------------------
# Chart 3: Radar — filled style (spider web)
# Features: radarStyle=filled, transparency (fill alpha), multiple series
# ------------------------------------------------------------------
items.append(chart(
"3-Stock & Radar",
chartType="radar",
title="Radar: Product Ratings (Filled)",
categories="Quality,Price,Design,Support,Delivery",
series1="BrandX:85,70,90,75,80",
series2="BrandY:70,90,65,85,75",
colors="4472C4,70AD47",
x="0", y="19", width="14", height="18",
radarStyle="filled",
transparency="40",
legend="bottom",
))
# ------------------------------------------------------------------
# Chart 4: Bubble — single series with explicit large differences in size
# Features: bubble with single series, bubbleScale=100, legend=none,
# axisTitle and catTitle labels
# ------------------------------------------------------------------
items.append(chart(
"3-Stock & Radar",
chartType="bubble",
title="Bubble: Regional Opportunity",
categories="5,15,30,50,70,90",
series1="Regions:20,45,30,80,55,65",
colors="4472C4",
x="15", y="19", width="14", height="18",
bubbleScale="100",
legend="none",
axisTitle="Revenue", catTitle="Market Size",
))
doc.batch(items)
doc.send({"command": "save"})
print(f"Generated: {FILE}")
print(" 3 sheets (1-Scatter & Bubble, 2-Combo & Radar, 3-Stock & Radar)")
print(" 12 charts total: scatter(3), bubble(2), combo(3), radar(2), stock(2)")
+241
View File
@@ -0,0 +1,241 @@
#!/bin/bash
# Advanced Charts Showcase — generates charts-advanced.xlsx exercising the
# xlsx `chart` element's advanced chart types: scatter, bubble, combo, radar,
# and stock (OHLC). 12 charts across 3 sheets.
#
# CLI twin of charts-advanced.py (officecli Python SDK). Both produce an
# equivalent charts-advanced.xlsx.
#
# Usage:
# ./charts-advanced.sh
# 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")/charts-advanced.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet: 1-Scatter & Bubble
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Scatter & Bubble"
# --------------------------------------------------------------------------
# Chart 1: Scatter with markers — circle markers, line connecting points
# Features: chartType=scatter, categories as X values, marker=circle,
# markerSize, lineWidth, legend=bottom
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Scatter & Bubble" --type chart \
--prop chartType=scatter \
--prop title="Scatter: Markers & Line" \
--prop categories=1,2,3,4,5,6 \
--prop series1=SeriesA:10,25,15,40,30,50 \
--prop series2=SeriesB:5,18,22,35,28,42 \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=circle --prop markerSize=8 \
--prop lineWidth=1.5 \
--prop legend=bottom
# --------------------------------------------------------------------------
# Chart 2: Scatter with smooth curve and trendline (reference line)
# Features: smooth=true (smooth curve), marker=diamond,
# referenceLine (trendline overlay), axisTitle, catTitle
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Scatter & Bubble" --type chart \
--prop chartType=scatter \
--prop title="Scatter: Smooth + Trendline" \
--prop categories=1,2,3,4,5,6,7,8 \
--prop series1=Growth:3,7,12,20,28,35,40,45 \
--prop colors=70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop smooth=true \
--prop marker=diamond --prop markerSize=7 \
--prop referenceLine=25:FF0000:Target:dash \
--prop axisTitle=Value --prop catTitle=Period
# --------------------------------------------------------------------------
# Chart 3: Scatter with varied marker styles per series
# Features: per-series marker style (series{N}.marker), gridlines styling
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Scatter & Bubble" --type chart \
--prop chartType=scatter \
--prop title="Scatter: Marker Styles" \
--prop categories=10,20,30,40,50 \
--prop series1=Squares:8,22,18,35,30 \
--prop series2=Triangles:15,10,28,20,42 \
--prop series3=Stars:5,30,12,45,25 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop series1.marker=square \
--prop series2.marker=triangle \
--prop series3.marker=star \
--prop markerSize=9 \
--prop lineWidth=1 \
--prop gridlines=D9D9D9:0.5:dot
# --------------------------------------------------------------------------
# Chart 4: Bubble chart with size data
# Features: chartType=bubble, categories as X, series as Y values,
# bubble sizes default to Y values, bubbleScale to control sizing
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Scatter & Bubble" --type chart \
--prop chartType=bubble \
--prop title="Bubble: Market Size" \
--prop categories=10,25,40,60,80 \
--prop series1=ProductA:30,50,20,70,45 \
--prop series2=ProductB:15,35,55,40,60 \
--prop colors=4472C4,ED7D31 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop bubbleScale=80 \
--prop legend=right
# ==========================================================================
# Sheet: 2-Combo & Radar
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Combo & Radar"
# --------------------------------------------------------------------------
# Chart 1: Combo chart — bar+line with comboSplit
# Features: chartType=combo, comboSplit=2 (first 2 series as bars,
# remaining as lines), categories as X labels
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Combo & Radar" --type chart \
--prop chartType=combo \
--prop title="Combo: Sales (Bar) + Growth % (Line)" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop series1=Revenue:120,145,132,168,155,180 \
--prop series2=Expenses:80,92,85,98,90,105 \
--prop series3=Growth:8,12,6,15,10,16 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop comboSplit=2 \
--prop legend=bottom \
--prop axisTitle=Amount --prop catTitle=Month
# --------------------------------------------------------------------------
# Chart 2: Combo with secondary axis
# Features: comboSplit=1, secondaryAxis=2 (series 2 on right Y-axis)
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Combo & Radar" --type chart \
--prop chartType=combo \
--prop title="Combo: Volume (Bar) + Price (Line, 2nd Axis)" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop series1=Volume:1200,1450,1320,1680 \
--prop series2=AvgPrice:45,52,48,58 \
--prop colors=5B9BD5,FF0000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop comboSplit=1 \
--prop secondaryAxis=2 \
--prop legend=bottom
# --------------------------------------------------------------------------
# Chart 3: Combo with combotypes — per-series type control
# Features: combotypes (per-series type: column, column, line, area)
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Combo & Radar" --type chart \
--prop chartType=combo \
--prop title="Combo: Mixed Types (combotypes)" \
--prop categories=A,B,C,D,E \
--prop series1=Bars:30,45,28,52,40 \
--prop series2=MoreBars:20,30,22,38,28 \
--prop series3=Lines:12,18,15,22,16 \
--prop series4=Area:8,12,10,15,11 \
--prop colors=4472C4,5B9BD5,ED7D31,70AD47 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop combotypes=column,column,line,area \
--prop legend=bottom
# --------------------------------------------------------------------------
# Chart 4: Radar (spider) chart with multiple series
# Features: chartType=radar, categories as spoke labels,
# multiple series, radarStyle=marker
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Combo & Radar" --type chart \
--prop chartType=radar \
--prop title="Radar: Skills Comparison" \
--prop categories=Speed,Strength,Stamina,Agility,Accuracy \
--prop series1=AthleteA:80,65,90,75,85 \
--prop series2=AthleteB:70,85,60,90,70 \
--prop series3=AthleteC:90,70,75,65,80 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop radarStyle=marker \
--prop legend=bottom
# ==========================================================================
# Sheet: 3-Stock & Radar
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Stock & Radar"
# --------------------------------------------------------------------------
# Chart 1: Stock (OHLC) chart — Open-High-Low-Close
# Features: chartType=stock, 4 series (Open/High/Low/Close),
# categories as date labels, catTitle, axisTitle
# --------------------------------------------------------------------------
officecli add "$FILE" "/3-Stock & Radar" --type chart \
--prop chartType=stock \
--prop title="Stock: OHLC Daily Prices" \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop series1=Open:145,148,150,147,152 \
--prop series2=High:152,155,157,153,160 \
--prop series3=Low:143,146,148,144,150 \
--prop series4=Close:148,150,147,152,158 \
--prop x=0 --prop y=0 --prop width=14 --prop height=18 \
--prop legend=bottom \
--prop catTitle=Day --prop axisTitle=Price
# --------------------------------------------------------------------------
# Chart 2: Stock chart — weekly OHLC with date categories
# Features: stock chart with 6 weeks of OHLC, gridlines styling
# --------------------------------------------------------------------------
officecli add "$FILE" "/3-Stock & Radar" --type chart \
--prop chartType=stock \
--prop title="Stock: Weekly OHLC (6 Weeks)" \
--prop categories=W1,W2,W3,W4,W5,W6 \
--prop series1=Open:100,104,102,108,105,110 \
--prop series2=High:106,110,108,115,112,118 \
--prop series3=Low:98,101,100,105,103,107 \
--prop series4=Close:104,102,108,105,110,115 \
--prop x=15 --prop y=0 --prop width=14 --prop height=18 \
--prop gridlines=E0E0E0:0.75 \
--prop legend=bottom
# --------------------------------------------------------------------------
# Chart 3: Radar — filled style (spider web)
# Features: radarStyle=filled, transparency (fill alpha), multiple series
# --------------------------------------------------------------------------
officecli add "$FILE" "/3-Stock & Radar" --type chart \
--prop chartType=radar \
--prop title="Radar: Product Ratings (Filled)" \
--prop categories=Quality,Price,Design,Support,Delivery \
--prop series1=BrandX:85,70,90,75,80 \
--prop series2=BrandY:70,90,65,85,75 \
--prop colors=4472C4,70AD47 \
--prop x=0 --prop y=19 --prop width=14 --prop height=18 \
--prop radarStyle=filled \
--prop transparency=40 \
--prop legend=bottom
# --------------------------------------------------------------------------
# Chart 4: Bubble — single series with explicit large differences in size
# Features: bubble with single series, bubbleScale=100, legend=none,
# axisTitle and catTitle labels
# --------------------------------------------------------------------------
officecli add "$FILE" "/3-Stock & Radar" --type chart \
--prop chartType=bubble \
--prop title="Bubble: Regional Opportunity" \
--prop categories=5,15,30,50,70,90 \
--prop series1=Regions:20,45,30,80,55,65 \
--prop colors=4472C4 \
--prop x=15 --prop y=19 --prop width=14 --prop height=18 \
--prop bubbleScale=100 \
--prop legend=none \
--prop axisTitle=Revenue --prop catTitle="Market Size"
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+220
View File
@@ -0,0 +1,220 @@
# Area Charts Showcase
This demo consists of three files that work together:
- **charts-area.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-area.xlsx** — The generated workbook with 6 sheets (1 data + 5 chart sheets, 20 charts total).
- **charts-area.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-area.py
# → charts-area.xlsx
```
## Chart Sheets
### Sheet: 1-Area Fundamentals
Four area charts covering data input methods, transparency, area fills, and gradients.
```bash
# Basic area with dataRange and axis titles
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop dataRange=Sheet1!A1:E13 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop catTitle=Month --prop axisTitle=Visitors \
--prop gridlines=D9D9D9:0.5:dot
# Inline series with transparency
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop series1="Subscriptions:120,180,210,250" \
--prop series2="One-time:90,140,160,200" \
--prop transparency=40 --prop legend=bottom
# Area with areafill gradient (single series)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop series1="Users:3200,3800,4500,5100,5800,6400" \
--prop areafill=4472C4-BDD7EE:90 --prop legend=none
# Per-series gradient fills
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90' \
--prop legend=right --prop legendfont=10:333333:Calibri
```
**Features:** `area`, `dataRange`, `categories`, `colors`, `catTitle`, `axisTitle`, `gridlines`, `transparency`, `areafill` (gradient from-to:angle), `gradients` (per-series), `legend` (bottom, right, none), `legendfont`
### Sheet: 2-Area Variants
Four charts covering all area chart type variants — stacked, percent stacked, and 3D.
```bash
# Stacked area with solid plot fill
officecli add data.xlsx /Sheet --type chart \
--prop chartType=areaStacked \
--prop plotFill=F5F5F5 --prop roundedCorners=true
# 100% stacked area with axis number format
officecli add data.xlsx /Sheet --type chart \
--prop chartType=areaPercentStacked \
--prop axisNumFmt=0% --prop axisLine=333333:1:solid
# 3D area with perspective rotation
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area3d \
--prop view3d=20,25,15
# 3D area with multiple series and gridlines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area3d \
--prop view3d=15,20,20 --prop gridlines=D9D9D9:0.5:dot
```
**Features:** `areaStacked`, `areaPercentStacked`, `area3d`, `plotFill` (solid), `roundedCorners`, `axisNumFmt`, `axisLine`, `view3d` (rotX,rotY,perspective)
### Sheet: 3-Area Styling
Four charts demonstrating visual styling — title effects, shadows, gridlines, and fills.
```bash
# Title styling with shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30
# Series shadow, outline, and smooth curve
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop smooth=true \
--prop series.shadow=000000-4-315-2-40 \
--prop series.outline=333333-1
# Axis font with gridlines and minor gridlines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop axisfont=9:58626E:Arial \
--prop gridlines=D9D9D9:0.5:dot \
--prop minorGridlines=EEEEEE:0.3:dot
# Chart fill, plot fill gradient, and borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop chartFill=FAFAFA \
--prop plotFill=E8F0FE-D6E4F0:90 \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot
```
**Features:** `title.font`/`.size`/`.color`/`.bold`/`.shadow`, `smooth`, `series.shadow` (color-blur-angle-dist-opacity), `series.outline` (color-width), `axisfont` (size:color:font), `gridlines`, `minorGridlines`, `chartFill`, `plotFill` (gradient), `chartArea.border`, `plotArea.border`, `roundedCorners`
### Sheet: 4-Labels & Legend
Four charts demonstrating data label and legend customization plus manual layout.
```bash
# Data labels with position, font, and number format
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=9:333333:true \
--prop dataLabels.numFmt=#,##0
# Individual label deletion and per-point colors
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop dataLabels=true \
--prop dataLabel1.delete=true --prop dataLabel2.delete=true \
--prop point4.color=C00000
# Legend overlay with font styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop legend=right --prop legendfont=10:1F4E79:Calibri \
--prop legend.overlay=true
# Manual layout — plotArea, title, legend positioning
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop plotArea.x=0.12 --prop plotArea.y=0.18 \
--prop plotArea.w=0.82 --prop plotArea.h=0.55 \
--prop title.x=0.25 --prop title.y=0.02 \
--prop legend.x=0.15 --prop legend.y=0.82 \
--prop legend.w=0.7 --prop legend.h=0.12
```
**Features:** `dataLabels`, `labelPos` (top), `labelFont`, `dataLabels.numFmt`, `dataLabel{N}.delete`, `point{N}.color`, `legend` (right), `legendfont`, `legend.overlay`, `plotArea.x/y/w/h`, `title.x/y`, `legend.x/y/w/h`
### Sheet: 5-Advanced
Four charts demonstrating advanced features — secondary axis, reference lines, axis scaling, and effects.
```bash
# Secondary axis (dual scale)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop secondaryAxis=2 \
--prop series1="Revenue:120,180,250,310,280,340" \
--prop series2="Conv %:2.1,2.8,3.2,3.9,3.5,4.1"
# Reference line (target/threshold)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop referenceLine=100:FF0000:1.5:dash \
--prop areafill=4472C4-BDD7EE:90
# Axis scaling with display units
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop axisMin=3000 --prop axisMax=7000 \
--prop majorUnit=500 --prop dispUnits=thousands
# Color rule with title glow and series shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop colorRule=50:C00000:70AD47 \
--prop referenceLine=50:888888:1:solid \
--prop title.glow=4472C4-8-60 \
--prop series.shadow=000000-3-315-1-30
```
**Features:** `secondaryAxis` (1-based series index), `referenceLine` (value:color:width:dash), `axisMin`, `axisMax`, `majorUnit`, `dispUnits` (thousands), `colorRule` (threshold:belowColor:aboveColor), `title.glow` (color-radius-opacity), `areafill`
## Complete Feature Coverage
| Feature | Sheet |
|---------|-------|
| **Chart types:** area, areaStacked, areaPercentStacked, area3d | 1, 2 |
| **Data input:** dataRange, series, categories, colors | 1 |
| **Area fills:** areafill (gradient), gradients (per-series), transparency | 1, 5 |
| **Axis titles:** catTitle, axisTitle | 1, 3 |
| **Axis scaling:** axisMin/Max, majorUnit, dispUnits | 5 |
| **Axis features:** axisNumFmt, axisLine | 2 |
| **Gridlines:** gridlines, minorGridlines | 1, 3 |
| **Data labels:** dataLabels, labelPos, labelFont, numFmt | 4 |
| **Custom labels:** dataLabel{N}.delete | 4 |
| **Point color:** point{N}.color | 4 |
| **Legend:** position, legendfont, legend.overlay, legend=none | 1, 4 |
| **Layout:** plotArea.x/y/w/h, title.x/y, legend.x/y/w/h | 4 |
| **Effects:** series.shadow, series.outline, smooth | 3 |
| **Title styling:** font, size, color, bold, shadow, glow | 3, 5 |
| **Fills:** plotFill, chartFill (solid + gradient) | 2, 3 |
| **Borders:** chartArea.border, plotArea.border | 3 |
| **Advanced:** secondaryAxis, referenceLine, colorRule | 5 |
| **3D:** view3d | 2 |
| **Other:** roundedCorners | 2, 3 |
## Inspect the Generated File
```bash
officecli query charts-area.xlsx chart
officecli get charts-area.xlsx "/1-Area Fundamentals/chart[1]"
```
+449
View File
@@ -0,0 +1,449 @@
#!/usr/bin/env python3
"""
Area Charts Showcase — area, areaStacked, areaPercentStacked, and area3d with all variations.
Generates: charts-area.xlsx
Every area chart feature officecli supports is demonstrated at least once:
area fills, gradients, transparency, stacking, axis scaling, gridlines,
data labels, legend positioning, reference lines, secondary axis,
shadows, manual layout, and 3D rotation.
5 sheets, 20 charts total.
1-Area Fundamentals 4 charts — data input variants, transparency, area fills, gradients
2-Area Variants 4 charts — areaStacked, areaPercentStacked, area3d
3-Area Styling 4 charts — title styling, shadows, gridlines, chart/plot fills
4-Labels & Legend 4 charts — data labels, per-point colors, legend, manual layout
5-Advanced 4 charts — secondary axis, reference line, axis scaling, effects
SDK twin of charts-area.sh (officecli CLI). Both produce an equivalent
charts-area.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started, the source data and
every chart is shipped over the named pipe in `doc.batch(...)` round-trips.
Each item is the same `{"command","parent","type","props"}` dict you'd put in
an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-area.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-area.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ==========================================================================
# Source data — shared across all charts
# ==========================================================================
print("\n--- Populating source data ---")
data_cmds = []
for j, h in enumerate(["Month", "Organic", "Paid", "Social", "Referral"]):
data_cmds.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}1",
"props": {"text": h, "bold": "true"}})
months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
organic = [4200, 4800, 5100, 5600, 6200, 6800, 7500, 8100, 7600, 7200, 6900, 7800]
paid = [3100, 3500, 3800, 4200, 4800, 5200, 5800, 6300, 5900, 5500, 5100, 5700]
social = [1800, 2100, 2400, 2800, 3200, 3600, 4000, 4300, 3900, 3500, 3200, 3800]
referral = [1200, 1400, 1500, 1700, 1900, 2100, 2300, 2500, 2300, 2100, 1900, 2200]
for i in range(12):
r = i + 2
for j, val in enumerate([months[i], organic[i], paid[i], social[i], referral[i]]):
data_cmds.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}{r}",
"props": {"text": str(val)}})
doc.batch(data_cmds)
# ==========================================================================
# Sheet: 1-Area Fundamentals
# ==========================================================================
print("\n--- 1-Area Fundamentals ---")
items = [add_sheet("1-Area Fundamentals")]
# ----------------------------------------------------------------------
# Chart 1: Basic area chart with dataRange, axis titles, and custom colors
# Features: chartType=area, dataRange, colors, catTitle, axisTitle, gridlines
# ----------------------------------------------------------------------
items.append(chart("/1-Area Fundamentals",
chartType="area",
title="Website Traffic Overview",
dataRange="Sheet1!A1:E13",
colors="4472C4,ED7D31,70AD47,FFC000",
x="0", y="0", width="12", height="18",
catTitle="Month", axisTitle="Visitors",
gridlines="D9D9D9:0.5:dot"))
# ----------------------------------------------------------------------
# Chart 2: Inline series with transparency
# Features: inline series, transparency (0-100), legend=bottom
# ----------------------------------------------------------------------
items.append(chart("/1-Area Fundamentals",
chartType="area",
title="Quarterly Revenue Streams",
series1="Subscriptions:120,180,210,250",
series2="One-time:90,140,160,200",
series3="Services:60,85,110,145",
categories="Q1,Q2,Q3,Q4",
colors="2E75B6,70AD47,FFC000",
x="13", y="0", width="12", height="18",
transparency="40",
legend="bottom"))
# ----------------------------------------------------------------------
# Chart 3: Area with areafill gradient
# Features: areafill (gradient from-to:angle), legend=none, single series
# ----------------------------------------------------------------------
items.append(chart("/1-Area Fundamentals",
chartType="area",
title="Monthly Active Users",
series1="Users:3200,3800,4500,5100,5800,6400",
categories="Jul,Aug,Sep,Oct,Nov,Dec",
x="0", y="19", width="12", height="18",
areafill="4472C4-BDD7EE:90",
legend="none"))
# ----------------------------------------------------------------------
# Chart 4: Per-series gradient fills
# Features: gradients (per-series gradient fills from-to:angle;...),
# legendfont (size:color:font)
# ----------------------------------------------------------------------
items.append(chart("/1-Area Fundamentals",
chartType="area",
title="Revenue by Channel",
series1="Direct:45,52,61,70",
series2="Partner:30,38,42,55",
categories="Q1,Q2,Q3,Q4",
x="13", y="19", width="12", height="18",
gradients="4472C4-BDD7EE:90;ED7D31-FBE5D6:90",
legend="right", legendfont="10:333333:Calibri"))
doc.batch(items)
# ==========================================================================
# Sheet: 2-Area Variants
# ==========================================================================
print("\n--- 2-Area Variants ---")
items = [add_sheet("2-Area Variants")]
# ----------------------------------------------------------------------
# Chart 1: Stacked area with plotFill and rounded corners
# Features: chartType=areaStacked, plotFill (solid), roundedCorners
# ----------------------------------------------------------------------
items.append(chart("/2-Area Variants",
chartType="areaStacked",
title="Cumulative Traffic Sources",
dataRange="Sheet1!A1:E13",
colors="4472C4,ED7D31,70AD47,FFC000",
x="0", y="0", width="12", height="18",
plotFill="F5F5F5",
roundedCorners="true",
legend="bottom"))
# ----------------------------------------------------------------------
# Chart 2: 100% stacked area with axis number format and axis line
# Features: chartType=areaPercentStacked, axisNumFmt, axisLine
# ----------------------------------------------------------------------
items.append(chart("/2-Area Variants",
chartType="areaPercentStacked",
title="Traffic Share by Channel",
dataRange="Sheet1!A1:E13",
colors="2E75B6,C55A11,548235,BF8F00",
x="13", y="0", width="12", height="18",
axisNumFmt="0%",
axisLine="333333:1:solid",
legend="bottom"))
# ----------------------------------------------------------------------
# Chart 3: 3D area with perspective rotation
# Features: chartType=area3d, view3d (rotX,rotY,perspective)
# ----------------------------------------------------------------------
items.append(chart("/2-Area Variants",
chartType="area3d",
title="3D Regional Sales",
series1="East:120,135,148,162,155,178",
series2="West:95,108,115,128,142,155",
series3="Central:88,92,105,118,125,138",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="4472C4,ED7D31,70AD47",
x="0", y="19", width="12", height="18",
view3d="20,25,15",
legend="right"))
# ----------------------------------------------------------------------
# Chart 4: 3D stacked area
# Features: area3d stacked appearance, multiple series, gridlines
# ----------------------------------------------------------------------
items.append(chart("/2-Area Variants",
chartType="area3d",
title="3D Stacked Inventory",
series1="Warehouse A:500,480,520,550,530,560",
series2="Warehouse B:320,350,340,380,400,410",
series3="Warehouse C:180,200,210,230,250,240",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="1F4E79,2E75B6,9DC3E6",
x="13", y="19", width="12", height="18",
view3d="15,20,20",
gridlines="D9D9D9:0.5:dot"))
doc.batch(items)
# ==========================================================================
# Sheet: 3-Area Styling
# ==========================================================================
print("\n--- 3-Area Styling ---")
items = [add_sheet("3-Area Styling")]
# ----------------------------------------------------------------------
# Chart 1: Title styling (font, size, color, bold, shadow)
# Features: title.font, title.size, title.color, title.bold, title.shadow
# ----------------------------------------------------------------------
items.append(chart("/3-Area Styling",
chartType="area",
title="Styled Title Demo",
series1="Revenue:80,120,160,200,240,280",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="4472C4",
x="0", y="0", width="12", height="18",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true",
"title.shadow": "000000-3-315-2-30"},
transparency="30"))
# ----------------------------------------------------------------------
# Chart 2: Series shadow, outline, and smooth curve
# Features: smooth, series.shadow (color-blur-angle-dist-opacity),
# series.outline (color-width)
# ----------------------------------------------------------------------
items.append(chart("/3-Area Styling",
chartType="area",
title="Smooth Area with Effects",
series1="Signups:150,180,220,260,310,350",
series2="Trials:90,110,140,170,200,230",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="4472C4,70AD47",
x="13", y="0", width="12", height="18",
smooth="true",
**{"series.shadow": "000000-4-315-2-40",
"series.outline": "333333-1"},
transparency="25"))
# ----------------------------------------------------------------------
# Chart 3: Axis font styling, gridlines, and minor gridlines
# Features: axisfont (size:color:font), gridlines (color:width:dash),
# minorGridlines
# ----------------------------------------------------------------------
items.append(chart("/3-Area Styling",
chartType="area",
title="Gridline Configuration",
dataRange="Sheet1!A1:C13",
colors="2E75B6,C55A11",
x="0", y="19", width="12", height="18",
axisfont="9:58626E:Arial",
gridlines="D9D9D9:0.5:dot",
minorGridlines="EEEEEE:0.3:dot",
catTitle="Month", axisTitle="Visitors"))
# ----------------------------------------------------------------------
# Chart 4: Chart fill, plot fill gradient, chart/plot area borders
# Features: chartFill, plotFill (gradient from-to:angle),
# chartArea.border, plotArea.border, roundedCorners
# ----------------------------------------------------------------------
items.append(chart("/3-Area Styling",
chartType="area",
title="Fills and Borders",
series1="Sales:200,240,280,320,360,400",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="4472C4",
x="13", y="19", width="12", height="18",
chartFill="FAFAFA",
plotFill="E8F0FE-D6E4F0:90",
**{"chartArea.border": "D0D0D0:1:solid",
"plotArea.border": "E0E0E0:0.5:dot"},
roundedCorners="true"))
doc.batch(items)
# ==========================================================================
# Sheet: 4-Labels & Legend
# ==========================================================================
print("\n--- 4-Labels & Legend ---")
items = [add_sheet("4-Labels & Legend")]
# ----------------------------------------------------------------------
# Chart 1: Data labels with position, font, and number format
# Features: dataLabels, labelPos (top), labelFont (size:color:bold),
# dataLabels.numFmt
# ----------------------------------------------------------------------
items.append(chart("/4-Labels & Legend",
chartType="area",
title="Labeled Area Chart",
series1="Users:3200,3800,4500,5100,5800,6400",
categories="Jul,Aug,Sep,Oct,Nov,Dec",
colors="4472C4",
x="0", y="0", width="12", height="18",
dataLabels="true", labelPos="top",
labelFont="9:333333:true",
**{"dataLabels.numFmt": "#,##0"}))
# ----------------------------------------------------------------------
# Chart 2: Individual label deletion and per-point colors
# Features: dataLabel{N}.delete, point{N}.color
# ----------------------------------------------------------------------
items.append(chart("/4-Labels & Legend",
chartType="area",
title="Highlighted Peak Month",
series1="Revenue:180,210,250,310,280,260",
categories="Jul,Aug,Sep,Oct,Nov,Dec",
colors="2E75B6",
x="13", y="0", width="12", height="18",
dataLabels="true",
**{"dataLabel1.delete": "true", "dataLabel2.delete": "true",
"dataLabel5.delete": "true", "dataLabel6.delete": "true",
"point4.color": "C00000"},
transparency="30"))
# ----------------------------------------------------------------------
# Chart 3: Legend positioning with overlay and font styling
# Features: legend=right, legendfont, legend.overlay
# ----------------------------------------------------------------------
items.append(chart("/4-Labels & Legend",
chartType="area",
title="Legend Overlay Demo",
series1="Desktop:4200,4800,5100,5600",
series2="Mobile:3100,3500,3800,4200",
series3="Tablet:1200,1400,1500,1700",
categories="Q1,Q2,Q3,Q4",
colors="4472C4,ED7D31,70AD47",
x="0", y="19", width="12", height="18",
legend="right", legendfont="10:1F4E79:Calibri",
**{"legend.overlay": "true"},
transparency="35"))
# ----------------------------------------------------------------------
# Chart 4: Manual layout — plotArea positioning
# Features: plotArea.x/y/w/h, title.x/y, legend.x/y/w/h (manual layout)
# ----------------------------------------------------------------------
items.append(chart("/4-Labels & Legend",
chartType="area",
title="Manual Layout",
series1="Growth:100,130,170,220,280,350",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="70AD47",
x="13", y="19", width="12", height="18",
**{"plotArea.x": "0.12", "plotArea.y": "0.18",
"plotArea.w": "0.82", "plotArea.h": "0.55",
"title.x": "0.25", "title.y": "0.02",
"legend.x": "0.15", "legend.y": "0.82",
"legend.w": "0.7", "legend.h": "0.12"}))
doc.batch(items)
# ==========================================================================
# Sheet: 5-Advanced
# ==========================================================================
print("\n--- 5-Advanced ---")
items = [add_sheet("5-Advanced")]
# ----------------------------------------------------------------------
# Chart 1: Secondary axis (dual scale)
# Features: secondaryAxis (1-based series index on secondary Y axis)
# ----------------------------------------------------------------------
items.append(chart("/5-Advanced",
chartType="area",
title="Revenue vs Conversion Rate",
series1="Revenue:120,180,250,310,280,340",
series2="Conv %:2.1,2.8,3.2,3.9,3.5,4.1",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="4472C4,C00000",
x="0", y="0", width="12", height="18",
secondaryAxis="2",
transparency="30"))
# ----------------------------------------------------------------------
# Chart 2: Reference line
# Features: referenceLine (value:color:width:dash)
# ----------------------------------------------------------------------
items.append(chart("/5-Advanced",
chartType="area",
title="Sales vs Target",
series1="Sales:85,92,108,115,98,120",
categories="Jan,Feb,Mar,Apr,May,Jun",
colors="4472C4",
x="13", y="0", width="12", height="18",
referenceLine="100:FF0000:1.5:dash",
transparency="25",
areafill="4472C4-BDD7EE:90"))
# ----------------------------------------------------------------------
# Chart 3: Axis min/max, major unit, log scale, display units
# Features: axisMin, axisMax, majorUnit, dispUnits (thousands/millions)
# ----------------------------------------------------------------------
items.append(chart("/5-Advanced",
chartType="area",
title="Axis Scaling Demo",
series1="Visits:3200,3800,4500,5100,5800,6400",
categories="Jul,Aug,Sep,Oct,Nov,Dec",
colors="2E75B6",
x="0", y="19", width="12", height="18",
axisMin="3000", axisMax="7000",
majorUnit="500",
dispUnits="thousands",
axisTitle="Visitors (K)",
transparency="30"))
# ----------------------------------------------------------------------
# Chart 4: Color rule, title glow, series shadow
# Features: colorRule (threshold:belowColor:aboveColor), title.glow
# (color-radius-opacity), series.shadow
# ----------------------------------------------------------------------
items.append(chart("/5-Advanced",
chartType="area",
title="Performance Threshold",
series1="Score:45,62,38,71,55,80",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="19", width="12", height="18",
colorRule="50:C00000:70AD47",
referenceLine="50:888888:1:solid",
**{"title.glow": "4472C4-8-60",
"series.shadow": "000000-3-315-1-30"},
transparency="20"))
doc.batch(items)
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nDone! Generated: {FILE}")
print(" 6 sheets (Sheet1 data + 5 chart sheets, 20 charts total)")
+395
View File
@@ -0,0 +1,395 @@
#!/bin/bash
# Area Charts Showcase — area, areaStacked, areaPercentStacked, and area3d.
# CLI twin of charts-area.py (officecli Python SDK). Both produce an
# equivalent charts-area.xlsx.
#
# 5 sheets, 20 charts total. Every area chart feature officecli supports is
# demonstrated at least once: area fills, gradients, transparency, stacking,
# axis scaling, gridlines, data labels, legend positioning, reference lines,
# secondary axis, shadows, manual layout, and 3D rotation.
#
# Usage: ./charts-area.sh
# 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")/charts-area.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Source data — shared across all charts
# ==========================================================================
officecli set "$FILE" /Sheet1/A1 --prop text=Month --prop bold=true
officecli set "$FILE" /Sheet1/B1 --prop text=Organic --prop bold=true
officecli set "$FILE" /Sheet1/C1 --prop text=Paid --prop bold=true
officecli set "$FILE" /Sheet1/D1 --prop text=Social --prop bold=true
officecli set "$FILE" /Sheet1/E1 --prop text=Referral --prop bold=true
officecli set "$FILE" /Sheet1/A2 --prop text=Jan
officecli set "$FILE" /Sheet1/B2 --prop text=4200
officecli set "$FILE" /Sheet1/C2 --prop text=3100
officecli set "$FILE" /Sheet1/D2 --prop text=1800
officecli set "$FILE" /Sheet1/E2 --prop text=1200
officecli set "$FILE" /Sheet1/A3 --prop text=Feb
officecli set "$FILE" /Sheet1/B3 --prop text=4800
officecli set "$FILE" /Sheet1/C3 --prop text=3500
officecli set "$FILE" /Sheet1/D3 --prop text=2100
officecli set "$FILE" /Sheet1/E3 --prop text=1400
officecli set "$FILE" /Sheet1/A4 --prop text=Mar
officecli set "$FILE" /Sheet1/B4 --prop text=5100
officecli set "$FILE" /Sheet1/C4 --prop text=3800
officecli set "$FILE" /Sheet1/D4 --prop text=2400
officecli set "$FILE" /Sheet1/E4 --prop text=1500
officecli set "$FILE" /Sheet1/A5 --prop text=Apr
officecli set "$FILE" /Sheet1/B5 --prop text=5600
officecli set "$FILE" /Sheet1/C5 --prop text=4200
officecli set "$FILE" /Sheet1/D5 --prop text=2800
officecli set "$FILE" /Sheet1/E5 --prop text=1700
officecli set "$FILE" /Sheet1/A6 --prop text=May
officecli set "$FILE" /Sheet1/B6 --prop text=6200
officecli set "$FILE" /Sheet1/C6 --prop text=4800
officecli set "$FILE" /Sheet1/D6 --prop text=3200
officecli set "$FILE" /Sheet1/E6 --prop text=1900
officecli set "$FILE" /Sheet1/A7 --prop text=Jun
officecli set "$FILE" /Sheet1/B7 --prop text=6800
officecli set "$FILE" /Sheet1/C7 --prop text=5200
officecli set "$FILE" /Sheet1/D7 --prop text=3600
officecli set "$FILE" /Sheet1/E7 --prop text=2100
officecli set "$FILE" /Sheet1/A8 --prop text=Jul
officecli set "$FILE" /Sheet1/B8 --prop text=7500
officecli set "$FILE" /Sheet1/C8 --prop text=5800
officecli set "$FILE" /Sheet1/D8 --prop text=4000
officecli set "$FILE" /Sheet1/E8 --prop text=2300
officecli set "$FILE" /Sheet1/A9 --prop text=Aug
officecli set "$FILE" /Sheet1/B9 --prop text=8100
officecli set "$FILE" /Sheet1/C9 --prop text=6300
officecli set "$FILE" /Sheet1/D9 --prop text=4300
officecli set "$FILE" /Sheet1/E9 --prop text=2500
officecli set "$FILE" /Sheet1/A10 --prop text=Sep
officecli set "$FILE" /Sheet1/B10 --prop text=7600
officecli set "$FILE" /Sheet1/C10 --prop text=5900
officecli set "$FILE" /Sheet1/D10 --prop text=3900
officecli set "$FILE" /Sheet1/E10 --prop text=2300
officecli set "$FILE" /Sheet1/A11 --prop text=Oct
officecli set "$FILE" /Sheet1/B11 --prop text=7200
officecli set "$FILE" /Sheet1/C11 --prop text=5500
officecli set "$FILE" /Sheet1/D11 --prop text=3500
officecli set "$FILE" /Sheet1/E11 --prop text=2100
officecli set "$FILE" /Sheet1/A12 --prop text=Nov
officecli set "$FILE" /Sheet1/B12 --prop text=6900
officecli set "$FILE" /Sheet1/C12 --prop text=5100
officecli set "$FILE" /Sheet1/D12 --prop text=3200
officecli set "$FILE" /Sheet1/E12 --prop text=1900
officecli set "$FILE" /Sheet1/A13 --prop text=Dec
officecli set "$FILE" /Sheet1/B13 --prop text=7800
officecli set "$FILE" /Sheet1/C13 --prop text=5700
officecli set "$FILE" /Sheet1/D13 --prop text=3800
officecli set "$FILE" /Sheet1/E13 --prop text=2200
# ==========================================================================
# Sheet: 1-Area Fundamentals
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Area Fundamentals"
# Chart 1: Basic area chart with dataRange, axis titles, and custom colors
# Features: chartType=area, dataRange, colors, catTitle, axisTitle, gridlines
officecli add "$FILE" "/1-Area Fundamentals" --type chart \
--prop chartType=area \
--prop title="Website Traffic Overview" \
--prop dataRange=Sheet1!A1:E13 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop catTitle=Month --prop axisTitle=Visitors \
--prop gridlines=D9D9D9:0.5:dot
# Chart 2: Inline series with transparency
# Features: inline series, transparency (0-100), legend=bottom
officecli add "$FILE" "/1-Area Fundamentals" --type chart \
--prop chartType=area \
--prop title="Quarterly Revenue Streams" \
--prop series1="Subscriptions:120,180,210,250" \
--prop series2="One-time:90,140,160,200" \
--prop series3="Services:60,85,110,145" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=2E75B6,70AD47,FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop transparency=40 \
--prop legend=bottom
# Chart 3: Area with areafill gradient
# Features: areafill (gradient from-to:angle), legend=none, single series
officecli add "$FILE" "/1-Area Fundamentals" --type chart \
--prop chartType=area \
--prop title="Monthly Active Users" \
--prop series1="Users:3200,3800,4500,5100,5800,6400" \
--prop categories=Jul,Aug,Sep,Oct,Nov,Dec \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop areafill=4472C4-BDD7EE:90 \
--prop legend=none
# Chart 4: Per-series gradient fills
# Features: gradients (per-series gradient fills from-to:angle;...),
# legendfont (size:color:font)
officecli add "$FILE" "/1-Area Fundamentals" --type chart \
--prop chartType=area \
--prop title="Revenue by Channel" \
--prop series1="Direct:45,52,61,70" \
--prop series2="Partner:30,38,42,55" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop "gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90" \
--prop legend=right --prop legendfont=10:333333:Calibri
# ==========================================================================
# Sheet: 2-Area Variants
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Area Variants"
# Chart 1: Stacked area with plotFill and rounded corners
# Features: chartType=areaStacked, plotFill (solid), roundedCorners
officecli add "$FILE" "/2-Area Variants" --type chart \
--prop chartType=areaStacked \
--prop title="Cumulative Traffic Sources" \
--prop dataRange=Sheet1!A1:E13 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop plotFill=F5F5F5 \
--prop roundedCorners=true \
--prop legend=bottom
# Chart 2: 100% stacked area with axis number format and axis line
# Features: chartType=areaPercentStacked, axisNumFmt, axisLine
officecli add "$FILE" "/2-Area Variants" --type chart \
--prop chartType=areaPercentStacked \
--prop title="Traffic Share by Channel" \
--prop dataRange=Sheet1!A1:E13 \
--prop colors=2E75B6,C55A11,548235,BF8F00 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop axisNumFmt=0% \
--prop axisLine=333333:1:solid \
--prop legend=bottom
# Chart 3: 3D area with perspective rotation
# Features: chartType=area3d, view3d (rotX,rotY,perspective)
officecli add "$FILE" "/2-Area Variants" --type chart \
--prop chartType=area3d \
--prop title="3D Regional Sales" \
--prop series1="East:120,135,148,162,155,178" \
--prop series2="West:95,108,115,128,142,155" \
--prop series3="Central:88,92,105,118,125,138" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=20,25,15 \
--prop legend=right
# Chart 4: 3D stacked area
# Features: area3d stacked appearance, multiple series, gridlines
officecli add "$FILE" "/2-Area Variants" --type chart \
--prop chartType=area3d \
--prop title="3D Stacked Inventory" \
--prop series1="Warehouse A:500,480,520,550,530,560" \
--prop series2="Warehouse B:320,350,340,380,400,410" \
--prop series3="Warehouse C:180,200,210,230,250,240" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=1F4E79,2E75B6,9DC3E6 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=15,20,20 \
--prop gridlines=D9D9D9:0.5:dot
# ==========================================================================
# Sheet: 3-Area Styling
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Area Styling"
# Chart 1: Title styling (font, size, color, bold, shadow)
# Features: title.font, title.size, title.color, title.bold, title.shadow
officecli add "$FILE" "/3-Area Styling" --type chart \
--prop chartType=area \
--prop title="Styled Title Demo" \
--prop series1="Revenue:80,120,160,200,240,280" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30 \
--prop transparency=30
# Chart 2: Series shadow, outline, and smooth curve
# Features: smooth, series.shadow (color-blur-angle-dist-opacity),
# series.outline (color-width)
officecli add "$FILE" "/3-Area Styling" --type chart \
--prop chartType=area \
--prop title="Smooth Area with Effects" \
--prop series1="Signups:150,180,220,260,310,350" \
--prop series2="Trials:90,110,140,170,200,230" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=4472C4,70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop smooth=true \
--prop series.shadow=000000-4-315-2-40 \
--prop series.outline=333333-1 \
--prop transparency=25
# Chart 3: Axis font styling, gridlines, and minor gridlines
# Features: axisfont (size:color:font), gridlines (color:width:dash),
# minorGridlines
officecli add "$FILE" "/3-Area Styling" --type chart \
--prop chartType=area \
--prop title="Gridline Configuration" \
--prop dataRange=Sheet1!A1:C13 \
--prop colors=2E75B6,C55A11 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop axisfont=9:58626E:Arial \
--prop gridlines=D9D9D9:0.5:dot \
--prop minorGridlines=EEEEEE:0.3:dot \
--prop catTitle=Month --prop axisTitle=Visitors
# Chart 4: Chart fill, plot fill gradient, chart/plot area borders
# Features: chartFill, plotFill (gradient from-to:angle),
# chartArea.border, plotArea.border, roundedCorners
officecli add "$FILE" "/3-Area Styling" --type chart \
--prop chartType=area \
--prop title="Fills and Borders" \
--prop series1="Sales:200,240,280,320,360,400" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=4472C4 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop chartFill=FAFAFA \
--prop "plotFill=E8F0FE-D6E4F0:90" \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot \
--prop roundedCorners=true
# ==========================================================================
# Sheet: 4-Labels & Legend
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="4-Labels & Legend"
# Chart 1: Data labels with position, font, and number format
# Features: dataLabels, labelPos (top), labelFont (size:color:bold),
# dataLabels.numFmt
officecli add "$FILE" "/4-Labels & Legend" --type chart \
--prop chartType=area \
--prop title="Labeled Area Chart" \
--prop series1="Users:3200,3800,4500,5100,5800,6400" \
--prop categories=Jul,Aug,Sep,Oct,Nov,Dec \
--prop colors=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=9:333333:true \
--prop dataLabels.numFmt=#,##0
# Chart 2: Individual label deletion and per-point colors
# Features: dataLabel{N}.delete, point{N}.color
officecli add "$FILE" "/4-Labels & Legend" --type chart \
--prop chartType=area \
--prop title="Highlighted Peak Month" \
--prop series1="Revenue:180,210,250,310,280,260" \
--prop categories=Jul,Aug,Sep,Oct,Nov,Dec \
--prop colors=2E75B6 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true \
--prop dataLabel1.delete=true --prop dataLabel2.delete=true \
--prop dataLabel5.delete=true --prop dataLabel6.delete=true \
--prop point4.color=C00000 \
--prop transparency=30
# Chart 3: Legend positioning with overlay and font styling
# Features: legend=right, legendfont, legend.overlay
officecli add "$FILE" "/4-Labels & Legend" --type chart \
--prop chartType=area \
--prop title="Legend Overlay Demo" \
--prop series1="Desktop:4200,4800,5100,5600" \
--prop series2="Mobile:3100,3500,3800,4200" \
--prop series3="Tablet:1200,1400,1500,1700" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=right --prop legendfont=10:1F4E79:Calibri \
--prop legend.overlay=true \
--prop transparency=35
# Chart 4: Manual layout — plotArea positioning
# Features: plotArea.x/y/w/h, title.x/y, legend.x/y/w/h (manual layout)
officecli add "$FILE" "/4-Labels & Legend" --type chart \
--prop chartType=area \
--prop title="Manual Layout" \
--prop series1="Growth:100,130,170,220,280,350" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=70AD47 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop plotArea.x=0.12 --prop plotArea.y=0.18 \
--prop plotArea.w=0.82 --prop plotArea.h=0.55 \
--prop title.x=0.25 --prop title.y=0.02 \
--prop legend.x=0.15 --prop legend.y=0.82 \
--prop legend.w=0.7 --prop legend.h=0.12
# ==========================================================================
# Sheet: 5-Advanced
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="5-Advanced"
# Chart 1: Secondary axis (dual scale)
# Features: secondaryAxis (1-based series index on secondary Y axis)
officecli add "$FILE" "/5-Advanced" --type chart \
--prop chartType=area \
--prop title="Revenue vs Conversion Rate" \
--prop series1="Revenue:120,180,250,310,280,340" \
--prop series2="Conv %:2.1,2.8,3.2,3.9,3.5,4.1" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=4472C4,C00000 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop secondaryAxis=2 \
--prop transparency=30
# Chart 2: Reference line
# Features: referenceLine (value:color:width:dash)
officecli add "$FILE" "/5-Advanced" --type chart \
--prop chartType=area \
--prop title="Sales vs Target" \
--prop series1="Sales:85,92,108,115,98,120" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop colors=4472C4 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop referenceLine=100:FF0000:1.5:dash \
--prop transparency=25 \
--prop areafill=4472C4-BDD7EE:90
# Chart 3: Axis min/max, major unit, log scale, display units
# Features: axisMin, axisMax, majorUnit, dispUnits (thousands/millions)
officecli add "$FILE" "/5-Advanced" --type chart \
--prop chartType=area \
--prop title="Axis Scaling Demo" \
--prop series1="Visits:3200,3800,4500,5100,5800,6400" \
--prop categories=Jul,Aug,Sep,Oct,Nov,Dec \
--prop colors=2E75B6 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop axisMin=3000 --prop axisMax=7000 \
--prop majorUnit=500 \
--prop dispUnits=thousands \
--prop "axisTitle=Visitors (K)" \
--prop transparency=30
# Chart 4: Color rule, title glow, series shadow
# Features: colorRule (threshold:belowColor:aboveColor), title.glow
# (color-radius-opacity), series.shadow
officecli add "$FILE" "/5-Advanced" --type chart \
--prop chartType=area \
--prop title="Performance Threshold" \
--prop series1="Score:45,62,38,71,55,80" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop colorRule=50:C00000:70AD47 \
--prop referenceLine=50:888888:1:solid \
--prop title.glow=4472C4-8-60 \
--prop series.shadow=000000-3-315-1-30 \
--prop transparency=20
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+334
View File
@@ -0,0 +1,334 @@
# Bar (Horizontal) Charts Showcase
This demo consists of three files that work together:
- **charts-bar.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-bar.xlsx** — The generated workbook with 8 sheets (1 data + 7 chart sheets, 28 charts total).
- **charts-bar.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-bar.py
# → charts-bar.xlsx
```
## Chart Sheets
### Sheet: 1-Bar Fundamentals
Four basic horizontal bar charts covering data input variants, colors, stacking, and shorthand syntax.
```bash
# Basic bar from cell range with axis titles and gridlines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop dataRange=Sheet1!A1:B9 \
--prop catTitle=Department --prop axisTitle=Score \
--prop axisfont=9:333333:Arial \
--prop gridlines=D9D9D9:0.5:dot
# Inline series with custom colors and data labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop series1="Satisfaction:85,72,91,68,78" \
--prop colors=4472C4,ED7D31,70AD47,FFC000,5B9BD5 \
--prop gapwidth=80 --prop dataLabels=outsideEnd
# Stacked bar with series outline
officecli add data.xlsx /Sheet --type chart \
--prop chartType=barStacked \
--prop series1="Q1:30,18,25,12" --prop series2="Q2:35,20,28,14" \
--prop overlap=0 --prop series.outline=FFFFFF-0.5
# data= shorthand with legend at bottom
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop 'data=Technical:45,38,52;Soft Skills:20,28,18;Compliance:12,15,10' \
--prop legend=bottom
```
**Features:** `bar`, `barStacked`, `dataRange`, `catTitle`, `axisTitle`, `axisfont`, `gridlines`, `colors`, `gapwidth`, `dataLabels=outsideEnd`, `overlap`, `series.outline`, `data=` shorthand, `legend=bottom`
### Sheet: 2-Bar Variants
Four bar chart type variants: stacked, 100% stacked, 3D, and 3D cylinder.
```bash
# Stacked bar with tight gap
officecli add data.xlsx /Sheet --type chart \
--prop chartType=barStacked \
--prop gapwidth=50
# 100% stacked with percentage axis and reference line
officecli add data.xlsx /Sheet --type chart \
--prop chartType=barPercentStacked \
--prop axisNumFmt=0% \
--prop referenceLine=0.5:FF0000:Target:dash
# 3D bar with perspective
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar3d \
--prop view3d=10,30,20 --prop style=3
# 3D bar with cylinder shape
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar3d \
--prop shape=cylinder --prop gapwidth=60
```
**Features:** `barStacked`, `barPercentStacked`, `bar3d`, `gapwidth`, `axisNumFmt=0%`, `referenceLine` (with label and dash), `view3d`, `style`, `shape=cylinder`
### Sheet: 3-Bar Styling
Four charts demonstrating visual styling: title formatting, shadows, gradients, and background fills.
```bash
# Title font, size, color, bold
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true
# Series shadow and outline
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop series.shadow=000000-4-315-2-30 \
--prop series.outline=1F4E79-1
# Per-bar gradient fills (angle=0 for horizontal)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop 'gradients=1F4E79-5B9BD5:0;C55A11-F4B183:0;...' \
--prop labelFont=9:333333:true
# Plot/chart fill with transparency and rounded corners
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop plotFill=F0F4F8-D6E4F0:90 --prop chartFill=FFFFFF \
--prop transparency=20 --prop roundedCorners=true
```
**Features:** `title.font/size/color/bold`, `series.shadow`, `series.outline`, `gradients` (per-bar), `labelFont`, `plotFill` gradient, `chartFill`, `transparency`, `roundedCorners`
### Sheet: 4-Axis & Labels
Four charts exploring axis configuration and data label customization.
```bash
# Custom axis scale with gridlines styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop axisMin=50 --prop axisMax=250 --prop majorUnit=50 \
--prop gridlines=D0D0D0:0.5:solid \
--prop minorGridlines=EEEEEE:0.3:dot \
--prop axisLine=C00000:1.5:solid --prop catAxisLine=2E75B6:1.5:solid
# Log scale, reversed axis, display units
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop logBase=10 --prop axisReverse=true \
--prop dispUnits=thousands
# Data labels with font, number format, separator
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop labelFont=10:1F4E79:true \
--prop dataLabels.numFmt=#,##0 --prop "dataLabels.separator=: "
# Per-point label delete/text and per-point color (highlight winner)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop dataLabel1.delete=true --prop dataLabel4.text="Winner!" \
--prop point4.color=C00000 --prop point2.color=2E75B6
```
**Features:** `axisMin`, `axisMax`, `majorUnit`, `gridlines`, `minorGridlines`, `axisLine`, `catAxisLine`, `logBase`, `axisReverse`, `dispUnits`, `dataLabels`, `labelPos`, `labelFont`, `dataLabels.numFmt`, `dataLabels.separator`, `dataLabel{N}.delete`, `dataLabel{N}.text`, `point{N}.color`
### Sheet: 5-Legend & Layout
Four charts covering legend configuration, manual layout, and dual-axis support.
```bash
# Legend on right side
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop legend=right
# Legend font styling with overlay
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop legend=top --prop legend.overlay=true \
--prop legendfont=10:1F4E79:Calibri
# Manual layout: plotArea, title, and legend positioning
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop plotArea.x=0.25 --prop plotArea.y=0.15 \
--prop plotArea.w=0.70 --prop plotArea.h=0.60 \
--prop title.x=0.20 --prop title.y=0.02 \
--prop legend.x=0.25 --prop legend.y=0.82
# Secondary axis with chart/plot area borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop secondaryAxis=2 \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot
```
**Features:** `legend=right/top/bottom`, `legend.overlay`, `legendfont`, `plotArea.x/y/w/h`, `title.x/y`, `legend.x/y/w/h`, `secondaryAxis`, `chartArea.border`, `plotArea.border`
### Sheet: 6-Advanced
Four charts with advanced features: reference lines, conditional coloring, effects, and data tables.
```bash
# Reference line with label
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop referenceLine=79:FF0000:Average:dash
# Conditional coloring (profit/loss)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop colorRule=0:C00000:70AD47 \
--prop referenceLine=0:888888:1:solid
# Title glow, title shadow, series shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop title.glow=4472C4-8-60 \
--prop title.shadow=000000-3-315-2-40 \
--prop series.shadow=000000-3-315-1-30
# Error bars and data table
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop errBars=percent:10 --prop dataTable=true \
--prop legend=none
```
**Features:** `referenceLine` (with label), `colorRule` (threshold coloring), `title.glow`, `title.shadow`, `series.shadow`, `errBars=percent:10`, `dataTable=true`
### Sheet: 7-Axis Controls
Four charts demonstrating fine-grained axis behaviour: cross position, category label rotation/offset/skip, stacked-bar series connector lines, and chart-level marker color.
```bash
# crosses, crossBetween, valAxisVisible
officecli add charts-bar.xlsx "/7-Axis Controls" --type chart \
--prop chartType=bar \
--prop title="Axis Cross Controls" \
--prop series1="Sales:120,80,-30,150" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop crosses=autoZero \
--prop crossBetween=between \
--prop valAxisVisible=true
# labelrotation, labeloffset, ticklabelskip (on a column chart)
officecli add charts-bar.xlsx "/7-Axis Controls" --type chart \
--prop chartType=column \
--prop title="Tick-label Rotation, Offset & Skip" \
--prop series1="Units:45,30,20,55,40,25,60" \
--prop categories=January,February,March,April,May,June,July \
--prop labelrotation=45 \
--prop labeloffset=100 \
--prop ticklabelskip=2
# axisposition, serlines (stacked bar)
officecli add charts-bar.xlsx "/7-Axis Controls" --type chart \
--prop chartType=barStacked \
--prop title="Stacked — axisposition + serlines" \
--prop series1="Online:55,48,60,70" \
--prop series2="Retail:30,40,35,25" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31 \
--prop axisposition=nextTo \
--prop serlines=true
# markercolor — chart-level fan-out to all series markers (line chart)
officecli add charts-bar.xlsx "/7-Axis Controls" --type chart \
--prop chartType=line \
--prop title="Line — markercolor" \
--prop series1="Sales:120,145,132,160" \
--prop series2="Costs:80,95,88,110" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31 \
--prop marker=circle --prop markerSize=8 \
--prop markercolor=FF0000 \
--prop lineWidth=2
```
**Features:** `crosses=autoZero` (value axis crosses category axis at zero; also: `min`, `max`), `crossBetween=between` (bars centred between tick marks vs `midCat`), `valAxisVisible=true/false` (show/hide the value axis), `labelrotation=45` (rotate category tick labels, -9090 degrees), `labeloffset=100` (category-axis label offset as % of default), `ticklabelskip=2` (draw tick labels every Nth category), `axisposition=nextTo` (tick labels next to axis; also: `high`, `low`), `serlines=true` (series connector lines on stacked bar charts), `markercolor=FF0000` (chart-level marker fill color applied to all series)
## Feature Coverage
| Feature | Sheet |
|---|---|
| `bar` (basic horizontal) | 1, 3, 4, 5, 6 |
| `barStacked` | 1, 2 |
| `barPercentStacked` | 2 |
| `bar3d` | 2 |
| `bar3d shape=cylinder` | 2 |
| `dataRange` (cell reference) | 1, 3, 5, 6 |
| `data=` shorthand | 1 |
| `series1=Name:values` | 1, 2, 3, 4, 5, 6 |
| `colors` | 1, 2, 3, 4, 5, 6 |
| `gapwidth` | 1, 2, 4, 6 |
| `overlap` | 1 |
| `dataLabels` / `labelPos` | 1, 3, 4, 6 |
| `labelFont` | 3, 4, 6 |
| `dataLabels.numFmt` | 4 |
| `dataLabels.separator` | 4 |
| `dataLabel{N}.delete/text` | 4 |
| `point{N}.color` | 4 |
| `catTitle` / `axisTitle` | 1 |
| `axisfont` | 1 |
| `axisMin/Max` / `majorUnit` | 4 |
| `gridlines` / `minorGridlines` | 1, 4, 6 |
| `axisLine` / `catAxisLine` | 4 |
| `logBase` | 4 |
| `axisReverse` | 4 |
| `dispUnits` | 4 |
| `axisNumFmt` | 2 |
| `legend` positions | 1, 2, 5, 6 |
| `legendfont` | 5 |
| `legend.overlay` | 5 |
| `title.font/size/color/bold` | 3 |
| `title.glow` / `title.shadow` | 6 |
| `series.shadow` | 3, 6 |
| `series.outline` | 1, 3 |
| `gradients` | 3 |
| `plotFill` / `chartFill` | 3, 6 |
| `transparency` | 3 |
| `roundedCorners` | 3 |
| `referenceLine` | 2, 6 |
| `colorRule` | 6 |
| `secondaryAxis` | 5 |
| `chartArea.border` / `plotArea.border` | 5 |
| `plotArea.x/y/w/h` | 5 |
| `title.x/y` | 5 |
| `legend.x/y/w/h` | 5 |
| `view3d` / `style` | 2 |
| `shape=cylinder` | 2 |
| `errBars` | 6 |
| `dataTable` | 6 |
| `crosses` (autoZero/min/max) | 7 |
| `crossBetween` (between/midCat) | 7 |
| `valAxisVisible` | 7 |
| `labelrotation` | 7 |
| `labeloffset` | 7 |
| `ticklabelskip` | 7 |
| `axisposition` (nextTo/high/low) | 7 |
| `serlines` | 7 |
| `markercolor` | 7 |
## Inspect the Generated File
```bash
officecli query charts-bar.xlsx chart
officecli get charts-bar.xlsx "/1-Bar Fundamentals/chart[1]"
```
+523
View File
@@ -0,0 +1,523 @@
#!/usr/bin/env python3
"""
Bar (Horizontal) Charts Showcase — bar, barStacked, barPercentStacked, and bar3d with all variations.
Generates: charts-bar.xlsx
Every horizontal bar chart feature officecli supports is demonstrated at least once:
gap width, overlap, data labels, axis scaling, gridlines, legend positioning,
reference lines, secondary axis, error bars, gradients, transparency, shadows,
manual layout, data table, 3D rotation, and conditional coloring.
8 sheets (Sheet1 data + 7 chart sheets), 28 charts total.
1-Bar Fundamentals 4 charts — data input variants, colors, stacked, data shorthand
2-Bar Variants 4 charts — barStacked, barPercentStacked, bar3d, cylinder
3-Bar Styling 4 charts — title styling, shadow/outline, gradients, plot/chart fill
4-Axis & Labels 4 charts — axis scale, log/reverse/dispUnits, label styling, per-point
5-Legend & Layout 4 charts — legend positions, overlay, manual layout, secondary axis
6-Advanced 4 charts — reference line, colorRule, glow/shadow, errBars/dataTable
7-Axis Controls 4 charts — crosses/crossBetween, label rotation/skip, axisposition/serlines, markercolor
SDK twin of charts-bar.sh (officecli CLI). Both produce an equivalent
charts-bar.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every cell write and
chart is shipped over the named pipe via `doc.batch(...)` / `doc.send(...)`.
Each item is the same `{"command","parent"/"path","type","props"}` dict you'd
put in an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-bar.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-bar.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(sheet, **props):
"""One `add chart` item in batch-shape (props become --prop k=v)."""
return {"command": "add", "parent": f"/{sheet}", "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Source data — shared across all charts
# ======================================================================
print("--- Populating source data ---")
data_items = []
for j, h in enumerate(["Department", "Q1", "Q2", "Q3", "Q4"]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}1",
"props": {"text": h, "bold": "true"}})
depts = ["Engineering", "Marketing", "Sales", "Support", "Finance", "HR", "Legal", "Operations"]
q1 = [185, 120, 210, 95, 78, 62, 55, 140]
q2 = [195, 135, 225, 105, 82, 68, 58, 152]
q3 = [210, 142, 240, 112, 88, 72, 62, 165]
q4 = [228, 158, 260, 118, 92, 78, 68, 178]
for i in range(8):
r = i + 2
for j, val in enumerate([depts[i], q1[i], q2[i], q3[i], q4[i]]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}{r}",
"props": {"text": str(val)}})
doc.batch(data_items)
# ======================================================================
# Sheet: 1-Bar Fundamentals
# ======================================================================
print("--- 1-Bar Fundamentals ---")
items = [add_sheet("1-Bar Fundamentals")]
# Chart 1: Basic bar chart with dataRange, axis titles, and gridlines
# Features: chartType=bar, dataRange, catTitle, axisTitle, axisfont, gridlines
items.append(chart("1-Bar Fundamentals",
chartType="bar",
title="Department Performance — Q1",
dataRange="Sheet1!A1:B9",
x="0", y="0", width="12", height="18",
catTitle="Department", axisTitle="Score",
axisfont="9:333333:Arial",
gridlines="D9D9D9:0.5:dot"))
# Chart 2: Inline series with custom colors, gap width, and data labels
# Features: inline series, colors per category, gapwidth, dataLabels=outsideEnd
items.append(chart("1-Bar Fundamentals",
chartType="bar",
title="Survey Results",
series1="Satisfaction:85,72,91,68,78",
categories="Product,Service,Delivery,Price,Overall",
colors="4472C4,ED7D31,70AD47,FFC000,5B9BD5",
x="13", y="0", width="12", height="18",
gapwidth="80",
dataLabels="outsideEnd"))
# Chart 3: Stacked bar with overlap and series outline
# Features: barStacked, overlap=0, series.outline (white separator)
items.append(chart("1-Bar Fundamentals",
chartType="barStacked",
title="Quarterly Headcount by Dept",
series1="Q1:30,18,25,12",
series2="Q2:35,20,28,14",
series3="Q3:38,22,30,16",
categories="Engineering,Marketing,Sales,Support",
colors="2E75B6,70AD47,FFC000",
x="0", y="19", width="12", height="18",
overlap="0",
**{"series.outline": "FFFFFF-0.5"}))
# Chart 4: data= shorthand with legend=bottom
# Features: data= shorthand (inline multi-series), legend=bottom
items.append(chart("1-Bar Fundamentals",
chartType="bar",
title="Training Hours by Team",
data="Technical:45,38,52;Soft Skills:20,28,18;Compliance:12,15,10",
categories="Engineering,Sales,Support",
colors="4472C4,ED7D31,70AD47",
x="13", y="19", width="12", height="18",
legend="bottom"))
doc.batch(items)
# ======================================================================
# Sheet: 2-Bar Variants
# ======================================================================
print("--- 2-Bar Variants ---")
items = [add_sheet("2-Bar Variants")]
# Chart 1: barStacked with tight gap width
# Features: barStacked, gapwidth=50 (tight bars)
items.append(chart("2-Bar Variants",
chartType="barStacked",
title="Budget Allocation",
series1="Salaries:120,80,95,60",
series2="Operations:45,35,40,25",
series3="Marketing:30,50,20,15",
categories="Engineering,Sales,Support,HR",
colors="1F4E79,2E75B6,9DC3E6",
x="0", y="0", width="12", height="18",
gapwidth="50",
legend="bottom"))
# Chart 2: barPercentStacked with axis number format and reference line
# Features: barPercentStacked, axisNumFmt=0%, referenceLine with label and dash
items.append(chart("2-Bar Variants",
chartType="barPercentStacked",
title="Task Completion Ratio",
series1="Done:75,60,90,45,80",
series2="In Progress:15,25,5,30,12",
series3="Blocked:10,15,5,25,8",
categories="Backend,Frontend,QA,Design,DevOps",
colors="70AD47,FFC000,C00000",
x="13", y="0", width="12", height="18",
axisNumFmt="0%",
referenceLine="0.5:FF0000:Target:dash",
legend="bottom"))
# Chart 3: bar3d with perspective and style
# Features: bar3d, view3d (rotX,rotY,perspective), style=3
items.append(chart("2-Bar Variants",
chartType="bar3d",
title="3D Revenue by Region",
series1="Revenue:340,280,310,195",
categories="North,South,East,West",
colors="4472C4,ED7D31,70AD47,FFC000",
x="0", y="19", width="12", height="18",
view3d="10,30,20",
style="3",
legend="right"))
# Chart 4: bar3d with cylinder shape
# Features: bar3d shape=cylinder, multi-series 3D bars
items.append(chart("2-Bar Variants",
chartType="bar3d",
title="Cylinder — Project Milestones",
series1="Completed:8,12,6,10,15",
series2="Remaining:4,3,6,5,2",
categories="Alpha,Beta,Gamma,Delta,Epsilon",
colors="2E75B6,BDD7EE",
x="13", y="19", width="12", height="18",
shape="cylinder",
gapwidth="60",
legend="bottom"))
doc.batch(items)
# ======================================================================
# Sheet: 3-Bar Styling
# ======================================================================
print("--- 3-Bar Styling ---")
items = [add_sheet("3-Bar Styling")]
# Chart 1: Title styling (font, size, color, bold)
# Features: title.font, title.size, title.color, title.bold
items.append(chart("3-Bar Styling",
chartType="bar",
title="Styled Title Demo",
series1="Score:88,76,92,65,84",
categories="Dept A,Dept B,Dept C,Dept D,Dept E",
colors="4472C4",
x="0", y="0", width="12", height="18",
gapwidth="100",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"}))
# Chart 2: Series shadow and outline effects
# Features: series.shadow (color-blur-angle-dist-opacity), series.outline
items.append(chart("3-Bar Styling",
chartType="bar",
title="Shadow & Outline",
series1="2024:165,142,180,128",
series2="2025:185,158,195,140",
categories="Engineering,Marketing,Sales,Support",
colors="2E75B6,ED7D31",
x="13", y="0", width="12", height="18",
legend="bottom",
**{"series.shadow": "000000-4-315-2-30",
"series.outline": "1F4E79-1"}))
# Chart 3: Per-series gradients
# Features: gradients (per-bar gradient fills, angle=0 for horizontal), labelFont (size:color:bold)
items.append(chart("3-Bar Styling",
chartType="bar",
title="Gradient Bars",
series1="Revenue:320,275,410,190,245",
categories="North,South,East,West,Central",
x="0", y="19", width="12", height="18",
gradients="1F4E79-5B9BD5:0;C55A11-F4B183:0;548235-A9D18E:0;7F6000-FFD966:0;843C0B-DDA15E:0",
dataLabels="outsideEnd",
labelFont="9:333333:true"))
# Chart 4: Plot fill gradient, chart fill, transparency, rounded corners
# Features: plotFill gradient, chartFill, transparency, roundedCorners
items.append(chart("3-Bar Styling",
chartType="bar",
title="Styled Background",
dataRange="Sheet1!A1:C9",
x="13", y="19", width="12", height="18",
colors="5B9BD5,ED7D31",
plotFill="F0F4F8-D6E4F0:90",
chartFill="FFFFFF",
transparency="20",
roundedCorners="true",
legend="right"))
doc.batch(items)
# ======================================================================
# Sheet: 4-Axis & Labels
# ======================================================================
print("--- 4-Axis & Labels ---")
items = [add_sheet("4-Axis & Labels")]
# Chart 1: Custom axis min/max, majorUnit, and gridlines styling
# Features: axisMin, axisMax, majorUnit, gridlines styling, minorGridlines, axisLine, catAxisLine
items.append(chart("4-Axis & Labels",
chartType="bar",
title="Axis Scale (50250)",
dataRange="Sheet1!A1:B9",
x="0", y="0", width="12", height="18",
axisMin="50", axisMax="250", majorUnit="50",
gridlines="D0D0D0:0.5:solid",
minorGridlines="EEEEEE:0.3:dot",
axisLine="C00000:1.5:solid",
catAxisLine="2E75B6:1.5:solid"))
# Chart 2: Log scale, axis reverse, and display units
# Features: logBase=10, axisReverse=true, dispUnits=thousands
items.append(chart("4-Axis & Labels",
chartType="bar",
title="Log Scale & Reverse",
series1="Users:10,100,1000,5000,25000,100000",
categories="Tier 1,Tier 2,Tier 3,Tier 4,Tier 5,Tier 6",
colors="2E75B6",
x="13", y="0", width="12", height="18",
logBase="10",
axisReverse="true",
dispUnits="thousands",
gridlines="E0E0E0:0.5:dash"))
# Chart 3: Data labels with labelFont, numFmt, separator
# Features: dataLabels, labelFont, dataLabels.numFmt, dataLabels.separator
items.append(chart("4-Axis & Labels",
chartType="bar",
title="Labeled Metrics",
series1="FY2025:148,92,215,178,125",
categories="Revenue,Costs,Gross,EBITDA,Net Income",
colors="4472C4",
x="0", y="19", width="12", height="18",
dataLabels="outsideEnd",
labelFont="10:1F4E79:true",
**{"dataLabels.numFmt": "#,##0",
"dataLabels.separator": ": "}))
# Chart 4: Per-point label delete/text and per-point color
# Features: dataLabel{N}.delete, dataLabel{N}.text, point{N}.color
items.append(chart("4-Axis & Labels",
chartType="bar",
title="Highlight Winner",
series1="Score:72,85,68,95,78",
categories="Team A,Team B,Team C,Team D,Team E",
colors="9DC3E6",
x="13", y="19", width="12", height="18",
dataLabels="true", labelPos="outsideEnd",
gapwidth="70",
**{"dataLabel1.delete": "true", "dataLabel3.delete": "true",
"dataLabel5.delete": "true",
"dataLabel4.text": "Winner!",
"point4.color": "C00000",
"point2.color": "2E75B6"}))
doc.batch(items)
# ======================================================================
# Sheet: 5-Legend & Layout
# ======================================================================
print("--- 5-Legend & Layout ---")
items = [add_sheet("5-Legend & Layout")]
# Chart 1: Legend positions (right)
# Features: legend=right (4-series bar with legend on right)
items.append(chart("5-Legend & Layout",
chartType="bar",
title="Legend: Right",
dataRange="Sheet1!A1:E9",
x="0", y="0", width="12", height="18",
colors="4472C4,ED7D31,70AD47,FFC000",
legend="right"))
# Chart 2: Legend font styling and overlay
# Features: legendfont (size:color:fontname), legend.overlay=true
# legend.overlay precedes legendfont (as in the CLI twin) so c:overlay is
# emitted before c:txPr — the schema order CT_Legend requires.
items.append(chart("5-Legend & Layout", **{
"chartType": "bar",
"title": "Legend: Font & Overlay",
"dataRange": "Sheet1!A1:E9",
"x": "13", "y": "0", "width": "12", "height": "18",
"colors": "1F4E79,2E75B6,5B9BD5,9DC3E6",
"legend": "top",
"legend.overlay": "true",
"legendfont": "10:1F4E79:Calibri"}))
# Chart 3: Manual layout — plotArea.x/y/w/h, title.x/y, legend.x/y/w/h
# Features: plotArea.x/y/w/h, title.x/y, legend.x/y/w/h (manual layout)
items.append(chart("5-Legend & Layout",
chartType="bar",
title="Manual Layout",
dataRange="Sheet1!A1:C9",
x="0", y="19", width="12", height="18",
colors="2E75B6,70AD47",
**{"plotArea.x": "0.25", "plotArea.y": "0.15",
"plotArea.w": "0.70", "plotArea.h": "0.60",
"title.x": "0.20", "title.y": "0.02",
"legend.x": "0.25", "legend.y": "0.82",
"legend.w": "0.50", "legend.h": "0.10",
"title.font": "Arial", "title.size": "13",
"title.bold": "true"}))
# Chart 4: Secondary axis with chart/plot area borders
# Features: secondaryAxis=2, chartArea.border, plotArea.border
items.append(chart("5-Legend & Layout",
chartType="bar",
title="Dual Axis: Revenue vs Margin",
series1="Revenue:340,280,410,195,310",
series2="Margin %:22,18,28,15,25",
categories="North,South,East,West,Central",
colors="2E75B6,C00000",
x="13", y="19", width="12", height="18",
secondaryAxis="2",
legend="bottom",
**{"chartArea.border": "D0D0D0:1:solid",
"plotArea.border": "E0E0E0:0.5:dot"}))
doc.batch(items)
# ======================================================================
# Sheet: 6-Advanced
# ======================================================================
print("--- 6-Advanced ---")
items = [add_sheet("6-Advanced")]
# Chart 1: Reference line with label
# Features: referenceLine (value:color:label:dash style)
items.append(chart("6-Advanced",
chartType="bar",
title="vs Company Average",
series1="Score:82,74,91,68,87,72",
categories="Engineering,Marketing,Sales,Support,Finance,HR",
colors="4472C4",
x="0", y="0", width="12", height="18",
referenceLine="79:FF0000:Average:dash",
gapwidth="80",
gridlines="E0E0E0:0.5:solid"))
# Chart 2: Conditional coloring (colorRule)
# Features: colorRule (threshold:belowColor:aboveColor), referenceLine=0 (zero baseline)
items.append(chart("6-Advanced",
chartType="bar",
title="Profit/Loss by Division",
series1="P&L:120,85,-45,160,-80,95,-20,140",
categories="Div A,Div B,Div C,Div D,Div E,Div F,Div G,Div H",
colors="2E75B6",
x="13", y="0", width="12", height="18",
colorRule="0:C00000:70AD47",
referenceLine="0:888888:1:solid",
dataLabels="outsideEnd",
labelFont="9:333333:false"))
# Chart 3: Title glow, title shadow, series shadow
# Features: title.glow (color-radius-opacity), title.shadow, series.shadow on bar charts
items.append(chart("6-Advanced",
chartType="bar",
title="Glow & Shadow Effects",
series1="East:185,195,210,228",
series2="West:140,152,165,178",
categories="Q1,Q2,Q3,Q4",
colors="4472C4,ED7D31",
x="0", y="19", width="12", height="18",
plotFill="F0F4F8", chartFill="FFFFFF",
legend="bottom",
**{"title.glow": "4472C4-8-60",
"title.shadow": "000000-3-315-2-40",
"title.font": "Calibri", "title.size": "16",
"title.bold": "true", "title.color": "1F4E79",
"series.shadow": "000000-3-315-1-30"}))
# Chart 4: Error bars and data table
# Features: errBars=percent:10, dataTable=true, legend=none
items.append(chart("6-Advanced",
chartType="bar",
title="With Error Bars & Data Table",
dataRange="Sheet1!A1:E9",
x="13", y="19", width="12", height="18",
colors="2E75B6,ED7D31,70AD47,FFC000",
errBars="percent:10",
dataTable="true",
legend="none",
plotFill="FAFAFA"))
doc.batch(items)
# ======================================================================
# Sheet: 7-Axis Controls
# ======================================================================
print("--- 7-Axis Controls ---")
items = [add_sheet("7-Axis Controls")]
# Chart 1: crosses, crossBetween, valAxisVisible
# Features: crosses=autoZero (value axis crosses cat axis at zero, the default),
# crossBetween=between (bars centred between tick marks vs midCat at the mark),
# valAxisVisible=true/false (show or hide the value axis entirely)
items.append(chart("7-Axis Controls",
chartType="bar",
title="Axis Cross Controls",
series1="Sales:120,80,-30,150",
categories="Q1,Q2,Q3,Q4",
x="0", y="0", width="12", height="18",
crosses="autoZero",
crossBetween="between",
valAxisVisible="true"))
# Chart 2: labelrotation, labeloffset, ticklabelskip
# Features: labelrotation=45 (rotate category tick labels, -90..90 degrees),
# labeloffset=100 (category-axis label offset as % of default; 100=default),
# ticklabelskip=2 (draw tick labels every 2nd category — reduces crowding)
items.append(chart("7-Axis Controls",
chartType="column",
title="Tick-label Rotation, Offset & Skip",
series1="Units:45,30,20,55,40,25,60",
categories="January,February,March,April,May,June,July",
x="13", y="0", width="12", height="18",
labelrotation="45",
labeloffset="100",
ticklabelskip="2"))
# Chart 3: axisposition, serlines (stacked bar)
# Features: axisposition=nextTo (tick labels next to the axis — alias for
# tickLabelPos; also accepts: high, low),
# serlines=true (series connector lines on stacked bar charts)
items.append(chart("7-Axis Controls",
chartType="barStacked",
title="Stacked — axisposition + serlines",
series1="Online:55,48,60,70",
series2="Retail:30,40,35,25",
categories="Q1,Q2,Q3,Q4",
colors="4472C4,ED7D31",
x="0", y="19", width="12", height="18",
axisposition="nextTo",
serlines="true"))
# Chart 4: markercolor on line/scatter (chart-level fanout)
# Features: markercolor=FF0000 (chart-level fan-out — applies the fill color
# to every series marker; per-series override via series[N] path)
items.append(chart("7-Axis Controls",
chartType="line",
title="Line — markercolor",
series1="Sales:120,145,132,160",
series2="Costs:80,95,88,110",
categories="Q1,Q2,Q3,Q4",
colors="4472C4,ED7D31",
x="13", y="19", width="12", height="18",
marker="circle", markerSize="8",
markercolor="FF0000",
lineWidth="2"))
doc.batch(items)
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"Generated: {FILE}")
print(" 8 sheets (Sheet1 data + 7 chart sheets, 28 charts total)")
+500
View File
@@ -0,0 +1,500 @@
#!/bin/bash
# Bar (Horizontal) Charts Showcase — generates charts-bar.xlsx exercising the
# full xlsx bar/barStacked/barPercentStacked/bar3d chart family.
#
# CLI twin of charts-bar.py (officecli Python SDK). Both produce an equivalent
# charts-bar.xlsx.
#
# 8 sheets (Sheet1 data + 7 chart sheets), 28 charts total.
#
# Usage: ./charts-bar.sh
FILE="$(dirname "$0")/charts-bar.xlsx"
rm -f "$FILE"
# Forward-compat tolerance: this showcase deliberately exercises a few props the
# chart handler doesn't consume yet (e.g. point{N}.color → exit 2
# unsupported_property) and a few values it rejects on some chart types (e.g.
# axisposition=nextTo). officecli warns but still creates the element; we log the
# warning and press on rather than aborting, so the whole 28-chart showcase
# runs — matching the SDK twin, whose doc.batch() doesn't abort on these either.
officecli() {
command officecli "$@" || echo " (officecli exit $? — continuing; prop unsupported on this chart type)"
}
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Source data — shared across all charts
# ==========================================================================
echo "--- Populating source data ---"
officecli set "$FILE" /Sheet1/A1 --prop text=Department --prop bold=true
officecli set "$FILE" /Sheet1/B1 --prop text=Q1 --prop bold=true
officecli set "$FILE" /Sheet1/C1 --prop text=Q2 --prop bold=true
officecli set "$FILE" /Sheet1/D1 --prop text=Q3 --prop bold=true
officecli set "$FILE" /Sheet1/E1 --prop text=Q4 --prop bold=true
officecli set "$FILE" /Sheet1/A2 --prop text=Engineering
officecli set "$FILE" /Sheet1/B2 --prop text=185
officecli set "$FILE" /Sheet1/C2 --prop text=195
officecli set "$FILE" /Sheet1/D2 --prop text=210
officecli set "$FILE" /Sheet1/E2 --prop text=228
officecli set "$FILE" /Sheet1/A3 --prop text=Marketing
officecli set "$FILE" /Sheet1/B3 --prop text=120
officecli set "$FILE" /Sheet1/C3 --prop text=135
officecli set "$FILE" /Sheet1/D3 --prop text=142
officecli set "$FILE" /Sheet1/E3 --prop text=158
officecli set "$FILE" /Sheet1/A4 --prop text=Sales
officecli set "$FILE" /Sheet1/B4 --prop text=210
officecli set "$FILE" /Sheet1/C4 --prop text=225
officecli set "$FILE" /Sheet1/D4 --prop text=240
officecli set "$FILE" /Sheet1/E4 --prop text=260
officecli set "$FILE" /Sheet1/A5 --prop text=Support
officecli set "$FILE" /Sheet1/B5 --prop text=95
officecli set "$FILE" /Sheet1/C5 --prop text=105
officecli set "$FILE" /Sheet1/D5 --prop text=112
officecli set "$FILE" /Sheet1/E5 --prop text=118
officecli set "$FILE" /Sheet1/A6 --prop text=Finance
officecli set "$FILE" /Sheet1/B6 --prop text=78
officecli set "$FILE" /Sheet1/C6 --prop text=82
officecli set "$FILE" /Sheet1/D6 --prop text=88
officecli set "$FILE" /Sheet1/E6 --prop text=92
officecli set "$FILE" /Sheet1/A7 --prop text=HR
officecli set "$FILE" /Sheet1/B7 --prop text=62
officecli set "$FILE" /Sheet1/C7 --prop text=68
officecli set "$FILE" /Sheet1/D7 --prop text=72
officecli set "$FILE" /Sheet1/E7 --prop text=78
officecli set "$FILE" /Sheet1/A8 --prop text=Legal
officecli set "$FILE" /Sheet1/B8 --prop text=55
officecli set "$FILE" /Sheet1/C8 --prop text=58
officecli set "$FILE" /Sheet1/D8 --prop text=62
officecli set "$FILE" /Sheet1/E8 --prop text=68
officecli set "$FILE" /Sheet1/A9 --prop text=Operations
officecli set "$FILE" /Sheet1/B9 --prop text=140
officecli set "$FILE" /Sheet1/C9 --prop text=152
officecli set "$FILE" /Sheet1/D9 --prop text=165
officecli set "$FILE" /Sheet1/E9 --prop text=178
# ==========================================================================
# Sheet: 1-Bar Fundamentals
# ==========================================================================
echo "--- 1-Bar Fundamentals ---"
officecli add "$FILE" / --type sheet --prop name="1-Bar Fundamentals"
# Chart 1: Basic bar chart with dataRange, axis titles, and gridlines
# Features: chartType=bar, dataRange, catTitle, axisTitle, axisfont, gridlines
officecli add "$FILE" "/1-Bar Fundamentals" --type chart \
--prop chartType=bar \
--prop title="Department Performance — Q1" \
--prop dataRange=Sheet1!A1:B9 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop catTitle=Department --prop axisTitle=Score \
--prop axisfont=9:333333:Arial \
--prop gridlines=D9D9D9:0.5:dot
# Chart 2: Inline series with custom colors, gap width, and data labels
# Features: inline series, colors per category, gapwidth, dataLabels=outsideEnd
officecli add "$FILE" "/1-Bar Fundamentals" --type chart \
--prop chartType=bar \
--prop title="Survey Results" \
--prop series1=Satisfaction:85,72,91,68,78 \
--prop categories=Product,Service,Delivery,Price,Overall \
--prop colors=4472C4,ED7D31,70AD47,FFC000,5B9BD5 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop gapwidth=80 \
--prop dataLabels=outsideEnd
# Chart 3: Stacked bar with overlap and series outline
# Features: barStacked, overlap=0, series.outline (white separator)
officecli add "$FILE" "/1-Bar Fundamentals" --type chart \
--prop chartType=barStacked \
--prop title="Quarterly Headcount by Dept" \
--prop series1=Q1:30,18,25,12 \
--prop series2=Q2:35,20,28,14 \
--prop series3=Q3:38,22,30,16 \
--prop categories=Engineering,Marketing,Sales,Support \
--prop colors=2E75B6,70AD47,FFC000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop overlap=0 \
--prop series.outline=FFFFFF-0.5
# Chart 4: data= shorthand with legend=bottom
# Features: data= shorthand (inline multi-series), legend=bottom
officecli add "$FILE" "/1-Bar Fundamentals" --type chart \
--prop chartType=bar \
--prop title="Training Hours by Team" \
--prop "data=Technical:45,38,52;Soft Skills:20,28,18;Compliance:12,15,10" \
--prop categories=Engineering,Sales,Support \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom
# ==========================================================================
# Sheet: 2-Bar Variants
# ==========================================================================
echo "--- 2-Bar Variants ---"
officecli add "$FILE" / --type sheet --prop name="2-Bar Variants"
# Chart 1: barStacked with tight gap width
# Features: barStacked, gapwidth=50 (tight bars)
officecli add "$FILE" "/2-Bar Variants" --type chart \
--prop chartType=barStacked \
--prop title="Budget Allocation" \
--prop series1=Salaries:120,80,95,60 \
--prop series2=Operations:45,35,40,25 \
--prop series3=Marketing:30,50,20,15 \
--prop categories=Engineering,Sales,Support,HR \
--prop colors=1F4E79,2E75B6,9DC3E6 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop gapwidth=50 \
--prop legend=bottom
# Chart 2: barPercentStacked with axis number format and reference line
# Features: barPercentStacked, axisNumFmt=0%, referenceLine with label and dash
officecli add "$FILE" "/2-Bar Variants" --type chart \
--prop chartType=barPercentStacked \
--prop title="Task Completion Ratio" \
--prop series1=Done:75,60,90,45,80 \
--prop "series2=In Progress:15,25,5,30,12" \
--prop series3=Blocked:10,15,5,25,8 \
--prop categories=Backend,Frontend,QA,Design,DevOps \
--prop colors=70AD47,FFC000,C00000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop axisNumFmt=0% \
--prop referenceLine=0.5:FF0000:Target:dash \
--prop legend=bottom
# Chart 3: bar3d with perspective and style
# Features: bar3d, view3d (rotX,rotY,perspective), style=3
officecli add "$FILE" "/2-Bar Variants" --type chart \
--prop chartType=bar3d \
--prop title="3D Revenue by Region" \
--prop series1=Revenue:340,280,310,195 \
--prop categories=North,South,East,West \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=10,30,20 \
--prop style=3 \
--prop legend=right
# Chart 4: bar3d with cylinder shape
# Features: bar3d shape=cylinder, multi-series 3D bars
officecli add "$FILE" "/2-Bar Variants" --type chart \
--prop chartType=bar3d \
--prop title="Cylinder — Project Milestones" \
--prop series1=Completed:8,12,6,10,15 \
--prop series2=Remaining:4,3,6,5,2 \
--prop categories=Alpha,Beta,Gamma,Delta,Epsilon \
--prop colors=2E75B6,BDD7EE \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop shape=cylinder \
--prop gapwidth=60 \
--prop legend=bottom
# ==========================================================================
# Sheet: 3-Bar Styling
# ==========================================================================
echo "--- 3-Bar Styling ---"
officecli add "$FILE" / --type sheet --prop name="3-Bar Styling"
# Chart 1: Title styling (font, size, color, bold)
# Features: title.font, title.size, title.color, title.bold
officecli add "$FILE" "/3-Bar Styling" --type chart \
--prop chartType=bar \
--prop title="Styled Title Demo" \
--prop series1=Score:88,76,92,65,84 \
--prop categories=Dept A,Dept B,Dept C,Dept D,Dept E \
--prop colors=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop gapwidth=100
# Chart 2: Series shadow and outline effects
# Features: series.shadow (color-blur-angle-dist-opacity), series.outline
officecli add "$FILE" "/3-Bar Styling" --type chart \
--prop chartType=bar \
--prop title="Shadow & Outline" \
--prop series1=2024:165,142,180,128 \
--prop series2=2025:185,158,195,140 \
--prop categories=Engineering,Marketing,Sales,Support \
--prop colors=2E75B6,ED7D31 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop series.shadow=000000-4-315-2-30 \
--prop series.outline=1F4E79-1 \
--prop legend=bottom
# Chart 3: Per-series gradients
# Features: gradients (per-bar gradient fills, angle=0 for horizontal), labelFont (size:color:bold)
officecli add "$FILE" "/3-Bar Styling" --type chart \
--prop chartType=bar \
--prop title="Gradient Bars" \
--prop series1=Revenue:320,275,410,190,245 \
--prop categories=North,South,East,West,Central \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop "gradients=1F4E79-5B9BD5:0;C55A11-F4B183:0;548235-A9D18E:0;7F6000-FFD966:0;843C0B-DDA15E:0" \
--prop dataLabels=outsideEnd \
--prop labelFont=9:333333:true
# Chart 4: Plot fill gradient, chart fill, transparency, rounded corners
# Features: plotFill gradient, chartFill, transparency, roundedCorners
officecli add "$FILE" "/3-Bar Styling" --type chart \
--prop chartType=bar \
--prop title="Styled Background" \
--prop dataRange=Sheet1!A1:C9 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=5B9BD5,ED7D31 \
--prop plotFill=F0F4F8-D6E4F0:90 \
--prop chartFill=FFFFFF \
--prop transparency=20 \
--prop roundedCorners=true \
--prop legend=right
# ==========================================================================
# Sheet: 4-Axis & Labels
# ==========================================================================
echo "--- 4-Axis & Labels ---"
officecli add "$FILE" / --type sheet --prop name="4-Axis & Labels"
# Chart 1: Custom axis min/max, majorUnit, and gridlines styling
# Features: axisMin, axisMax, majorUnit, gridlines styling, minorGridlines, axisLine, catAxisLine
officecli add "$FILE" "/4-Axis & Labels" --type chart \
--prop chartType=bar \
--prop title="Axis Scale (50250)" \
--prop dataRange=Sheet1!A1:B9 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop axisMin=50 --prop axisMax=250 --prop majorUnit=50 \
--prop gridlines=D0D0D0:0.5:solid \
--prop minorGridlines=EEEEEE:0.3:dot \
--prop axisLine=C00000:1.5:solid \
--prop catAxisLine=2E75B6:1.5:solid
# Chart 2: Log scale, axis reverse, and display units
# Features: logBase=10, axisReverse=true, dispUnits=thousands
officecli add "$FILE" "/4-Axis & Labels" --type chart \
--prop chartType=bar \
--prop title="Log Scale & Reverse" \
--prop "series1=Users:10,100,1000,5000,25000,100000" \
--prop "categories=Tier 1,Tier 2,Tier 3,Tier 4,Tier 5,Tier 6" \
--prop colors=2E75B6 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop logBase=10 \
--prop axisReverse=true \
--prop dispUnits=thousands \
--prop gridlines=E0E0E0:0.5:dash
# Chart 3: Data labels with labelFont, numFmt, separator
# Features: dataLabels, labelFont, dataLabels.numFmt, dataLabels.separator
officecli add "$FILE" "/4-Axis & Labels" --type chart \
--prop chartType=bar \
--prop title="Labeled Metrics" \
--prop series1=FY2025:148,92,215,178,125 \
--prop categories=Revenue,Costs,Gross,EBITDA,Net Income \
--prop colors=4472C4 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop dataLabels=outsideEnd \
--prop labelFont=10:1F4E79:true \
--prop dataLabels.numFmt=#,##0 \
--prop "dataLabels.separator=: "
# Chart 4: Per-point label delete/text and per-point color
# Features: dataLabel{N}.delete, dataLabel{N}.text, point{N}.color
officecli add "$FILE" "/4-Axis & Labels" --type chart \
--prop chartType=bar \
--prop title="Highlight Winner" \
--prop series1=Score:72,85,68,95,78 \
--prop categories=Team A,Team B,Team C,Team D,Team E \
--prop colors=9DC3E6 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop dataLabel1.delete=true --prop dataLabel3.delete=true \
--prop dataLabel5.delete=true \
--prop dataLabel4.text="Winner!" \
--prop point4.color=C00000 \
--prop point2.color=2E75B6 \
--prop gapwidth=70
# ==========================================================================
# Sheet: 5-Legend & Layout
# ==========================================================================
echo "--- 5-Legend & Layout ---"
officecli add "$FILE" / --type sheet --prop name="5-Legend & Layout"
# Chart 1: Legend positions (right)
# Features: legend=right (4-series bar with legend on right)
officecli add "$FILE" "/5-Legend & Layout" --type chart \
--prop chartType=bar \
--prop title="Legend: Right" \
--prop dataRange=Sheet1!A1:E9 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop legend=right
# Chart 2: Legend font styling and overlay
# Features: legendfont (size:color:fontname), legend.overlay=true
officecli add "$FILE" "/5-Legend & Layout" --type chart \
--prop chartType=bar \
--prop title="Legend: Font & Overlay" \
--prop dataRange=Sheet1!A1:E9 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=1F4E79,2E75B6,5B9BD5,9DC3E6 \
--prop legend=top \
--prop legend.overlay=true \
--prop legendfont=10:1F4E79:Calibri
# Chart 3: Manual layout — plotArea.x/y/w/h, title.x/y, legend.x/y/w/h
# Features: plotArea.x/y/w/h, title.x/y, legend.x/y/w/h (manual layout)
officecli add "$FILE" "/5-Legend & Layout" --type chart \
--prop chartType=bar \
--prop title="Manual Layout" \
--prop dataRange=Sheet1!A1:C9 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=2E75B6,70AD47 \
--prop plotArea.x=0.25 --prop plotArea.y=0.15 \
--prop plotArea.w=0.70 --prop plotArea.h=0.60 \
--prop title.x=0.20 --prop title.y=0.02 \
--prop legend.x=0.25 --prop legend.y=0.82 \
--prop legend.w=0.50 --prop legend.h=0.10 \
--prop title.font=Arial --prop title.size=13 \
--prop title.bold=true
# Chart 4: Secondary axis with chart/plot area borders
# Features: secondaryAxis=2, chartArea.border, plotArea.border
officecli add "$FILE" "/5-Legend & Layout" --type chart \
--prop chartType=bar \
--prop title="Dual Axis: Revenue vs Margin" \
--prop "series1=Revenue:340,280,410,195,310" \
--prop "series2=Margin %:22,18,28,15,25" \
--prop categories=North,South,East,West,Central \
--prop colors=2E75B6,C00000 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop secondaryAxis=2 \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot \
--prop legend=bottom
# ==========================================================================
# Sheet: 6-Advanced
# ==========================================================================
echo "--- 6-Advanced ---"
officecli add "$FILE" / --type sheet --prop name="6-Advanced"
# Chart 1: Reference line with label
# Features: referenceLine (value:color:label:dash style)
officecli add "$FILE" "/6-Advanced" --type chart \
--prop chartType=bar \
--prop title="vs Company Average" \
--prop series1=Score:82,74,91,68,87,72 \
--prop categories=Engineering,Marketing,Sales,Support,Finance,HR \
--prop colors=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop referenceLine=79:FF0000:Average:dash \
--prop gapwidth=80 \
--prop gridlines=E0E0E0:0.5:solid
# Chart 2: Conditional coloring (colorRule)
# Features: colorRule (threshold:belowColor:aboveColor), referenceLine=0 (zero baseline)
officecli add "$FILE" "/6-Advanced" --type chart \
--prop chartType=bar \
--prop title="Profit/Loss by Division" \
--prop "series1=P&L:120,85,-45,160,-80,95,-20,140" \
--prop categories=Div A,Div B,Div C,Div D,Div E,Div F,Div G,Div H \
--prop colors=2E75B6 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colorRule=0:C00000:70AD47 \
--prop referenceLine=0:888888:1:solid \
--prop dataLabels=outsideEnd \
--prop labelFont=9:333333:false
# Chart 3: Title glow, title shadow, series shadow
# Features: title.glow (color-radius-opacity), title.shadow, series.shadow on bar charts
officecli add "$FILE" "/6-Advanced" --type chart \
--prop chartType=bar \
--prop title="Glow & Shadow Effects" \
--prop series1=East:185,195,210,228 \
--prop series2=West:140,152,165,178 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop title.glow=4472C4-8-60 \
--prop title.shadow=000000-3-315-2-40 \
--prop title.font=Calibri --prop title.size=16 \
--prop title.bold=true --prop title.color=1F4E79 \
--prop series.shadow=000000-3-315-1-30 \
--prop plotFill=F0F4F8 --prop chartFill=FFFFFF \
--prop legend=bottom
# Chart 4: Error bars and data table
# Features: errBars=percent:10, dataTable=true, legend=none
officecli add "$FILE" "/6-Advanced" --type chart \
--prop chartType=bar \
--prop title="With Error Bars & Data Table" \
--prop dataRange=Sheet1!A1:E9 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop errBars=percent:10 \
--prop dataTable=true \
--prop legend=none \
--prop plotFill=FAFAFA
# ==========================================================================
# Sheet: 7-Axis Controls
# ==========================================================================
echo "--- 7-Axis Controls ---"
officecli add "$FILE" / --type sheet --prop name="7-Axis Controls"
# Chart 1: crosses, crossBetween, valAxisVisible
# Features: crosses=autoZero, crossBetween=between, valAxisVisible=true/false
officecli add "$FILE" "/7-Axis Controls" --type chart \
--prop chartType=bar \
--prop title="Axis Cross Controls" \
--prop series1=Sales:120,80,-30,150 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop crosses=autoZero \
--prop crossBetween=between \
--prop valAxisVisible=true
# Chart 2: labelrotation, labeloffset, ticklabelskip
# Features: labelrotation=45, labeloffset=100, ticklabelskip=2
officecli add "$FILE" "/7-Axis Controls" --type chart \
--prop chartType=column \
--prop title="Tick-label Rotation, Offset & Skip" \
--prop series1=Units:45,30,20,55,40,25,60 \
--prop categories=January,February,March,April,May,June,July \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop labelrotation=45 \
--prop labeloffset=100 \
--prop ticklabelskip=2
# Chart 3: axisposition, serlines (stacked bar)
# Features: axisposition=nextTo (alias for tickLabelPos), serlines=true
officecli add "$FILE" "/7-Axis Controls" --type chart \
--prop chartType=barStacked \
--prop title="Stacked — axisposition + serlines" \
--prop series1=Online:55,48,60,70 \
--prop series2=Retail:30,40,35,25 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop axisposition=nextTo \
--prop serlines=true
# Chart 4: markercolor on line/scatter (chart-level fanout)
# Features: markercolor=FF0000 (chart-level fan-out to every series marker)
officecli add "$FILE" "/7-Axis Controls" --type chart \
--prop chartType=line \
--prop title="Line — markercolor" \
--prop series1=Sales:120,145,132,160 \
--prop series2=Costs:80,95,88,110 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=circle --prop markerSize=8 \
--prop markercolor=FF0000 \
--prop lineWidth=2
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+267
View File
@@ -0,0 +1,267 @@
# Basic Charts Showcase
This demo consists of three files that work together:
- **charts-basic.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments, then executed by the script.
- **charts-basic.xlsx** — The generated workbook with 8 sheets (1 data + 7 chart sheets, 28 charts total). Open in Excel to see the rendered charts.
- **charts-basic.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-basic.py
# → charts-basic.xlsx
```
## Source Data
**Sheet1**: 12 months of regional sales data (East, South, North, West) used by all charts.
## Chart Sheets
### Sheet: 1-Column Charts
Four column chart variants demonstrating the column family.
```bash
# Basic clustered column with axis titles and axis font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop title="Regional Sales" \
--prop dataRange=Sheet1!A1:E13 \
--prop catTitle=Month --prop axisTitle=Sales \
--prop axisfont=9:58626E:Arial \
--prop gridlines=D9D9D9:0.5:dot
# Stacked column with custom colors, data labels, gap control, series outline
officecli add data.xlsx /Sheet --type chart \
--prop chartType=columnStacked \
--prop colors=2E75B6,70AD47,FFC000,C00000 \
--prop dataLabels=true --prop labelPos=center \
--prop gapwidth=60 \
--prop series.outline=FFFFFF-0.5
# 100% stacked with legend positioning and plot fill
officecli add data.xlsx /Sheet --type chart \
--prop chartType=columnPercentStacked \
--prop legend=bottom --prop legendfont=9:8B949E \
--prop plotFill=F5F5F5
# 3D column with perspective and title styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column3d \
--prop view3d=15,20,30 \
--prop title.font=Calibri --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true
```
**Features:** `column`, `columnStacked`, `columnPercentStacked`, `column3d`, `dataRange`, `catTitle`, `axisTitle`, `axisfont`, `gridlines`, `colors`, `dataLabels`, `labelPos`, `gapwidth`, `series.outline`, `legend`, `legendfont`, `plotFill`, `view3d`, `title.font/size/color/bold`
### Sheet: 2-Bar Charts
Four horizontal bar chart variants.
```bash
# Horizontal bar with inline data and gap control
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar \
--prop 'data=East:198;South:158;North:142;West:180' \
--prop gapwidth=80 \
--prop dataLabels=true --prop labelPos=outsideEnd
# Stacked bar with named series and overlap
officecli add data.xlsx /Sheet --type chart \
--prop chartType=barStacked \
--prop series1=H1:663,598,528,661 \
--prop series2=H2:833,718,669,868 \
--prop gapwidth=50 --prop overlap=0
# 100% stacked bar with reference line and axis lines
# Note: value axis of a barPercentStacked chart is 0-1 (= 0%-100%), so a 50% line = 0.5
# referenceLine forms: value | value:color | value:color:label | value:color:width:dash
# | value:color:label:dash | value:color:width:dash:label
# Width is in points (default 1.5pt). e.g. 0.5:FF0000:2:dash draws a 2pt dashed line.
officecli add data.xlsx /Sheet --type chart \
--prop chartType=barPercentStacked \
--prop referenceLine=0.5:FF0000:Target:dash \
--prop axisLine=333333:1:solid \
--prop catAxisLine=333333:1:solid
# 3D bar with chart area fill and preset style
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bar3d \
--prop view3d=10,30,20 \
--prop chartFill=F2F2F2 \
--prop style=3
```
**Features:** `bar`, `barStacked`, `barPercentStacked`, `bar3d`, inline `data`, named `series`, `gapwidth`, `overlap`, `labelPos=outsideEnd`, `referenceLine`, `axisLine`, `catAxisLine`, `chartFill`, `style`
### Sheet: 3-Line Charts
Four line chart variants with markers, smoothing, and data tables.
```bash
# Line with cell-range series (dotted syntax) and markers
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop series1.name=East \
--prop series1.values=Sheet1!B2:B13 \
--prop series1.categories=Sheet1!A2:A13 \
--prop showMarkers=true --prop marker=circle:6:2E75B6 \
--prop gridlines=D9D9D9:0.5:dot \
--prop minorGridlines=EEEEEE:0.3:dot
# Smooth line with series shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop smooth=true --prop lineWidth=2.5 \
--prop gridlines=none \
--prop series.shadow=000000-4-315-2-40
# Stacked line with tick marks
officecli add data.xlsx /Sheet --type chart \
--prop chartType=lineStacked \
--prop majorTickMark=outside --prop tickLabelPos=low
# Dashed line with data table and hidden legend
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop lineDash=dash --prop lineWidth=1.5 \
--prop dataTable=true --prop legend=none
```
**Features:** `series1.name/values/categories` (cell range), `showMarkers`, `marker` (style:size:color), `smooth`, `lineWidth`, `lineDash`, `gridlines`, `minorGridlines`, `series.shadow`, `lineStacked`, `majorTickMark`, `tickLabelPos`, `dataTable`, `legend=none`
### Sheet: 4-Area Charts
Four area chart variants with transparency and gradients.
```bash
# Area with transparency and gradient
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area \
--prop transparency=40 \
--prop gradient=4472C4-BDD7EE:90
# Stacked area with plot fill and rounded corners
officecli add data.xlsx /Sheet --type chart \
--prop chartType=areaStacked \
--prop plotFill=F5F5F5 --prop roundedCorners=true
# 100% stacked area with axis visibility control
officecli add data.xlsx /Sheet --type chart \
--prop chartType=areaPercentStacked \
--prop axisVisible=true --prop axisLine=999999:0.5:solid
# 3D area with perspective
officecli add data.xlsx /Sheet --type chart \
--prop chartType=area3d \
--prop view3d=20,25,15
```
**Features:** `area`, `areaStacked`, `areaPercentStacked`, `area3d`, `transparency`, `gradient`, `plotFill`, `roundedCorners`, `axisVisible`, `axisLine`
### Sheet: 5-Styling
Demonstrates styling and formatting properties on various charts.
```bash
# Fully styled chart: title effects, legend, axis fonts, series effects
officecli add data.xlsx /Sheet --type chart \
--prop title.font=Georgia --prop title.size=18 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30 \
--prop legendfont=10:444444:Helvetica --prop legend=right \
--prop axisfont=9:58626E:Arial \
--prop series.outline=FFFFFF-0.5 \
--prop series.shadow=000000-3-315-2-25 \
--prop roundedCorners=true --prop referenceLine=160:FF0000:1:dash
# Dual Y-axis (secondary axis)
officecli add data.xlsx /Sheet --type chart \
--prop secondaryAxis=2
# Per-point coloring and negative value inversion
officecli add data.xlsx /Sheet --type chart \
--prop point1.color=70AD47 --prop point3.color=FF0000 \
--prop invertIfNeg=true
# Gradient plot fill and custom data label text
officecli add data.xlsx /Sheet --type chart \
--prop plotFill=E8F0FE-FFFFFF:90 \
--prop marker=diamond:8:4472C4 \
--prop dataLabels.numFmt=#,##0 \
--prop dataLabel3.text=Peak!
```
**Features:** `title.shadow`, `secondaryAxis`, `point{N}.color`, `invertIfNeg`, `plotFill` gradient, `dataLabels.numFmt`, `dataLabel{N}.text`
### Sheet: 6-Layout
Manual positioning and axis control properties.
```bash
# Manual layout of plot area, title, legend
officecli add data.xlsx /Sheet --type chart \
--prop plotArea.x=0.15 --prop plotArea.y=0.15 \
--prop plotArea.w=0.7 --prop plotArea.h=0.7 \
--prop title.x=0.3 --prop title.y=0.01 \
--prop legend.x=0.02 --prop legend.y=0.4 \
--prop legend.overlay=true
# Logarithmic scale, reversed axis, display units
officecli add data.xlsx /Sheet --type chart \
--prop logBase=10 \
--prop axisOrientation=maxMin \
--prop dispUnits=thousands
# Label font, separator, per-label hide
officecli add data.xlsx /Sheet --type chart \
--prop labelFont=11:2E75B6:true \
--prop "dataLabels.separator=: " \
--prop dataLabel2.text=Best! \
--prop dataLabel3.delete=true
# Error bars, minor ticks, opacity
officecli add data.xlsx /Sheet --type chart \
--prop errBars=percentage \
--prop majorTickMark=outside --prop minorTickMark=inside \
--prop opacity=80
```
**Features:** `plotArea.x/y/w/h`, `title.x/y`, `legend.x/y`, `legend.overlay`, `logBase`, `axisOrientation`, `dispUnits`, `labelFont`, `dataLabels.separator`, `dataLabel{N}.delete`, `errBars`, `minorTickMark`, `opacity`
### Sheet: 7-Effects
Visual effects: gradients, conditional colors, glow, presets.
```bash
# Per-series gradients
officecli add data.xlsx /Sheet --type chart \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90'
# Area fill gradient and title glow
officecli add data.xlsx /Sheet --type chart \
--prop areafill=4472C4-BDD7EE:90 \
--prop title.glow=4472C4-8-60
# Conditional coloring (below/above threshold)
officecli add data.xlsx /Sheet --type chart \
--prop colorRule=60:FF0000:70AD47
# Preset style and leader lines
officecli add data.xlsx /Sheet --type chart \
--prop style=26 \
--prop dataLabels.showLeaderLines=true
```
**Features:** `gradients`, `areafill`, `title.glow`, `colorRule`, `style`, `dataLabels.showLeaderLines`
## Inspect the Generated File
```bash
officecli query charts-basic.xlsx chart
officecli get charts-basic.xlsx "/1-Column Charts/chart[1]"
```
+547
View File
@@ -0,0 +1,547 @@
#!/usr/bin/env python3
"""
Basic Charts Showcase — column, bar, line, and area charts with all variations.
Generates: charts-basic.xlsx
Each sheet demonstrates one chart family with all its variants and key properties.
See charts-basic.md for a guide to each sheet.
SDK twin of charts-basic.sh (officecli CLI). Both produce an equivalent
charts-basic.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started, every cell write and
every chart is shipped over the named pipe, batched per sheet. Each item is the
same `{"command","parent","type","props"}` / `{"command":"set","path","props"}`
dict you'd put in an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-basic.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-basic.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(sheet, **props):
"""One `add chart` item in batch-shape (parent is the sheet path)."""
return {"command": "add", "parent": f"/{sheet}", "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Source data — shared across all charts
# ======================================================================
print("--- Populating source data ---")
data_items = []
for j, h in enumerate(["Month", "East", "South", "North", "West"]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}1",
"props": {"text": h, "bold": "true"}})
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
east = [120, 135, 148, 162, 155, 178, 195, 210, 188, 172, 165, 198]
south = [95, 108, 115, 128, 142, 155, 168, 175, 160, 148, 135, 158]
north = [88, 92, 105, 118, 125, 138, 145, 152, 140, 130, 122, 142]
west = [110, 118, 130, 145, 138, 162, 175, 190, 170, 155, 148, 180]
for i in range(12):
r = i + 2
for j, val in enumerate([months[i], east[i], south[i], north[i], west[i]]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}{r}",
"props": {"text": str(val)}})
doc.batch(data_items)
# ======================================================================
# Sheet: 1-Column Charts
# ======================================================================
print("--- 1-Column Charts ---")
doc.batch([
add_sheet("1-Column Charts"),
# ------------------------------------------------------------------
# Chart 1: Basic clustered column from cell range with axis titles
# Features: chartType=column, dataRange, catTitle, axisTitle, axisfont, gridlines
# ------------------------------------------------------------------
chart("1-Column Charts",
chartType="column",
title="Regional Sales by Month",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="12", height="18",
catTitle="Month", axisTitle="Sales",
axisfont="9:58626E:Arial",
gridlines="D9D9D9:0.5:dot"),
# ------------------------------------------------------------------
# Chart 2: Stacked column with custom colors, data labels, and gap control
# Features: columnStacked, colors, dataLabels, labelPos, gapwidth, series.outline
# ------------------------------------------------------------------
chart("1-Column Charts",
chartType="columnStacked",
title="Stacked Regional Sales",
dataRange="Sheet1!A1:E13",
colors="2E75B6,70AD47,FFC000,C00000",
x="13", y="0", width="12", height="18",
dataLabels="true", labelPos="center",
gapwidth="60",
**{"series.outline": "FFFFFF-0.5"}),
# ------------------------------------------------------------------
# Chart 3: 100% stacked column with legend position and plotFill
# Features: columnPercentStacked, legend=bottom, legendfont, plotFill
# ------------------------------------------------------------------
chart("1-Column Charts",
chartType="columnPercentStacked",
title="Market Share by Month",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
legend="bottom",
legendfont="9:8B949E",
plotFill="F5F5F5"),
# ------------------------------------------------------------------
# Chart 4: 3D column with perspective and title styling
# Features: column3d, view3d (rotX,rotY,perspective), title.font/size/color/bold
# ------------------------------------------------------------------
chart("1-Column Charts",
chartType="column3d",
title="3D Regional Sales",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
view3d="15,20,30",
**{"title.font": "Calibri", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"}),
])
# ======================================================================
# Sheet: 2-Bar Charts
# ======================================================================
print("--- 2-Bar Charts ---")
doc.batch([
add_sheet("2-Bar Charts"),
# ------------------------------------------------------------------
# Chart 1: Horizontal bar with inline data and gapwidth
# Features: bar, inline data (Name:v1;Name2:v2), gapwidth, labelPos=outsideEnd
# ------------------------------------------------------------------
chart("2-Bar Charts",
chartType="bar",
title="Q4 Sales by Region",
data="East:198;South:158;North:142;West:180",
categories="East,South,North,West",
colors="2E75B6,70AD47,FFC000,C00000",
x="0", y="0", width="12", height="18",
gapwidth="80",
dataLabels="true", labelPos="outsideEnd"),
# ------------------------------------------------------------------
# Chart 2: Stacked bar with named series and overlap
# Features: barStacked, named series (series1=Name:v1,v2), overlap
# ------------------------------------------------------------------
chart("2-Bar Charts",
chartType="barStacked",
title="H1 vs H2 Sales",
series1="H1:663,598,528,661",
series2="H2:833,718,669,868",
categories="East,South,North,West",
colors="4472C4,ED7D31",
x="13", y="0", width="12", height="18",
dataLabels="true", labelPos="center",
gapwidth="50", overlap="0"),
# ------------------------------------------------------------------
# Chart 3: 100% stacked bar with reference line
#
# Note: on a barPercentStacked chart, the value axis is 0-1 (displayed as
# 0%-100%), so a 50% reference line must be written as 0.5 — not 50.
# referenceLine supports: value | value:color | value:color:label |
# value:color:width:dash | value:color:label:dash (legacy) |
# value:color:width:dash:label (canonical). Width is in points; default 1.5pt.
#
# Features: barPercentStacked, referenceLine, axisLine, catAxisLine
# ------------------------------------------------------------------
chart("2-Bar Charts",
chartType="barPercentStacked",
title="Regional Contribution %",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
referenceLine="0.5:FF0000:Target:dash",
axisLine="333333:1:solid",
catAxisLine="333333:1:solid"),
# ------------------------------------------------------------------
# Chart 4: 3D bar with chart area fill and display units
# Features: bar3d, chartFill (chart area background), style/styleId (preset 1-48)
# ------------------------------------------------------------------
chart("2-Bar Charts",
chartType="bar3d",
title="3D Regional Comparison",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
view3d="10,30,20",
chartFill="F2F2F2",
style="3"),
])
# ======================================================================
# Sheet: 3-Line Charts
# ======================================================================
print("--- 3-Line Charts ---")
doc.batch([
add_sheet("3-Line Charts"),
# ------------------------------------------------------------------
# Chart 1: Line with markers and cell-range series (dotted syntax)
# Features: series.name/values/categories (cell range), marker (style:size:color),
# gridlines, minorGridlines
# ------------------------------------------------------------------
chart("3-Line Charts",
chartType="line",
title="East Region Trend",
x="0", y="0", width="12", height="18",
showMarkers="true", marker="circle:6:2E75B6",
gridlines="D9D9D9:0.5:dot",
minorGridlines="EEEEEE:0.3:dot",
**{"series1.name": "East",
"series1.values": "Sheet1!B2:B13",
"series1.categories": "Sheet1!A2:A13"}),
# ------------------------------------------------------------------
# Chart 2: Smooth line with custom width and no gridlines
# Features: smooth, lineWidth, gridlines=none, series.shadow (color-blur-angle-dist-opacity)
# ------------------------------------------------------------------
chart("3-Line Charts",
chartType="line",
title="Smoothed Sales Trend",
dataRange="Sheet1!A1:E13",
x="13", y="0", width="12", height="18",
smooth="true", lineWidth="2.5",
colors="0070C0,00B050,FFC000,FF0000",
gridlines="none",
**{"series.shadow": "000000-4-315-2-40"}),
# ------------------------------------------------------------------
# Chart 3: Stacked line
# Features: lineStacked, majorTickMark, tickLabelPos
# ------------------------------------------------------------------
chart("3-Line Charts",
chartType="lineStacked",
title="Cumulative Sales",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
catTitle="Month", axisTitle="Cumulative",
majorTickMark="outside", tickLabelPos="low"),
# ------------------------------------------------------------------
# Chart 4: Line with dashed lines, data table, and hidden legend
# Features: lineDash (solid/dot/dash/dashdot/longdash), dataTable, legend=none
# ------------------------------------------------------------------
chart("3-Line Charts",
chartType="line",
title="Trend with Data Table",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
lineDash="dash", lineWidth="1.5",
dataTable="true",
legend="none"),
])
# ======================================================================
# Sheet: 4-Area Charts
# ======================================================================
print("--- 4-Area Charts ---")
doc.batch([
add_sheet("4-Area Charts"),
# ------------------------------------------------------------------
# Chart 1: Area with transparency and gradient fill
# Features: area, transparency (0-100%), gradient (color1-color2:angle)
# ------------------------------------------------------------------
chart("4-Area Charts",
chartType="area",
title="Sales Volume",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="12", height="18",
transparency="40",
gradient="4472C4-BDD7EE:90"),
# ------------------------------------------------------------------
# Chart 2: Stacked area with plotFill and rounded corners
# Features: areaStacked, plotFill, roundedCorners
# ------------------------------------------------------------------
chart("4-Area Charts",
chartType="areaStacked",
title="Stacked Volume",
dataRange="Sheet1!A1:E13",
x="13", y="0", width="12", height="18",
plotFill="F5F5F5",
roundedCorners="true",
transparency="30"),
# ------------------------------------------------------------------
# Chart 3: 100% stacked area with axis control
# Features: areaPercentStacked, axisVisible, axisLine
# ------------------------------------------------------------------
chart("4-Area Charts",
chartType="areaPercentStacked",
title="Regional Mix %",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
transparency="20",
axisVisible="true",
axisLine="999999:0.5:solid"),
# ------------------------------------------------------------------
# Chart 4: 3D area with perspective
# Features: area3d, view3d
# ------------------------------------------------------------------
chart("4-Area Charts",
chartType="area3d",
title="3D Sales Volume",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
view3d="20,25,15",
colors="5B9BD5,A5D5A5,FFD966,F4B183"),
])
# ======================================================================
# Sheet: 5-Styling
# Demonstrates all styling/layout properties on a single column chart
# ======================================================================
print("--- 5-Styling ---")
doc.batch([
add_sheet("5-Styling"),
# ------------------------------------------------------------------
# Chart 1: Fully styled column chart — title, legend, axis, series effects
# Features: title.font/size/color/bold/shadow, legendfont, axisfont,
# series.outline, series.shadow, roundedCorners, referenceLine
# ------------------------------------------------------------------
chart("5-Styling",
chartType="column",
title="Fully Styled Chart",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="14", height="20",
legend="right",
axisfont="9:58626E:Arial",
catTitle="Month", axisTitle="Revenue",
gridlines="CCCCCC:0.5:dot",
plotFill="FAFAFA",
chartFill="FFFFFF",
gapwidth="100",
roundedCorners="true",
referenceLine="160:FF0000:1:dash",
colors="4472C4,ED7D31,70AD47,FFC000",
legendfont="10:444444:Helvetica",
**{"title.font": "Georgia", "title.size": "18",
"title.color": "1F4E79", "title.bold": "true",
"title.shadow": "000000-3-315-2-30",
"series.outline": "FFFFFF-0.5",
"series.shadow": "000000-3-315-2-25"}),
# ------------------------------------------------------------------
# Chart 2: Column with secondary axis (dual Y-axis)
# Features: secondaryAxis (comma-separated 1-based series indices for second Y-axis)
# ------------------------------------------------------------------
chart("5-Styling",
chartType="column",
title="Sales vs Growth Rate",
series1="Sales:120,135,148,162",
series2="Growth:5.2,8.1,12.3,15.6",
categories="Q1,Q2,Q3,Q4",
x="15", y="0", width="10", height="20",
secondaryAxis="2",
colors="4472C4,FF0000"),
# ------------------------------------------------------------------
# Chart 3: Column with individual point colors and inverted negatives
# Features: point{N}.color (per-point coloring), invertIfNeg
# ------------------------------------------------------------------
chart("5-Styling",
chartType="column",
title="Quarterly P&L",
series1="P&L:500,300,-200,800",
categories="Q1,Q2,Q3,Q4",
x="0", y="21", width="10", height="18",
invertIfNeg="true",
dataLabels="true", labelPos="outsideEnd",
**{"point1.color": "70AD47", "point2.color": "70AD47",
"point3.color": "FF0000", "point4.color": "70AD47"}),
# ------------------------------------------------------------------
# Chart 4: Line with gradient plot area and custom data labels
# Features: plotFill gradient (color1-color2:angle), marker styles (diamond),
# dataLabels.numFmt, dataLabel{N}.text (custom text for one label)
# ------------------------------------------------------------------
chart("5-Styling",
chartType="line",
title="Custom Labels Demo",
series1="Revenue:100,200,300,250",
categories="Q1,Q2,Q3,Q4",
x="11", y="21", width="14", height="18",
plotFill="E8F0FE-FFFFFF:90",
showMarkers="true", marker="diamond:8:4472C4",
lineWidth="2",
dataLabels="true", labelPos="top",
**{"dataLabels.numFmt": "#,##0",
"dataLabel3.text": "Peak!"}),
])
# ======================================================================
# Sheet: 6-Layout
# Manual layout of plot area, title, legend; axis orientation; log scale;
# display units; label font and separator; error bars
# ======================================================================
print("--- 6-Layout ---")
doc.batch([
add_sheet("6-Layout"),
# ------------------------------------------------------------------
# Chart 1: Manual layout positioning of plot area, title, legend
# Features: plotArea.x/y/w/h (0-1 fraction), title.x/y, legend.x/y, legend.overlay
# ------------------------------------------------------------------
chart("6-Layout",
chartType="column",
title="Manual Layout",
dataRange="Sheet1!A1:C13",
x="0", y="0", width="12", height="18",
**{"plotArea.x": "0.15", "plotArea.y": "0.15",
"plotArea.w": "0.7", "plotArea.h": "0.7",
"title.x": "0.3", "title.y": "0.01",
"legend.x": "0.02", "legend.y": "0.4",
"legend.overlay": "true"}),
# ------------------------------------------------------------------
# Chart 2: Reversed axis, log scale, display units
# Features: logBase (logarithmic scale), axisOrientation=maxMin (reversed),
# dispUnits (thousands/millions)
# ------------------------------------------------------------------
chart("6-Layout",
chartType="bar",
title="Log Scale + Reversed Axis",
series1="Revenue:10,100,1000,10000",
categories="Startup,Small,Medium,Enterprise",
x="13", y="0", width="12", height="18",
logBase="10",
axisOrientation="maxMin",
dispUnits="thousands"),
# ------------------------------------------------------------------
# Chart 3: Label font, separator, leader lines, and per-label layout
# Features: labelFont (size:color:bold), dataLabels.separator,
# dataLabel{N}.text (custom), dataLabel{N}.delete (hide one label)
# ------------------------------------------------------------------
chart("6-Layout",
chartType="column",
title="Label Formatting",
series1="Sales:120,200,150,180",
categories="Q1,Q2,Q3,Q4",
x="0", y="19", width="12", height="18",
dataLabels="true", labelPos="outsideEnd",
labelFont="11:2E75B6:true",
**{"dataLabels.separator": ": ",
"dataLabel2.text": "Best!",
"dataLabel3.delete": "true"}),
# ------------------------------------------------------------------
# Chart 4: Error bars, minor ticks, opacity
# Features: errBars (percentage/stdDev/fixed), minorTickMark, opacity (0-100%)
# ------------------------------------------------------------------
chart("6-Layout",
chartType="line",
title="Error Bars + Ticks",
series1="Measurement:50,55,48,62,58",
categories="Mon,Tue,Wed,Thu,Fri",
x="13", y="19", width="12", height="18",
showMarkers="true", marker="square:7:4472C4",
errBars="percentage",
majorTickMark="outside", minorTickMark="inside",
opacity="80"),
])
# ======================================================================
# Sheet: 7-Effects
# Gradients, conditional color, area fill, title glow, preset themes
# ======================================================================
print("--- 7-Effects ---")
doc.batch([
add_sheet("7-Effects"),
# ------------------------------------------------------------------
# Chart 1: Per-series gradients
# Features: gradients (per-series, semicolon-separated "C1-C2:angle")
# ------------------------------------------------------------------
chart("7-Effects",
chartType="column",
title="Per-Series Gradients",
series1="East:120,135,148",
series2="West:110,118,130",
categories="Q1,Q2,Q3",
x="0", y="0", width="12", height="18",
gradients="4472C4-BDD7EE:90;ED7D31-FBE5D6:90"),
# ------------------------------------------------------------------
# Chart 2: Area fill gradient and title glow effect
# Features: areafill (area gradient), title.glow (color-radius-opacity)
# ------------------------------------------------------------------
chart("7-Effects",
chartType="area",
title="Glow Title + Area Fill",
dataRange="Sheet1!A1:C13",
x="13", y="0", width="12", height="18",
areafill="4472C4-BDD7EE:90",
transparency="30",
**{"title.glow": "4472C4-8-60", "title.size": "16"}),
# ------------------------------------------------------------------
# Chart 3: Conditional coloring rule
# Features: colorRule (threshold:belowColor:aboveColor — below 60 red, above green)
# ------------------------------------------------------------------
chart("7-Effects",
chartType="column",
title="Conditional Colors",
series1="Score:85,42,91,38,76,55",
categories="A,B,C,D,E,F",
x="0", y="19", width="12", height="18",
colorRule="60:FF0000:70AD47",
dataLabels="true", labelPos="outsideEnd"),
# ------------------------------------------------------------------
# Chart 4: Preset style/theme and leader lines
# Features: style (preset 1-48), dataLabels.showLeaderLines
# ------------------------------------------------------------------
chart("7-Effects",
chartType="column",
title="Preset Style 26",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
style="26",
dataLabels="true",
**{"dataLabels.showLeaderLines": "true"}),
])
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"Generated: {FILE}")
print(" 8 sheets (Sheet1 data + 7 chart sheets, 28 charts total)")
+441
View File
@@ -0,0 +1,441 @@
#!/bin/bash
# Basic Charts Showcase — column, bar, line, and area charts with all variations.
# Generates: charts-basic.xlsx
#
# CLI twin of charts-basic.py (officecli Python SDK). Both produce an
# equivalent charts-basic.xlsx. See charts-basic.md for a guide to each sheet.
#
# Usage: ./charts-basic.sh
# 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")/charts-basic.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Source data — shared across all charts
# ==========================================================================
echo "--- Populating source data ---"
officecli set "$FILE" /Sheet1/A1 --prop text=Month --prop bold=true
officecli set "$FILE" /Sheet1/B1 --prop text=East --prop bold=true
officecli set "$FILE" /Sheet1/C1 --prop text=South --prop bold=true
officecli set "$FILE" /Sheet1/D1 --prop text=North --prop bold=true
officecli set "$FILE" /Sheet1/E1 --prop text=West --prop bold=true
officecli set "$FILE" /Sheet1/A2 --prop text=Jan ; officecli set "$FILE" /Sheet1/B2 --prop text=120 ; officecli set "$FILE" /Sheet1/C2 --prop text=95 ; officecli set "$FILE" /Sheet1/D2 --prop text=88 ; officecli set "$FILE" /Sheet1/E2 --prop text=110
officecli set "$FILE" /Sheet1/A3 --prop text=Feb ; officecli set "$FILE" /Sheet1/B3 --prop text=135 ; officecli set "$FILE" /Sheet1/C3 --prop text=108 ; officecli set "$FILE" /Sheet1/D3 --prop text=92 ; officecli set "$FILE" /Sheet1/E3 --prop text=118
officecli set "$FILE" /Sheet1/A4 --prop text=Mar ; officecli set "$FILE" /Sheet1/B4 --prop text=148 ; officecli set "$FILE" /Sheet1/C4 --prop text=115 ; officecli set "$FILE" /Sheet1/D4 --prop text=105 ; officecli set "$FILE" /Sheet1/E4 --prop text=130
officecli set "$FILE" /Sheet1/A5 --prop text=Apr ; officecli set "$FILE" /Sheet1/B5 --prop text=162 ; officecli set "$FILE" /Sheet1/C5 --prop text=128 ; officecli set "$FILE" /Sheet1/D5 --prop text=118 ; officecli set "$FILE" /Sheet1/E5 --prop text=145
officecli set "$FILE" /Sheet1/A6 --prop text=May ; officecli set "$FILE" /Sheet1/B6 --prop text=155 ; officecli set "$FILE" /Sheet1/C6 --prop text=142 ; officecli set "$FILE" /Sheet1/D6 --prop text=125 ; officecli set "$FILE" /Sheet1/E6 --prop text=138
officecli set "$FILE" /Sheet1/A7 --prop text=Jun ; officecli set "$FILE" /Sheet1/B7 --prop text=178 ; officecli set "$FILE" /Sheet1/C7 --prop text=155 ; officecli set "$FILE" /Sheet1/D7 --prop text=138 ; officecli set "$FILE" /Sheet1/E7 --prop text=162
officecli set "$FILE" /Sheet1/A8 --prop text=Jul ; officecli set "$FILE" /Sheet1/B8 --prop text=195 ; officecli set "$FILE" /Sheet1/C8 --prop text=168 ; officecli set "$FILE" /Sheet1/D8 --prop text=145 ; officecli set "$FILE" /Sheet1/E8 --prop text=175
officecli set "$FILE" /Sheet1/A9 --prop text=Aug ; officecli set "$FILE" /Sheet1/B9 --prop text=210 ; officecli set "$FILE" /Sheet1/C9 --prop text=175 ; officecli set "$FILE" /Sheet1/D9 --prop text=152 ; officecli set "$FILE" /Sheet1/E9 --prop text=190
officecli set "$FILE" /Sheet1/A10 --prop text=Sep ; officecli set "$FILE" /Sheet1/B10 --prop text=188 ; officecli set "$FILE" /Sheet1/C10 --prop text=160 ; officecli set "$FILE" /Sheet1/D10 --prop text=140 ; officecli set "$FILE" /Sheet1/E10 --prop text=170
officecli set "$FILE" /Sheet1/A11 --prop text=Oct ; officecli set "$FILE" /Sheet1/B11 --prop text=172 ; officecli set "$FILE" /Sheet1/C11 --prop text=148 ; officecli set "$FILE" /Sheet1/D11 --prop text=130 ; officecli set "$FILE" /Sheet1/E11 --prop text=155
officecli set "$FILE" /Sheet1/A12 --prop text=Nov ; officecli set "$FILE" /Sheet1/B12 --prop text=165 ; officecli set "$FILE" /Sheet1/C12 --prop text=135 ; officecli set "$FILE" /Sheet1/D12 --prop text=122 ; officecli set "$FILE" /Sheet1/E12 --prop text=148
officecli set "$FILE" /Sheet1/A13 --prop text=Dec ; officecli set "$FILE" /Sheet1/B13 --prop text=198 ; officecli set "$FILE" /Sheet1/C13 --prop text=158 ; officecli set "$FILE" /Sheet1/D13 --prop text=142 ; officecli set "$FILE" /Sheet1/E13 --prop text=180
# ==========================================================================
# Sheet: 1-Column Charts
# ==========================================================================
echo "--- 1-Column Charts ---"
officecli add "$FILE" / --type sheet --prop name="1-Column Charts"
# Chart 1: Basic clustered column from cell range with axis titles
# Features: chartType=column, dataRange, catTitle, axisTitle, axisfont, gridlines
officecli add "$FILE" "/1-Column Charts" --type chart \
--prop chartType=column \
--prop title="Regional Sales by Month" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop catTitle=Month --prop axisTitle=Sales \
--prop axisfont=9:58626E:Arial \
--prop gridlines=D9D9D9:0.5:dot
# Chart 2: Stacked column with custom colors, data labels, and gap control
# Features: columnStacked, colors, dataLabels, labelPos, gapwidth, series.outline
officecli add "$FILE" "/1-Column Charts" --type chart \
--prop chartType=columnStacked \
--prop title="Stacked Regional Sales" \
--prop dataRange=Sheet1!A1:E13 \
--prop colors=2E75B6,70AD47,FFC000,C00000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=center \
--prop gapwidth=60 \
--prop series.outline=FFFFFF-0.5
# Chart 3: 100% stacked column with legend position and plotFill
# Features: columnPercentStacked, legend=bottom, legendfont, plotFill
officecli add "$FILE" "/1-Column Charts" --type chart \
--prop chartType=columnPercentStacked \
--prop title="Market Share by Month" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop legendfont=9:8B949E \
--prop plotFill=F5F5F5
# Chart 4: 3D column with perspective and title styling
# Features: column3d, view3d (rotX,rotY,perspective), title.font/size/color/bold
officecli add "$FILE" "/1-Column Charts" --type chart \
--prop chartType=column3d \
--prop title="3D Regional Sales" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=15,20,30 \
--prop title.font=Calibri --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true
# ==========================================================================
# Sheet: 2-Bar Charts
# ==========================================================================
echo "--- 2-Bar Charts ---"
officecli add "$FILE" / --type sheet --prop name="2-Bar Charts"
# Chart 1: Horizontal bar with inline data and gapwidth
# Features: bar, inline data (Name:v1;Name2:v2), gapwidth, labelPos=outsideEnd
officecli add "$FILE" "/2-Bar Charts" --type chart \
--prop chartType=bar \
--prop title="Q4 Sales by Region" \
--prop 'data=East:198;South:158;North:142;West:180' \
--prop categories=East,South,North,West \
--prop colors=2E75B6,70AD47,FFC000,C00000 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop gapwidth=80 \
--prop dataLabels=true --prop labelPos=outsideEnd
# Chart 2: Stacked bar with named series and overlap
# Features: barStacked, named series (series1=Name:v1,v2), overlap
officecli add "$FILE" "/2-Bar Charts" --type chart \
--prop chartType=barStacked \
--prop title="H1 vs H2 Sales" \
--prop series1=H1:663,598,528,661 \
--prop series2=H2:833,718,669,868 \
--prop categories=East,South,North,West \
--prop colors=4472C4,ED7D31 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=center \
--prop gapwidth=50 --prop overlap=0
# Chart 3: 100% stacked bar with reference line
# Note: on a barPercentStacked chart, the value axis is 0-1 (displayed as 0%-100%),
# so a 50% reference line must be written as 0.5 — not 50.
# referenceLine supports: value | value:color | value:color:label
# | value:color:width:dash | value:color:label:dash (legacy)
# | value:color:width:dash:label (canonical). Width is in points; default 1.5pt.
# Features: barPercentStacked, referenceLine, axisLine, catAxisLine
officecli add "$FILE" "/2-Bar Charts" --type chart \
--prop chartType=barPercentStacked \
--prop title="Regional Contribution %" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop referenceLine=0.5:FF0000:Target:dash \
--prop axisLine=333333:1:solid \
--prop catAxisLine=333333:1:solid
# Chart 4: 3D bar with chart area fill and display units
# Features: bar3d, chartFill (chart area background), style/styleId (preset 1-48)
officecli add "$FILE" "/2-Bar Charts" --type chart \
--prop chartType=bar3d \
--prop title="3D Regional Comparison" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=10,30,20 \
--prop chartFill=F2F2F2 \
--prop style=3
# ==========================================================================
# Sheet: 3-Line Charts
# ==========================================================================
echo "--- 3-Line Charts ---"
officecli add "$FILE" / --type sheet --prop name="3-Line Charts"
# Chart 1: Line with markers and cell-range series (dotted syntax)
# Features: series.name/values/categories (cell range), marker (style:size:color),
# gridlines, minorGridlines
officecli add "$FILE" "/3-Line Charts" --type chart \
--prop chartType=line \
--prop title="East Region Trend" \
--prop series1.name=East \
--prop series1.values=Sheet1!B2:B13 \
--prop series1.categories=Sheet1!A2:A13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop showMarkers=true --prop marker=circle:6:2E75B6 \
--prop gridlines=D9D9D9:0.5:dot \
--prop minorGridlines=EEEEEE:0.3:dot
# Chart 2: Smooth line with custom width and no gridlines
# Features: smooth, lineWidth, gridlines=none, series.shadow (color-blur-angle-dist-opacity)
officecli add "$FILE" "/3-Line Charts" --type chart \
--prop chartType=line \
--prop title="Smoothed Sales Trend" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop smooth=true --prop lineWidth=2.5 \
--prop colors=0070C0,00B050,FFC000,FF0000 \
--prop gridlines=none \
--prop series.shadow=000000-4-315-2-40
# Chart 3: Stacked line
# Features: lineStacked, majorTickMark, tickLabelPos
officecli add "$FILE" "/3-Line Charts" --type chart \
--prop chartType=lineStacked \
--prop title="Cumulative Sales" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop catTitle=Month --prop axisTitle=Cumulative \
--prop majorTickMark=outside --prop tickLabelPos=low
# Chart 4: Line with dashed lines, data table, and hidden legend
# Features: lineDash (solid/dot/dash/dashdot/longdash), dataTable, legend=none
officecli add "$FILE" "/3-Line Charts" --type chart \
--prop chartType=line \
--prop title="Trend with Data Table" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop lineDash=dash --prop lineWidth=1.5 \
--prop dataTable=true \
--prop legend=none
# ==========================================================================
# Sheet: 4-Area Charts
# ==========================================================================
echo "--- 4-Area Charts ---"
officecli add "$FILE" / --type sheet --prop name="4-Area Charts"
# Chart 1: Area with transparency and gradient fill
# Features: area, transparency (0-100%), gradient (color1-color2:angle)
officecli add "$FILE" "/4-Area Charts" --type chart \
--prop chartType=area \
--prop title="Sales Volume" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop transparency=40 \
--prop gradient=4472C4-BDD7EE:90
# Chart 2: Stacked area with plotFill and rounded corners
# Features: areaStacked, plotFill, roundedCorners
officecli add "$FILE" "/4-Area Charts" --type chart \
--prop chartType=areaStacked \
--prop title="Stacked Volume" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop plotFill=F5F5F5 \
--prop roundedCorners=true \
--prop transparency=30
# Chart 3: 100% stacked area with axis control
# Features: areaPercentStacked, axisVisible, axisLine
officecli add "$FILE" "/4-Area Charts" --type chart \
--prop chartType=areaPercentStacked \
--prop title="Regional Mix %" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop transparency=20 \
--prop axisVisible=true \
--prop axisLine=999999:0.5:solid
# Chart 4: 3D area with perspective
# Features: area3d, view3d
officecli add "$FILE" "/4-Area Charts" --type chart \
--prop chartType=area3d \
--prop title="3D Sales Volume" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=20,25,15 \
--prop colors=5B9BD5,A5D5A5,FFD966,F4B183
# ==========================================================================
# Sheet: 5-Styling
# Demonstrates all styling/layout properties on a single column chart
# ==========================================================================
echo "--- 5-Styling ---"
officecli add "$FILE" / --type sheet --prop name="5-Styling"
# Chart 1: Fully styled column chart — title, legend, axis, series effects
# Features: title.font/size/color/bold/shadow, legendfont, axisfont,
# series.outline, series.shadow, roundedCorners, referenceLine
officecli add "$FILE" "/5-Styling" --type chart \
--prop chartType=column \
--prop title="Fully Styled Chart" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=14 --prop height=20 \
--prop title.font=Georgia --prop title.size=18 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30 \
--prop legendfont=10:444444:Helvetica \
--prop legend=right \
--prop axisfont=9:58626E:Arial \
--prop catTitle=Month --prop axisTitle=Revenue \
--prop gridlines=CCCCCC:0.5:dot \
--prop plotFill=FAFAFA \
--prop chartFill=FFFFFF \
--prop series.outline=FFFFFF-0.5 \
--prop series.shadow=000000-3-315-2-25 \
--prop gapwidth=100 \
--prop roundedCorners=true \
--prop referenceLine=160:FF0000:1:dash \
--prop colors=4472C4,ED7D31,70AD47,FFC000
# Chart 2: Column with secondary axis (dual Y-axis)
# Features: secondaryAxis (comma-separated 1-based series indices for second Y-axis)
officecli add "$FILE" "/5-Styling" --type chart \
--prop chartType=column \
--prop title="Sales vs Growth Rate" \
--prop series1=Sales:120,135,148,162 \
--prop series2=Growth:5.2,8.1,12.3,15.6 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=15 --prop y=0 --prop width=10 --prop height=20 \
--prop secondaryAxis=2 \
--prop colors=4472C4,FF0000
# Chart 3: Column with individual point colors and inverted negatives
# Features: point{N}.color (per-point coloring), invertIfNeg
officecli add "$FILE" "/5-Styling" --type chart \
--prop chartType=column \
--prop title="Quarterly P&L" \
--prop 'series1=P&L:500,300,-200,800' \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=0 --prop y=21 --prop width=10 --prop height=18 \
--prop point1.color=70AD47 --prop point2.color=70AD47 \
--prop point3.color=FF0000 --prop point4.color=70AD47 \
--prop invertIfNeg=true \
--prop dataLabels=true --prop labelPos=outsideEnd
# Chart 4: Line with gradient plot area and custom data labels
# Features: plotFill gradient (color1-color2:angle), marker styles (diamond),
# dataLabels.numFmt, dataLabel{N}.text (custom text for one label)
officecli add "$FILE" "/5-Styling" --type chart \
--prop chartType=line \
--prop title="Custom Labels Demo" \
--prop series1=Revenue:100,200,300,250 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=11 --prop y=21 --prop width=14 --prop height=18 \
--prop plotFill=E8F0FE-FFFFFF:90 \
--prop showMarkers=true --prop marker=diamond:8:4472C4 \
--prop lineWidth=2 \
--prop dataLabels=true --prop labelPos=top \
--prop dataLabels.numFmt=#,##0 \
--prop dataLabel3.text=Peak!
# ==========================================================================
# Sheet: 6-Layout
# Manual layout of plot area, title, legend; axis orientation; log scale;
# display units; label font and separator; error bars
# ==========================================================================
echo "--- 6-Layout ---"
officecli add "$FILE" / --type sheet --prop name="6-Layout"
# Chart 1: Manual layout positioning of plot area, title, legend
# Features: plotArea.x/y/w/h (0-1 fraction), title.x/y, legend.x/y, legend.overlay
officecli add "$FILE" "/6-Layout" --type chart \
--prop chartType=column \
--prop title="Manual Layout" \
--prop dataRange=Sheet1!A1:C13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop plotArea.x=0.15 --prop plotArea.y=0.15 \
--prop plotArea.w=0.7 --prop plotArea.h=0.7 \
--prop title.x=0.3 --prop title.y=0.01 \
--prop legend.x=0.02 --prop legend.y=0.4 \
--prop legend.overlay=true
# Chart 2: Reversed axis, log scale, display units
# Features: logBase (logarithmic scale), axisOrientation=maxMin (reversed),
# dispUnits (thousands/millions)
officecli add "$FILE" "/6-Layout" --type chart \
--prop chartType=bar \
--prop title="Log Scale + Reversed Axis" \
--prop series1=Revenue:10,100,1000,10000 \
--prop categories=Startup,Small,Medium,Enterprise \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop logBase=10 \
--prop axisOrientation=maxMin \
--prop dispUnits=thousands
# Chart 3: Label font, separator, leader lines, and per-label layout
# Features: labelFont (size:color:bold), dataLabels.separator,
# dataLabel{N}.text (custom), dataLabel{N}.delete (hide one label)
officecli add "$FILE" "/6-Layout" --type chart \
--prop chartType=column \
--prop title="Label Formatting" \
--prop series1=Sales:120,200,150,180 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop labelFont=11:2E75B6:true \
--prop 'dataLabels.separator=: ' \
--prop dataLabel2.text=Best! \
--prop dataLabel3.delete=true
# Chart 4: Error bars, minor ticks, opacity
# Features: errBars (percentage/stdDev/fixed), minorTickMark, opacity (0-100%)
officecli add "$FILE" "/6-Layout" --type chart \
--prop chartType=line \
--prop title="Error Bars + Ticks" \
--prop series1=Measurement:50,55,48,62,58 \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop showMarkers=true --prop marker=square:7:4472C4 \
--prop errBars=percentage \
--prop majorTickMark=outside --prop minorTickMark=inside \
--prop opacity=80
# ==========================================================================
# Sheet: 7-Effects
# Gradients, conditional color, area fill, title glow, preset themes
# ==========================================================================
echo "--- 7-Effects ---"
officecli add "$FILE" / --type sheet --prop name="7-Effects"
# Chart 1: Per-series gradients
# Features: gradients (per-series, semicolon-separated "C1-C2:angle")
officecli add "$FILE" "/7-Effects" --type chart \
--prop chartType=column \
--prop title="Per-Series Gradients" \
--prop series1=East:120,135,148 \
--prop series2=West:110,118,130 \
--prop categories=Q1,Q2,Q3 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90'
# Chart 2: Area fill gradient and title glow effect
# Features: areafill (area gradient), title.glow (color-radius-opacity)
officecli add "$FILE" "/7-Effects" --type chart \
--prop chartType=area \
--prop title="Glow Title + Area Fill" \
--prop dataRange=Sheet1!A1:C13 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop areafill=4472C4-BDD7EE:90 \
--prop transparency=30 \
--prop title.glow=4472C4-8-60 \
--prop title.size=16
# Chart 3: Conditional coloring rule
# Features: colorRule (threshold:belowColor:aboveColor — values below 60 red, above green)
officecli add "$FILE" "/7-Effects" --type chart \
--prop chartType=column \
--prop title="Conditional Colors" \
--prop series1=Score:85,42,91,38,76,55 \
--prop categories=A,B,C,D,E,F \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colorRule=60:FF0000:70AD47 \
--prop dataLabels=true --prop labelPos=outsideEnd
# Chart 4: Preset style/theme and leader lines
# Features: style (preset 1-48), dataLabels.showLeaderLines
officecli add "$FILE" "/7-Effects" --type chart \
--prop chartType=column \
--prop title="Preset Style 26" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop style=26 \
--prop dataLabels=true \
--prop dataLabels.showLeaderLines=true
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
echo " 8 sheets (Sheet1 data + 7 chart sheets, 28 charts total)"
Binary file not shown.
+181
View File
@@ -0,0 +1,181 @@
# Box-Whisker Chart Showcase
This demo consists of three files that work together:
- **charts-boxwhisker.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-boxwhisker.xlsx** — The generated workbook with 2 sheets (8 box-whisker charts total).
- **charts-boxwhisker.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-boxwhisker.py
# → charts-boxwhisker.xlsx
```
## Chart Sheets
### Sheet: 1-Basics & Quartile
Four box-whisker charts covering basic usage, quartile methods, title styling, and series colors.
```bash
# Chart 1: Single series, exclusive quartile, data labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Test Score Distribution" \
--prop series1="Scores:45,52,58,61,63,65,67,68,70,72,75,78,82,85,90,95,99" \
--prop quartileMethod=exclusive \
--prop dataLabels=true
# Chart 2: Three-series comparison, inclusive quartile, legend
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Salary by Department ($k)" \
--prop series1="Engineering:85,92,95,98,102,105,108,112,118,125,135,150,180" \
--prop series2="Marketing:60,65,68,72,75,78,80,83,88,92,98,110" \
--prop series3="Sales:55,62,68,75,82,90,98,105,115,125,140,160,190" \
--prop quartileMethod=inclusive \
--prop legend=bottom
# Chart 3: Title styling — color, size, bold, font, shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Styled Title Demo" \
--prop title.color=1B2838 --prop title.size=20 \
--prop title.bold=true --prop title.font=Georgia \
--prop title.shadow=000000-6-45-3-50 \
--prop series1="Data:18,22,25,28,30,32,35,38,40,42,45,55,62,78"
# Chart 4: Per-series colors and drop shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Custom Series Colors" \
--prop series1="GroupA:30,38,45,52,58,62,65,68,71,74,78,85,92" \
--prop series2="GroupB:20,28,35,40,48,55,60,66,70,80,88,95,110" \
--prop colors=5B9BD5,ED7D31 \
--prop series.shadow=000000-6-45-3-35
```
**Features:** `quartileMethod=exclusive`, `quartileMethod=inclusive`, `dataLabels`, `legend=bottom`, multi-series (3), `title.color`, `title.size`, `title.bold`, `title.font`, `title.shadow`, `colors` (per-series), `series.shadow`
### Sheet: 2-Axes & Styling
Four box-whisker charts covering axis control, gridlines, area fills, and a full presentation-grade chart.
```bash
# Chart 5: Axis scaling, axis titles, axis title styling, axis font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Response Time (ms)" \
--prop series1="API:12,18,22,25,28,30,32,35,38,40,42,45,55,62,78,95,120" \
--prop series2="DB:5,8,10,12,14,16,18,20,22,25,28,32,38,45,60" \
--prop axismin=0 --prop axismax=130 \
--prop majorunit=10 --prop minorunit=5 \
--prop xAxisTitle="Service" --prop yAxisTitle="Latency (ms)" \
--prop axisTitle.color=4A5568 --prop axisTitle.size=12 \
--prop axisTitle.bold=true --prop axisTitle.font="Helvetica Neue" \
--prop "axisfont=10:6B7280:Consolas"
# Chart 6: Axis visibility, axis lines, gridlines, cross-axis gridlines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Axis & Gridline Control" \
--prop series1="Temp:15,18,20,22,24,26,28,30,32,35,38,40,42" \
--prop cataxis.visible=false \
--prop "valaxis.line=334155:1.5" \
--prop gridlines=true --prop gridlineColor=E2E8F0 \
--prop xGridlines=true --prop xGridlineColor=F1F5F9
# Chart 7: Card style — area fills/borders, gapWidth, no tick labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Card Style" \
--prop series1="Weight:50,55,58,60,62,64,66,68,70,72,75,78,82,88,95" \
--prop fill=6366F1 \
--prop gapWidth=200 \
--prop tickLabels=false --prop gridlines=false \
--prop plotareafill=F8FAFC --prop "plotarea.border=E2E8F0:1" \
--prop chartareafill=FFFFFF --prop "chartarea.border=CBD5E1:0.75"
# Chart 8: Full presentation-grade — all properties combined
officecli add data.xlsx /Sheet --type chart \
--prop chartType=boxWhisker \
--prop title="Server Latency Dashboard" \
--prop title.color=0F172A --prop title.size=18 \
--prop title.bold=true --prop title.font="Helvetica Neue" \
--prop title.shadow=000000-4-45-2-40 \
--prop series1="US-East:8,12,15,18,20,22,24,26,28,30,35,42,55,70,95" \
--prop series2="EU-West:10,14,18,22,25,28,30,33,36,40,45,50,60,80" \
--prop series3="AP-South:15,20,25,30,35,38,42,45,48,52,58,65,75,90,120" \
--prop quartileMethod=exclusive \
--prop colors=3B82F6,10B981,F59E0B \
--prop series.shadow=000000-4-45-2-30 \
--prop axismin=0 --prop axismax=130 --prop majorunit=10 \
--prop xAxisTitle="Region" --prop yAxisTitle="Latency (ms)" \
--prop axisTitle.color=475569 --prop axisTitle.size=11 \
--prop axisTitle.bold=true --prop axisTitle.font="Helvetica Neue" \
--prop "axisfont=9:64748B:Helvetica Neue" \
--prop "axisline=CBD5E1:1" \
--prop gridlineColor=E2E8F0 \
--prop dataLabels=true --prop "datalabels.numfmt=0" \
--prop legend=top --prop legend.overlay=false \
--prop "legendfont=10:475569:Helvetica Neue" \
--prop plotareafill=F8FAFC --prop "plotarea.border=E2E8F0:0.75" \
--prop chartareafill=FFFFFF --prop "chartarea.border=CBD5E1:0.75"
```
**Features:** `axismin`, `axismax`, `majorunit`, `minorunit`, `xAxisTitle`, `yAxisTitle`, `axisTitle.color`, `axisTitle.size`, `axisTitle.bold`, `axisTitle.font`, `axisfont`, `cataxis.visible`, `valaxis.line`, `gridlines`, `gridlineColor`, `xGridlines`, `xGridlineColor`, `fill` (single color), `gapWidth`, `tickLabels`, `plotareafill`, `plotarea.border`, `chartareafill`, `chartarea.border`, `axisline`, `datalabels.numfmt`, `legend.overlay`, `legendfont`
## Property Coverage
| Property | Chart |
|---|---|
| `chartType=boxWhisker` | 1-8 |
| `quartileMethod=exclusive` | 1, 8 |
| `quartileMethod=inclusive` | 2 |
| `dataLabels` | 1, 8 |
| `datalabels.numfmt` | 8 |
| `legend=bottom` | 2 |
| `legend=top` | 8 |
| `legend.overlay` | 8 |
| `legendfont` | 8 |
| `title.color` | 3, 8 |
| `title.size` | 3, 8 |
| `title.bold` | 3, 8 |
| `title.font` | 3, 8 |
| `title.shadow` | 3, 8 |
| `fill` (single color) | 7 |
| `colors` (per-series) | 4, 8 |
| `series.shadow` | 4, 8 |
| `axismin` / `axismax` | 5, 8 |
| `majorunit` | 5, 8 |
| `minorunit` | 5 |
| `xAxisTitle` | 5, 8 |
| `yAxisTitle` | 5, 8 |
| `axisTitle.color` | 5, 8 |
| `axisTitle.size` | 5, 8 |
| `axisTitle.bold` | 5, 8 |
| `axisTitle.font` | 5, 8 |
| `axisfont` | 5, 8 |
| `cataxis.visible` | 6 |
| `valaxis.line` | 6 |
| `axisline` | 8 |
| `gridlines` | 6, 7 |
| `gridlineColor` | 6, 8 |
| `xGridlines` | 6 |
| `xGridlineColor` | 6 |
| `tickLabels` | 7 |
| `gapWidth` | 7 |
| `plotareafill` | 7, 8 |
| `plotarea.border` | 7, 8 |
| `chartareafill` | 7, 8 |
| `chartarea.border` | 7, 8 |
## Inspect the Generated File
```bash
officecli query charts-boxwhisker.xlsx chart
officecli get charts-boxwhisker.xlsx "/1-Basics & Quartile/chart[1]"
```
+230
View File
@@ -0,0 +1,230 @@
#!/usr/bin/env python3
"""
Box-Whisker Chart Showcase — generates charts-boxwhisker.xlsx exercising the
full xlsx `boxWhisker` chart property surface across 8 charts on 2 sheets:
quartile methods, multi-series, title/series/axis styling, axis scaling and
titles, axis/gridline control, plot/chart-area fills and borders, data labels,
legend, and a full presentation-grade combination.
SDK twin of charts-boxwhisker.sh (officecli CLI). Both produce an equivalent
charts-boxwhisker.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in a single `doc.batch(...)` round-trip.
Each item is the same `{"command","parent","type","props"}` dict you'd put in
an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-boxwhisker.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-boxwhisker.xlsx")
def sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape (parent = sheet path)."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet 1: Basics & Quartile Methods
# ======================================================================
s1 = "/1-Basics & Quartile"
sheet1_items = [
sheet("1-Basics & Quartile"),
# ------------------------------------------------------------------
# Chart 1: Basic single-series with exclusive quartile and data labels
# Features: single series, quartileMethod=exclusive, dataLabels
# ------------------------------------------------------------------
chart(s1,
chartType="boxWhisker",
title="Test Score Distribution",
series1="Scores:45,52,58,61,63,65,67,68,70,72,75,78,82,85,90,95,99",
quartileMethod="exclusive",
dataLabels="true",
x="0", y="0", width="13", height="18"),
# ------------------------------------------------------------------
# Chart 2: Multi-series with inclusive quartile, legend at bottom
# Features: 3 series, quartileMethod=inclusive, legend=bottom
# ------------------------------------------------------------------
chart(s1,
chartType="boxWhisker",
title="Salary by Department ($k)",
series1="Engineering:85,92,95,98,102,105,108,112,118,125,135,150,180",
series2="Marketing:60,65,68,72,75,78,80,83,88,92,98,110",
series3="Sales:55,62,68,75,82,90,98,105,115,125,140,160,190",
quartileMethod="inclusive",
legend="bottom",
x="14", y="0", width="13", height="18"),
# ------------------------------------------------------------------
# Chart 3: Title styling — color, size, bold, font, shadow
# Features: title.color, title.size, title.bold, title.font, title.shadow
# ------------------------------------------------------------------
chart(s1,
chartType="boxWhisker",
title="Styled Title Demo",
**{"title.color": "1B2838",
"title.size": "20",
"title.bold": "true",
"title.font": "Georgia",
"title.shadow": "000000-6-45-3-50"},
series1="Data:18,22,25,28,30,32,35,38,40,42,45,55,62,78",
x="0", y="19", width="13", height="18"),
# ------------------------------------------------------------------
# Chart 4: Series colors — fill, colors (per-series), series.shadow
# Features: colors (per-series hex), series.shadow
# ------------------------------------------------------------------
chart(s1,
chartType="boxWhisker",
title="Custom Series Colors",
series1="GroupA:30,38,45,52,58,62,65,68,71,74,78,85,92",
series2="GroupB:20,28,35,40,48,55,60,66,70,80,88,95,110",
colors="5B9BD5,ED7D31",
**{"series.shadow": "000000-6-45-3-35"},
x="14", y="19", width="13", height="18"),
]
doc.batch(sheet1_items)
print(f" Sheet 1: Basics & Quartile — {len(sheet1_items) - 1} charts")
# ======================================================================
# Sheet 2: Axes & Styling
# ======================================================================
s2 = "/2-Axes & Styling"
sheet2_items = [
sheet("2-Axes & Styling"),
# ------------------------------------------------------------------
# Chart 5: Axis scaling + axis titles + axis title styling + axis font
# Features: axismin, axismax, majorunit, minorunit, xAxisTitle,
# yAxisTitle, axisTitle.color/.size/.bold/.font, axisfont
# ------------------------------------------------------------------
chart(s2,
chartType="boxWhisker",
title="Response Time (ms)",
series1="API:12,18,22,25,28,30,32,35,38,40,42,45,55,62,78,95,120",
series2="DB:5,8,10,12,14,16,18,20,22,25,28,32,38,45,60",
axismin="0", axismax="130", majorunit="10", minorunit="5",
xAxisTitle="Service",
yAxisTitle="Latency (ms)",
**{"axisTitle.color": "4A5568",
"axisTitle.size": "12",
"axisTitle.bold": "true",
"axisTitle.font": "Helvetica Neue"},
axisfont="10:6B7280:Consolas",
x="0", y="0", width="13", height="18"),
# ------------------------------------------------------------------
# Chart 6: Axis visibility + axis lines + gridlines + xGridlines
# Features: cataxis.visible=false, valaxis.line, gridlines,
# gridlineColor, xGridlines, xGridlineColor
# ------------------------------------------------------------------
chart(s2,
chartType="boxWhisker",
title="Axis & Gridline Control",
series1="Temp:15,18,20,22,24,26,28,30,32,35,38,40,42",
**{"cataxis.visible": "false",
"valaxis.line": "334155:1.5"},
gridlines="true",
gridlineColor="E2E8F0",
xGridlines="true",
xGridlineColor="F1F5F9",
x="14", y="0", width="13", height="18"),
# ------------------------------------------------------------------
# Chart 7: Plot/chart area fills, borders, gapWidth, tickLabels=false
# Features: fill (single color), gapWidth, tickLabels=false,
# gridlines=false, plotareafill, plotarea.border, chartareafill,
# chartarea.border
# ------------------------------------------------------------------
chart(s2,
chartType="boxWhisker",
title="Card Style",
series1="Weight:50,55,58,60,62,64,66,68,70,72,75,78,82,88,95",
fill="6366F1",
gapWidth="200",
tickLabels="false",
gridlines="false",
plotareafill="F8FAFC",
chartareafill="FFFFFF",
**{"plotarea.border": "E2E8F0:1",
"chartarea.border": "CBD5E1:0.75"},
x="0", y="19", width="13", height="18"),
# ------------------------------------------------------------------
# Chart 8: Full presentation-grade — everything combined
# Features: ALL properties combined — title styling, multi-series
# colors, series.shadow, axis scaling, axis titles + styling,
# axisfont, axisline, gridlineColor, dataLabels + numfmt, legend +
# overlay + legendfont, plot/chart area fill + border
# ------------------------------------------------------------------
chart(s2,
chartType="boxWhisker",
title="Server Latency Dashboard",
**{"title.color": "0F172A",
"title.size": "18",
"title.bold": "true",
"title.font": "Helvetica Neue",
"title.shadow": "000000-4-45-2-40"},
series1="US-East:8,12,15,18,20,22,24,26,28,30,35,42,55,70,95",
series2="EU-West:10,14,18,22,25,28,30,33,36,40,45,50,60,80",
series3="AP-South:15,20,25,30,35,38,42,45,48,52,58,65,75,90,120",
quartileMethod="exclusive",
colors="3B82F6,10B981,F59E0B",
axismin="0", axismax="130", majorunit="10",
xAxisTitle="Region",
yAxisTitle="Latency (ms)",
axisfont="9:64748B:Helvetica Neue",
axisline="CBD5E1:1",
gridlineColor="E2E8F0",
dataLabels="true",
legend="top",
legendfont="10:475569:Helvetica Neue",
plotareafill="F8FAFC",
chartareafill="FFFFFF",
**{"series.shadow": "000000-4-45-2-30",
"axisTitle.color": "475569",
"axisTitle.size": "11",
"axisTitle.bold": "true",
"axisTitle.font": "Helvetica Neue",
"datalabels.numfmt": "0",
"legend.overlay": "false",
"plotarea.border": "E2E8F0:0.75",
"chartarea.border": "CBD5E1:0.75"},
x="14", y="19", width="16", height="22"),
]
doc.batch(sheet2_items)
print(f" Sheet 2: Axes & Styling — {len(sheet2_items) - 1} charts")
# Remove blank default Sheet1
doc.send({"command": "remove", "path": "/Sheet1"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nGenerated: {FILE}")
print(" 2 sheets (8 charts total)")
print(" Sheet 1: Basics & Quartile Methods (4 charts)")
print(" Sheet 2: Axes & Styling (4 charts)")
+183
View File
@@ -0,0 +1,183 @@
#!/bin/bash
# Box-Whisker Chart Showcase — generates charts-boxwhisker.xlsx exercising the
# full xlsx boxWhisker chart property surface across 8 charts on 2 sheets.
# CLI twin of charts-boxwhisker.py (officecli Python SDK).
# Usage: ./charts-boxwhisker.sh
# 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")/charts-boxwhisker.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet 1: Basics & Quartile Methods
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Basics & Quartile"
# --------------------------------------------------------------------------
# Chart 1: Basic single-series with exclusive quartile and data labels
# Features: single series, quartileMethod=exclusive, dataLabels
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Basics & Quartile" --type chart \
--prop chartType=boxWhisker \
--prop title="Test Score Distribution" \
--prop "series1=Scores:45,52,58,61,63,65,67,68,70,72,75,78,82,85,90,95,99" \
--prop quartileMethod=exclusive \
--prop dataLabels=true \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# --------------------------------------------------------------------------
# Chart 2: Multi-series with inclusive quartile, legend at bottom
# Features: 3 series, quartileMethod=inclusive, legend=bottom
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Basics & Quartile" --type chart \
--prop chartType=boxWhisker \
--prop title="Salary by Department (\$k)" \
--prop "series1=Engineering:85,92,95,98,102,105,108,112,118,125,135,150,180" \
--prop "series2=Marketing:60,65,68,72,75,78,80,83,88,92,98,110" \
--prop "series3=Sales:55,62,68,75,82,90,98,105,115,125,140,160,190" \
--prop quartileMethod=inclusive \
--prop legend=bottom \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# --------------------------------------------------------------------------
# Chart 3: Title styling — color, size, bold, font, shadow
# Features: title.color, title.size, title.bold, title.font, title.shadow
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Basics & Quartile" --type chart \
--prop chartType=boxWhisker \
--prop title="Styled Title Demo" \
--prop title.color=1B2838 \
--prop title.size=20 \
--prop title.bold=true \
--prop title.font=Georgia \
--prop title.shadow=000000-6-45-3-50 \
--prop "series1=Data:18,22,25,28,30,32,35,38,40,42,45,55,62,78" \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# --------------------------------------------------------------------------
# Chart 4: Series colors — fill, colors (per-series), series.shadow
# Features: colors (per-series hex), series.shadow
# --------------------------------------------------------------------------
officecli add "$FILE" "/1-Basics & Quartile" --type chart \
--prop chartType=boxWhisker \
--prop title="Custom Series Colors" \
--prop "series1=GroupA:30,38,45,52,58,62,65,68,71,74,78,85,92" \
--prop "series2=GroupB:20,28,35,40,48,55,60,66,70,80,88,95,110" \
--prop colors=5B9BD5,ED7D31 \
--prop series.shadow=000000-6-45-3-35 \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 2: Axes & Styling
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Axes & Styling"
# --------------------------------------------------------------------------
# Chart 5: Axis scaling + axis titles + axis title styling + axis font
# Features: axismin, axismax, majorunit, minorunit, xAxisTitle, yAxisTitle,
# axisTitle.color/.size/.bold/.font, axisfont
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Axes & Styling" --type chart \
--prop chartType=boxWhisker \
--prop title="Response Time (ms)" \
--prop "series1=API:12,18,22,25,28,30,32,35,38,40,42,45,55,62,78,95,120" \
--prop "series2=DB:5,8,10,12,14,16,18,20,22,25,28,32,38,45,60" \
--prop axismin=0 --prop axismax=130 --prop majorunit=10 --prop minorunit=5 \
--prop xAxisTitle=Service \
--prop yAxisTitle="Latency (ms)" \
--prop axisTitle.color=4A5568 \
--prop axisTitle.size=12 \
--prop axisTitle.bold=true \
--prop axisTitle.font="Helvetica Neue" \
--prop "axisfont=10:6B7280:Consolas" \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# --------------------------------------------------------------------------
# Chart 6: Axis visibility + axis lines + gridlines + xGridlines
# Features: cataxis.visible=false, valaxis.line, gridlines, gridlineColor,
# xGridlines, xGridlineColor
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Axes & Styling" --type chart \
--prop chartType=boxWhisker \
--prop title="Axis & Gridline Control" \
--prop "series1=Temp:15,18,20,22,24,26,28,30,32,35,38,40,42" \
--prop cataxis.visible=false \
--prop "valaxis.line=334155:1.5" \
--prop gridlines=true \
--prop gridlineColor=E2E8F0 \
--prop xGridlines=true \
--prop xGridlineColor=F1F5F9 \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# --------------------------------------------------------------------------
# Chart 7: Plot/chart area fills, borders, gapWidth, tickLabels=false
# Features: fill (single color), gapWidth, tickLabels=false, gridlines=false,
# plotareafill, plotarea.border, chartareafill, chartarea.border
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Axes & Styling" --type chart \
--prop chartType=boxWhisker \
--prop title="Card Style" \
--prop "series1=Weight:50,55,58,60,62,64,66,68,70,72,75,78,82,88,95" \
--prop fill=6366F1 \
--prop gapWidth=200 \
--prop tickLabels=false \
--prop gridlines=false \
--prop plotareafill=F8FAFC \
--prop "plotarea.border=E2E8F0:1" \
--prop chartareafill=FFFFFF \
--prop "chartarea.border=CBD5E1:0.75" \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# --------------------------------------------------------------------------
# Chart 8: Full presentation-grade — everything combined
# Features: ALL properties combined — title styling, multi-series colors,
# series.shadow, axis scaling, axis titles + styling, axisfont, axisline,
# gridlineColor, dataLabels + numfmt, legend + overlay + legendfont,
# plot/chart area fill + border
# --------------------------------------------------------------------------
officecli add "$FILE" "/2-Axes & Styling" --type chart \
--prop chartType=boxWhisker \
--prop title="Server Latency Dashboard" \
--prop title.color=0F172A \
--prop title.size=18 \
--prop title.bold=true \
--prop title.font="Helvetica Neue" \
--prop title.shadow=000000-4-45-2-40 \
--prop "series1=US-East:8,12,15,18,20,22,24,26,28,30,35,42,55,70,95" \
--prop "series2=EU-West:10,14,18,22,25,28,30,33,36,40,45,50,60,80" \
--prop "series3=AP-South:15,20,25,30,35,38,42,45,48,52,58,65,75,90,120" \
--prop quartileMethod=exclusive \
--prop colors=3B82F6,10B981,F59E0B \
--prop series.shadow=000000-4-45-2-30 \
--prop axismin=0 --prop axismax=130 --prop majorunit=10 \
--prop xAxisTitle=Region \
--prop yAxisTitle="Latency (ms)" \
--prop axisTitle.color=475569 \
--prop axisTitle.size=11 \
--prop axisTitle.bold=true \
--prop axisTitle.font="Helvetica Neue" \
--prop "axisfont=9:64748B:Helvetica Neue" \
--prop "axisline=CBD5E1:1" \
--prop gridlineColor=E2E8F0 \
--prop dataLabels=true \
--prop "datalabels.numfmt=0" \
--prop legend=top \
--prop legend.overlay=false \
--prop "legendfont=10:475569:Helvetica Neue" \
--prop plotareafill=F8FAFC \
--prop "plotarea.border=E2E8F0:0.75" \
--prop chartareafill=FFFFFF \
--prop "chartarea.border=CBD5E1:0.75" \
--prop x=14 --prop y=19 --prop width=16 --prop height=22
# Remove blank default Sheet1
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+174
View File
@@ -0,0 +1,174 @@
# Bubble Charts Showcase
This demo consists of three files that work together:
- **charts-bubble.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-bubble.xlsx** — The generated workbook with 4 sheets (4 chart sheets, 14 charts total).
- **charts-bubble.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-bubble.py
# -> charts-bubble.xlsx
```
## Chart Sheets
### Sheet: 1-Bubble Fundamentals
Four bubble charts covering basic rendering, bubble scale, size representation, and data labels.
```bash
# Basic bubble with 2 series (X,Y,Size triplets separated by semicolons)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop series1="Enterprise:50,12,80;120,8,45;200,15,60" \
--prop series2="Consumer:30,25,50;80,18,35;150,22,70" \
--prop catTitle=Market Size ($M) --prop axisTitle=Growth Rate (%)
# bubbleScale=100 with center data labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop bubbleScale=100 \
--prop dataLabels=true --prop labelPos=center
# Small bubbles with bubbleScale=50
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop bubbleScale=50
# Size proportional to diameter (width) instead of area
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop sizeRepresents=width
```
**Features:** `bubble`, X;Y;Size triplet format, `catTitle`, `axisTitle`, `bubbleScale`, `dataLabels`, `labelPos=center`, `labelFont`, `sizeRepresents=width`
### Sheet: 2-Bubble Styling
Four styled bubble charts with title fonts, transparency, grid styling, and shadow effects.
```bash
# Title and legend styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop legend=right --prop legendfont=10:333333:Calibri
# Transparent overlapping bubbles (ARGB with alpha)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop colors=804472C4,80ED7D31 \
--prop bubbleScale=120
# Grid and axis line styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop gridlines=D9D9D9:0.5 --prop axisfont=9:666666 \
--prop axisLine=333333-1
# Shadow and fill effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop plotFill=F0F4F8 --prop chartFill=FAFAFA \
--prop series.shadow=000000-4-315-2-30
```
**Features:** `title.font/size/color/bold`, `legend=right`, `legendfont`, ARGB transparency (`80RRGGBB`), `bubbleScale`, `gridlines`, `axisfont`, `axisLine`, `plotFill`, `chartFill`, `series.shadow`
### Sheet: 3-Bubble Advanced
Four advanced bubble charts with secondary axis, reference lines, log scale, and trendlines.
```bash
# Secondary axis for second series
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop secondaryAxis=2
# Reference line (growth threshold)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop referenceLine=18:Target Growth:C00000
# Logarithmic scale with axis range
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop axisMin=1 --prop axisMax=50 \
--prop logBase=10
# Borders and trendline
officecli add data.xlsx /Sheet --type chart \
--prop chartType=bubble \
--prop chartArea.border=333333-1.5 \
--prop plotArea.border=999999-0.75 \
--prop trendline=linear
```
**Features:** `secondaryAxis`, `referenceLine`, `axisMin/Max`, `logBase`, `chartArea.border`, `plotArea.border`, `trendline=linear`
### Sheet: 4-Bubble Series Data
Two charts demonstrating bubble-series-specific data properties: negative bubble rendering and linking bubble sizes to worksheet cell ranges.
```bash
# shownegbubbles — render bubbles whose size value is negative
officecli add charts-bubble.xlsx "/4-Bubble Series Data" --type chart \
--prop chartType=bubble \
--prop title="shownegbubbles — negative sizes visible" \
--prop series1="Data:60,30,90" \
--prop series2="Neg:40,50,70" \
--prop colors=4472C4,C00000 \
--prop shownegbubbles=true \
--prop bubbleScale=80 \
--prop legend=bottom
# series1.bubbleSize — link bubble sizes to worksheet cells
# First populate size data in cells A1:A3, then reference it:
officecli add charts-bubble.xlsx "/4-Bubble Series Data" --type cell --prop ref=A1 --prop value=10
officecli add charts-bubble.xlsx "/4-Bubble Series Data" --type cell --prop ref=A2 --prop value=25
officecli add charts-bubble.xlsx "/4-Bubble Series Data" --type cell --prop ref=A3 --prop value=40
officecli add charts-bubble.xlsx "/4-Bubble Series Data" --type chart \
--prop chartType=bubble \
--prop title="series1.bubbleSize — range ref" \
--prop series1="Sizes:80,45,60" \
--prop 'series1.bubbleSize=4-Bubble Series Data!$A$1:$A$3' \
--prop colors=70AD47 \
--prop bubbleScale=100 --prop legend=bottom
```
**Features:** `shownegbubbles=true` (Excel hides negative-size bubbles by default; set true to reflect and display them), `series1.bubbleSize=<range>` (link bubble sizes to a worksheet cell range so Excel recomputes when source data changes; `bubbleSizeRef` is emitted on `Get` alongside the cached literal values)
## Complete Feature Coverage
| Feature | Sheet |
|---------|-------|
| `bubble` chart type | 1, 2, 3, 4 |
| `catTitle`, `axisTitle` | 1 |
| `bubbleScale` (50/80/100/120) | 1, 2, 3, 4 |
| `sizeRepresents=width` | 1 |
| `dataLabels`, `labelPos=center`, `labelFont` | 1 |
| `title.font/size/color/bold` | 2 |
| `legend`, `legendfont` | 1, 2, 3, 4 |
| ARGB transparency (`80RRGGBB`) | 2 |
| `gridlines`, `axisfont`, `axisLine` | 2 |
| `plotFill`, `chartFill` | 2, 3 |
| `series.shadow` | 2 |
| `secondaryAxis` | 3 |
| `referenceLine` | 3 |
| `axisMin/Max`, `logBase` | 3 |
| `chartArea.border`, `plotArea.border` | 3 |
| `trendline=linear` | 3 |
| `shownegbubbles` | 4 |
| `series1.bubbleSize` (range ref) | 4 |
## Inspect the Generated File
```bash
officecli query charts-bubble.xlsx chart
officecli get charts-bubble.xlsx "/1-Bubble Fundamentals/chart[1]"
```
+320
View File
@@ -0,0 +1,320 @@
#!/usr/bin/env python3
"""
Bubble Charts Showcase — bubble scale, size representation, and styling.
Generates: charts-bubble.xlsx — 4 chart sheets, 14 charts total
exercising chartType=bubble (X;Y;Size data), bubbleScale, sizeRepresents,
dataLabels, ARGB transparency, gridlines/axis styling, plot/chart fills,
series shadow, secondaryAxis, referenceLine, log scale, trendline,
shownegbubbles, and series1.bubbleSize range references.
SDK twin of charts-bubble.sh (officecli CLI). Both produce an equivalent
charts-bubble.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet,
cell and chart is shipped over the named pipe — grouped per sheet into
`doc.batch(...)` round-trips. Each item is the same
`{"command","parent","type","props"}` dict you'd put in an `officecli
batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-bubble.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-bubble.xlsx")
def sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
def cell(parent, ref, value):
"""One `add cell` item in batch-shape (matches the CLI's
`add ... --type cell --prop ref=.. --prop value=..`)."""
return {"command": "add", "parent": parent, "type": "cell",
"props": {"ref": ref, "value": value}}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet: 1-Bubble Fundamentals
# ======================================================================
print("\n--- 1-Bubble Fundamentals ---")
S1 = "/1-Bubble Fundamentals"
items = [
sheet("1-Bubble Fundamentals"),
# ------------------------------------------------------------------
# Chart 1: Basic bubble chart with 2 series
# Features: chartType=bubble, X;Y;Size triplets, catTitle, axisTitle
# ------------------------------------------------------------------
chart(S1,
chartType="bubble",
title="Market Analysis",
series1="Enterprise:80,45,60",
series2="Consumer:50,35,70",
colors="4472C4,ED7D31",
x="0", y="0", width="12", height="18",
catTitle="Market Size", axisTitle="Growth Rate",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 2: bubbleScale=100 with dataLabels
# Features: bubbleScale=100, dataLabels with center positioning
# ------------------------------------------------------------------
chart(S1,
chartType="bubble",
title="Product Portfolio",
series1="Products:90,50,70,40",
colors="2E75B6",
x="13", y="0", width="12", height="18",
bubbleScale="100",
dataLabels="true", labelPos="center",
labelFont="9:FFFFFF:true",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 3: bubbleScale=50 (smaller bubbles)
# Features: bubbleScale=50
# ------------------------------------------------------------------
chart(S1,
chartType="bubble",
title="Small Bubbles (Scale 50)",
series1="Tech:60,80,45",
series2="Finance:55,70,35",
colors="70AD47,FFC000",
x="0", y="19", width="12", height="18",
bubbleScale="50",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 4: sizeRepresents=width
# Features: sizeRepresents=width (bubble diameter proportional to value)
# ------------------------------------------------------------------
chart(S1,
chartType="bubble",
title="Size by Width",
series1="Regions:70,40,55,85",
colors="5B9BD5",
x="13", y="19", width="12", height="18",
sizeRepresents="width",
bubbleScale="100",
legend="bottom"),
]
doc.batch(items)
# ======================================================================
# Sheet: 2-Bubble Styling
# ======================================================================
print("--- 2-Bubble Styling ---")
S2 = "/2-Bubble Styling"
items = [
sheet("2-Bubble Styling"),
# ------------------------------------------------------------------
# Chart 1: Title styling, legend positioning
# Features: title.font/size/color/bold, legend=right, legendfont
# ------------------------------------------------------------------
chart(S2,
chartType="bubble",
title="Styled Bubble Chart",
series1="SegmentA:65,50,80",
series2="SegmentB:45,60,40",
colors="1F4E79,C55A11",
x="0", y="0", width="12", height="18",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"},
legend="right", legendfont="10:333333:Calibri"),
# ------------------------------------------------------------------
# Chart 2: Series colors, transparency
# Features: ARGB colors with alpha (80=50% transparency)
# ------------------------------------------------------------------
chart(S2,
chartType="bubble",
title="Transparent Overlapping Bubbles",
series1="GroupX:75,60,90,50",
series2="GroupY:65,55,80,45",
colors="804472C4,80ED7D31",
x="13", y="0", width="12", height="18",
bubbleScale="120",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 3: gridlines, axisfont, axisLine
# Features: gridlines, axisfont, axisLine
# ------------------------------------------------------------------
chart(S2,
chartType="bubble",
title="Grid & Axis Styling",
series1="Div1:55,70,45",
series2="Div2:60,40,75",
colors="2E75B6,548235",
x="0", y="19", width="12", height="18",
gridlines="D9D9D9:0.5",
axisfont="9:666666",
axisLine="333333:1",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 4: plotFill, chartFill, series.shadow
# Features: plotFill, chartFill, series.shadow
# ------------------------------------------------------------------
chart(S2,
chartType="bubble",
title="Shadow & Fill Effects",
series1="Portfolio:80,55,65,45",
colors="4472C4",
x="13", y="19", width="12", height="18",
plotFill="F0F4F8", chartFill="FAFAFA",
**{"series.shadow": "000000-4-315-2-30"},
legend="bottom"),
]
doc.batch(items)
# ======================================================================
# Sheet: 3-Bubble Advanced
# ======================================================================
print("--- 3-Bubble Advanced ---")
S3 = "/3-Bubble Advanced"
items = [
sheet("3-Bubble Advanced"),
# ------------------------------------------------------------------
# Chart 1: secondaryAxis
# Features: secondaryAxis on bubble chart
# ------------------------------------------------------------------
chart(S3,
chartType="bubble",
title="Dual-Axis Bubble",
series1="Domestic:70,85,60,90",
series2="International:45,55,80,65",
categories="1,2,3,4",
colors="4472C4,ED7D31",
x="0", y="0", width="12", height="18",
secondaryAxis="2",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 2: referenceLine
# Features: referenceLine on bubble chart
# ------------------------------------------------------------------
chart(S3,
chartType="bubble",
title="Growth Threshold",
series1="Products:60,80,45,55",
categories="1,2,3,4",
colors="70AD47",
x="13", y="0", width="12", height="18",
referenceLine="50:C00000:Target",
bubbleScale="80",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 3: axisMin/Max, logBase
# Features: axisMin/Max, logBase=10 (logarithmic scale)
# ------------------------------------------------------------------
chart(S3,
chartType="bubble",
title="Log Scale Analysis",
series1="Markets:5,15,50,120",
categories="1,2,3,4",
colors="2E75B6",
x="0", y="19", width="12", height="18",
axisMin="1", axisMax="200",
logBase="10",
bubbleScale="80",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 4: chartArea.border, plotArea.border, trendline
# Features: chartArea.border, plotArea.border, trendline=linear
# ------------------------------------------------------------------
chart(S3,
chartType="bubble",
title="Trend & Borders",
series1="Investments:20,55,95,140,180",
categories="1,2,3,4,5",
colors="4472C4",
x="13", y="19", width="12", height="18",
**{"chartArea.border": "333333:1.5",
"plotArea.border": "999999:0.75"},
trendline="linear",
legend="bottom"),
]
doc.batch(items)
# ======================================================================
# Sheet: 4-Bubble Series Data
# ======================================================================
print("--- 4-Bubble Series Data ---")
S4 = "/4-Bubble Series Data"
items = [
sheet("4-Bubble Series Data"),
# ------------------------------------------------------------------
# Chart 1: shownegbubbles — render bubbles for negative size values
# Features: shownegbubbles=true (render bubbles whose size value is
# negative by reflecting them — Excel hides them by default)
# ------------------------------------------------------------------
chart(S4,
chartType="bubble",
title="shownegbubbles — negative sizes visible",
series1="Data:60,30,90",
series2="Neg:40,50,70",
colors="4472C4,C00000",
x="0", y="0", width="12", height="18",
shownegbubbles="true",
bubbleScale="80",
legend="bottom"),
# ------------------------------------------------------------------
# Chart 2: series1.bubbleSize (range ref) — sizes from worksheet cells
#
# Populate some size data first, then reference it. Demonstrates the
# bubbleSize + bubbleSizeRef round-trip: Excel re-computes when the
# source cells change; bubbleSizeRef is emitted on Get alongside the
# cached literal bubbleSize values.
# ------------------------------------------------------------------
cell(S4, "A1", "10"),
cell(S4, "A2", "25"),
cell(S4, "A3", "40"),
chart(S4,
chartType="bubble",
title="series1.bubbleSize — range ref",
series1="Sizes:80,45,60",
**{"series1.bubbleSize": "4-Bubble Series Data!$A$1:$A$3"},
colors="70AD47",
x="13", y="0", width="12", height="18",
bubbleScale="100", legend="bottom"),
]
doc.batch(items)
# Remove blank default Sheet1 (all data is inline)
doc.send({"command": "remove", "path": "/Sheet1"})
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nDone! Generated: {FILE}")
print(" 4 chart sheets, 14 charts total")
+225
View File
@@ -0,0 +1,225 @@
#!/bin/bash
# Bubble Charts Showcase — bubble scale, size representation, and styling.
# Generates charts-bubble.xlsx (4 chart sheets, 14 charts total).
#
# CLI twin of charts-bubble.py (officecli Python SDK). Both produce an
# equivalent charts-bubble.xlsx.
#
# Usage: ./charts-bubble.sh
# 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")/charts-bubble.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet: 1-Bubble Fundamentals
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Bubble Fundamentals"
# Chart 1: Basic bubble chart with 2 series
# Features: chartType=bubble, X;Y;Size triplets, catTitle, axisTitle
officecli add "$FILE" "/1-Bubble Fundamentals" --type chart \
--prop chartType=bubble \
--prop title="Market Analysis" \
--prop series1=Enterprise:80,45,60 \
--prop series2=Consumer:50,35,70 \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop "catTitle=Market Size" --prop "axisTitle=Growth Rate" \
--prop legend=bottom
# Chart 2: bubbleScale=100 with dataLabels
# Features: bubbleScale=100, dataLabels with center positioning
officecli add "$FILE" "/1-Bubble Fundamentals" --type chart \
--prop chartType=bubble \
--prop title="Product Portfolio" \
--prop series1=Products:90,50,70,40 \
--prop colors=2E75B6 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop bubbleScale=100 \
--prop dataLabels=true --prop labelPos=center \
--prop labelFont=9:FFFFFF:true \
--prop legend=bottom
# Chart 3: bubbleScale=50 (smaller bubbles)
# Features: bubbleScale=50
officecli add "$FILE" "/1-Bubble Fundamentals" --type chart \
--prop chartType=bubble \
--prop title="Small Bubbles (Scale 50)" \
--prop series1=Tech:60,80,45 \
--prop series2=Finance:55,70,35 \
--prop colors=70AD47,FFC000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop bubbleScale=50 \
--prop legend=bottom
# Chart 4: sizeRepresents=width
# Features: sizeRepresents=width (bubble diameter proportional to value)
officecli add "$FILE" "/1-Bubble Fundamentals" --type chart \
--prop chartType=bubble \
--prop title="Size by Width" \
--prop series1=Regions:70,40,55,85 \
--prop colors=5B9BD5 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop sizeRepresents=width \
--prop bubbleScale=100 \
--prop legend=bottom
# ==========================================================================
# Sheet: 2-Bubble Styling
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Bubble Styling"
# Chart 1: Title styling, legend positioning
# Features: title.font/size/color/bold, legend=right, legendfont
officecli add "$FILE" "/2-Bubble Styling" --type chart \
--prop chartType=bubble \
--prop title="Styled Bubble Chart" \
--prop series1=SegmentA:65,50,80 \
--prop series2=SegmentB:45,60,40 \
--prop colors=1F4E79,C55A11 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop legend=right --prop legendfont=10:333333:Calibri
# Chart 2: Series colors, transparency
# Features: ARGB colors with alpha (80=50% transparency)
officecli add "$FILE" "/2-Bubble Styling" --type chart \
--prop chartType=bubble \
--prop title="Transparent Overlapping Bubbles" \
--prop series1=GroupX:75,60,90,50 \
--prop series2=GroupY:65,55,80,45 \
--prop colors=804472C4,80ED7D31 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop bubbleScale=120 \
--prop legend=bottom
# Chart 3: gridlines, axisfont, axisLine
# Features: gridlines, axisfont, axisLine
officecli add "$FILE" "/2-Bubble Styling" --type chart \
--prop chartType=bubble \
--prop title="Grid & Axis Styling" \
--prop series1=Div1:55,70,45 \
--prop series2=Div2:60,40,75 \
--prop colors=2E75B6,548235 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop gridlines=D9D9D9:0.5 \
--prop axisfont=9:666666 \
--prop axisLine=333333:1 \
--prop legend=bottom
# Chart 4: plotFill, chartFill, series.shadow
# Features: plotFill, chartFill, series.shadow
officecli add "$FILE" "/2-Bubble Styling" --type chart \
--prop chartType=bubble \
--prop title="Shadow & Fill Effects" \
--prop series1=Portfolio:80,55,65,45 \
--prop colors=4472C4 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop plotFill=F0F4F8 --prop chartFill=FAFAFA \
--prop series.shadow=000000-4-315-2-30 \
--prop legend=bottom
# ==========================================================================
# Sheet: 3-Bubble Advanced
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Bubble Advanced"
# Chart 1: secondaryAxis
# Features: secondaryAxis on bubble chart
officecli add "$FILE" "/3-Bubble Advanced" --type chart \
--prop chartType=bubble \
--prop title="Dual-Axis Bubble" \
--prop series1=Domestic:70,85,60,90 \
--prop series2=International:45,55,80,65 \
--prop categories=1,2,3,4 \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop secondaryAxis=2 \
--prop legend=bottom
# Chart 2: referenceLine
# Features: referenceLine on bubble chart
officecli add "$FILE" "/3-Bubble Advanced" --type chart \
--prop chartType=bubble \
--prop title="Growth Threshold" \
--prop series1=Products:60,80,45,55 \
--prop categories=1,2,3,4 \
--prop colors=70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop "referenceLine=50:C00000:Target" \
--prop bubbleScale=80 \
--prop legend=bottom
# Chart 3: axisMin/Max, logBase
# Features: axisMin/Max, logBase=10 (logarithmic scale)
officecli add "$FILE" "/3-Bubble Advanced" --type chart \
--prop chartType=bubble \
--prop title="Log Scale Analysis" \
--prop series1=Markets:5,15,50,120 \
--prop categories=1,2,3,4 \
--prop colors=2E75B6 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop axisMin=1 --prop axisMax=200 \
--prop logBase=10 \
--prop bubbleScale=80 \
--prop legend=bottom
# Chart 4: chartArea.border, plotArea.border, trendline
# Features: chartArea.border, plotArea.border, trendline=linear
officecli add "$FILE" "/3-Bubble Advanced" --type chart \
--prop chartType=bubble \
--prop title="Trend & Borders" \
--prop series1=Investments:20,55,95,140,180 \
--prop categories=1,2,3,4,5 \
--prop colors=4472C4 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop chartArea.border=333333:1.5 \
--prop plotArea.border=999999:0.75 \
--prop trendline=linear \
--prop legend=bottom
# ==========================================================================
# Sheet: 4-Bubble Series Data
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="4-Bubble Series Data"
# Chart 1: shownegbubbles — render bubbles for negative size values
# Features: shownegbubbles=true (Excel hides negative-size bubbles by default)
officecli add "$FILE" "/4-Bubble Series Data" --type chart \
--prop chartType=bubble \
--prop title="shownegbubbles — negative sizes visible" \
--prop series1=Data:60,30,90 \
--prop series2=Neg:40,50,70 \
--prop colors=4472C4,C00000 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop shownegbubbles=true \
--prop bubbleScale=80 \
--prop legend=bottom
# Chart 2: series1.bubbleSize (range ref) — sizes from worksheet cells
# Populate size data first, then reference it (bubbleSize + bubbleSizeRef round-trip).
officecli add "$FILE" "/4-Bubble Series Data" --type cell --prop ref=A1 --prop value=10
officecli add "$FILE" "/4-Bubble Series Data" --type cell --prop ref=A2 --prop value=25
officecli add "$FILE" "/4-Bubble Series Data" --type cell --prop ref=A3 --prop value=40
officecli add "$FILE" "/4-Bubble Series Data" --type chart \
--prop chartType=bubble \
--prop title="series1.bubbleSize — range ref" \
--prop series1=Sizes:80,45,60 \
--prop 'series1.bubbleSize=4-Bubble Series Data!$A$1:$A$3' \
--prop colors=70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop bubbleScale=100 --prop legend=bottom
# Remove blank default Sheet1 (all data is inline)
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+283
View File
@@ -0,0 +1,283 @@
# Column Charts Showcase
This demo consists of three files that work together:
- **charts-column.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-column.xlsx** — The generated workbook with 8 sheets (1 data + 7 chart sheets, 28 charts total).
- **charts-column.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-column.py
# → charts-column.xlsx
```
## Chart Sheets
### Sheet: 1-Column Fundamentals
Four basic column charts covering every data input method.
```bash
# dataRange with axis titles and axis font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop dataRange=Sheet1!A1:E13 \
--prop catTitle=Month --prop axisTitle=Revenue \
--prop axisfont=9:58626E:Arial --prop gridlines=D9D9D9:0.5:dot
# Inline named series with gap width
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop series1="Laptops:320,280,350,310" \
--prop series2="Phones:450,420,480,460" \
--prop categories=Jan,Feb,Mar,Apr \
--prop colors=2E75B6,C00000,70AD47 \
--prop gapwidth=80
# Cell-range series (dotted syntax)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop series1.name=East \
--prop series1.values=Sheet1!B2:B13 \
--prop series1.categories=Sheet1!A2:A13 \
--prop minorGridlines=EEEEEE:0.3:dot
# Inline data shorthand
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop 'data=Team A:85,92,78;Team B:70,80,85' \
--prop categories=Mon,Tue,Wed \
--prop legend=right
```
**Features:** `series1=Name:v1,v2`, `series1.name`/`.values`/`.categories` (cell range), `dataRange`, `data` (shorthand), `categories`, `colors`, `catTitle`, `axisTitle`, `axisfont`, `gridlines`, `minorGridlines`, `gapwidth`, `legend` (bottom, right)
### Sheet: 2-Column Variants
Four charts covering all column chart type variants.
```bash
# Stacked column with center labels and series outline
officecli add data.xlsx /Sheet --type chart \
--prop chartType=columnStacked \
--prop dataLabels=center \
--prop series.outline=FFFFFF-0.5
# 100% stacked column — proportional
officecli add data.xlsx /Sheet --type chart \
--prop chartType=columnPercentStacked \
--prop axisNumFmt=0%
# 3D column with perspective
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column3d \
--prop view3d=15,20,30 --prop style=3
# 3D column with gap depth
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column3d \
--prop gapDepth=200
```
**Features:** `columnStacked`, `columnPercentStacked`, `column3d`, `dataLabels=center`, `series.outline`, `axisNumFmt`, `view3d` (rotX,rotY,perspective), `style` (preset 1-48), `gapDepth`
### Sheet: 3-Column Styling
Four charts demonstrating visual styling — title formatting, shadows, gradients, and transparency.
```bash
# Styled title
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true
# Series shadow and outline effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop series.shadow=000000-4-315-2-40 \
--prop series.outline=FFFFFF-0.5
# Per-series gradient fills
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90;70AD47-C5E0B4:90'
# Transparent columns on gradient background
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop transparency=30 \
--prop plotFill=F0F4F8-D6E4F0:90 --prop chartFill=FFFFFF \
--prop roundedCorners=true
```
**Features:** `title.font`/`.size`/`.color`/`.bold`, `series.shadow` (color-blur-angle-dist-opacity), `series.outline`, `gradients` (per-series), `transparency`, `plotFill` (gradient), `chartFill`, `roundedCorners`
### Sheet: 4-Axis & Gridlines
Four charts demonstrating every axis and gridline configuration.
```bash
# Custom axis scaling with axis lines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop axisMin=50 --prop axisMax=250 \
--prop majorUnit=50 --prop minorUnit=25 \
--prop axisLine=C00000:1.5:solid --prop catAxisLine=2E75B6:1.5:solid
# Logarithmic scale with reversed axis
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop logBase=10 --prop axisReverse=true
# Display units with tick marks
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop dispUnits=thousands --prop axisNumFmt=#,##0 \
--prop majorTickMark=outside --prop minorTickMark=inside
# Hidden axes with data table
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop gridlines=none --prop axisVisible=false \
--prop dataTable=true --prop legend=none
```
**Features:** `axisMin`, `axisMax`, `majorUnit`, `minorUnit`, `axisLine`, `catAxisLine`, `logBase` (logarithmic scale), `axisReverse` (flip direction), `dispUnits` (thousands/millions), `axisNumFmt`, `majorTickMark`, `minorTickMark`, `axisVisible`, `dataTable`, `gridlines=none`, `legend=none`
### Sheet: 5-Labels & Legend
Four charts demonstrating data label and legend customization.
```bash
# Data labels with number format
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop labelFont=9:333333:true \
--prop dataLabels.numFmt=#,##0
# Custom individual labels (hide some, highlight peak)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop dataLabels=true \
--prop dataLabel1.delete=true --prop dataLabel2.delete=true \
--prop point4.color=C00000 --prop dataLabel4.text=Peak!
# Legend overlay with styled font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop legend=right --prop legend.overlay=true \
--prop legendfont=10:333333:Calibri --prop plotFill=F5F5F5
# Manual layout — plotArea, title, legend positioning
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop plotArea.x=0.12 --prop plotArea.y=0.18 \
--prop plotArea.w=0.82 --prop plotArea.h=0.55 \
--prop title.x=0.25 --prop title.y=0.02 \
--prop legend.x=0.15 --prop legend.y=0.82 \
--prop legend.w=0.7 --prop legend.h=0.12
```
**Features:** `dataLabels`, `labelPos` (outsideEnd/center/insideEnd/insideBase), `labelFont`, `dataLabels.numFmt`, `dataLabel{N}.delete`, `dataLabel{N}.text`, `point{N}.color`, `legend` (right), `legend.overlay`, `legendfont`, `plotFill`, `plotArea.x/y/w/h`, `title.x/y`, `legend.x/y/w/h`
### Sheet: 6-Effects & Advanced
Four charts demonstrating advanced features — secondary axis, reference lines, effects, and conditional coloring.
```bash
# Secondary axis (dual scale)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop secondaryAxis=2 \
--prop series1="Revenue:120,180,250,310" \
--prop series2="Growth %:50,33,39,24"
# Reference line (target threshold)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop referenceLine=150:FF0000:1.5:dash
# Title glow/shadow effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop title.glow=4472C4-8-60 \
--prop title.shadow=000000-3-315-2-40 \
--prop series.shadow=000000-3-315-1-30
# Conditional coloring with chart/plot borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop colorRule=0:C00000:70AD47 \
--prop referenceLine=0:888888:1:solid \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot
```
**Features:** `secondaryAxis` (1-based series indices), `referenceLine` (value:color:width:dash), `title.glow` (color-radius-opacity), `title.shadow` (color-blur-angle-dist-opacity), `series.shadow`, `colorRule` (threshold:belowColor:aboveColor), `chartArea.border`, `plotArea.border`
### Sheet: 7-Bar Shape & Gap
Four charts demonstrating column gap width, overlap, and 3D bar shapes.
```bash
# Narrow gap (bars close together)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop gapwidth=30
# Wide gap with negative overlap (separated bars within group)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column \
--prop gapwidth=200 --prop overlap=-50
# Cylinder shape (3D)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column3d \
--prop shape=cylinder --prop view3d=15,20,30
# Cone shape (3D)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=column3d \
--prop shape=cone --prop view3d=15,20,30
```
**Features:** `gapwidth` (0-500), `overlap` (-100 to 100, negative = separated), `shape` (cylinder, cone, pyramid — 3D column shapes)
## Complete Feature Coverage
| Feature | Sheet |
|---------|-------|
| **Chart types:** column, columnStacked, columnPercentStacked, column3d | 1, 2 |
| **Data input:** series, dataRange, data, series.name/values/categories | 1 |
| **Colors:** colors, gradients | 1, 3 |
| **Gap & overlap:** gapwidth, overlap | 1, 7 |
| **Axis scaling:** axisMin/Max, majorUnit, minorUnit | 4 |
| **Axis features:** logBase, axisReverse, dispUnits, axisNumFmt | 2, 4 |
| **Axis lines:** axisLine, catAxisLine | 4 |
| **Axis visibility:** axisVisible | 4 |
| **Tick marks:** majorTickMark, minorTickMark | 4 |
| **Gridlines:** gridlines, minorGridlines, gridlines=none | 1, 4 |
| **Data labels:** dataLabels, labelPos, labelFont, numFmt | 2, 5 |
| **Custom labels:** dataLabel{N}.text, dataLabel{N}.delete | 5 |
| **Point color:** point{N}.color | 5 |
| **Legend:** position, legendfont, legend.overlay, legend=none | 1, 4, 5 |
| **Layout:** plotArea.x/y/w/h, title.x/y, legend.x/y/w/h | 5 |
| **Effects:** series.shadow, series.outline, transparency | 2, 3 |
| **Title styling:** font, size, color, bold, glow, shadow | 3, 6 |
| **Fills:** plotFill, chartFill (solid + gradient) | 3, 5 |
| **Borders:** chartArea.border, plotArea.border | 6 |
| **Advanced:** secondaryAxis, referenceLine, colorRule | 6 |
| **3D:** view3d, gapDepth, style, shape (cylinder/cone/pyramid) | 2, 7 |
| **Other:** dataTable, roundedCorners, catTitle, axisTitle, axisfont | 1, 3, 4 |
## Inspect the Generated File
```bash
officecli query charts-column.xlsx chart
officecli get charts-column.xlsx "/1-Column Fundamentals/chart[1]"
```
+601
View File
@@ -0,0 +1,601 @@
#!/usr/bin/env python3
"""
Column & Bar Charts Showcase — column, columnStacked, columnPercentStacked, and column3d with all variations.
Generates: charts-column.xlsx
SDK twin of charts-column.sh (officecli CLI). Both produce an equivalent
charts-column.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started, the shared source data
and every chart are shipped over the named pipe, and each sheet's charts are
applied in `doc.batch(...)` round-trips. Each item is the same
`{"command","parent","type","props"}` dict you'd put in an `officecli batch` list.
Every column chart feature officecli supports is demonstrated at least once:
gap width, overlap, bar shapes, axis scaling, gridlines, data labels,
legend positioning, reference lines, secondary axis, gradients,
transparency, shadows, manual layout, and 3D rotation.
8 sheets (Sheet1 data + 7 chart sheets), 28 charts total.
1-Column Fundamentals 4 charts — data input variants, axis titles, inline/cell-range/data
2-Column Variants 4 charts — columnStacked, columnPercentStacked, column3d
3-Column Styling 4 charts — title styling, series effects, gradients, transparency
4-Axis & Gridlines 4 charts — axis scaling, log scale, reverse, display units
5-Labels & Legend 4 charts — data labels, custom labels, legend layout
6-Effects & Advanced 4 charts — secondary axis, reference line, glow/shadow, colorRule
7-Bar Shape & Gap 4 charts — gapwidth, overlap, 3D shapes (cylinder, cone, pyramid)
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-column.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-column.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def cell(path, **props):
"""One `set` item writing a cell in batch-shape."""
return {"command": "set", "path": path, "props": props}
def chart(sheet, **props):
"""One `add chart` item in batch-shape, anchored on a sheet."""
return {"command": "add", "parent": sheet, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Source data — shared across all charts (Sheet1)
# ======================================================================
print("--- Populating source data ---")
data_items = []
for j, h in enumerate(["Month", "East", "South", "North", "West"]):
data_items.append(cell(f"/Sheet1/{'ABCDE'[j]}1", text=h, bold="true"))
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
east = [120, 135, 148, 162, 155, 178, 195, 210, 188, 172, 165, 198]
south = [95, 108, 115, 128, 142, 155, 168, 175, 160, 148, 135, 158]
north = [88, 92, 105, 118, 125, 138, 145, 152, 140, 130, 122, 142]
west = [110, 118, 130, 145, 138, 162, 175, 190, 170, 155, 148, 180]
for i in range(12):
r = i + 2
for j, val in enumerate([months[i], east[i], south[i], north[i], west[i]]):
data_items.append(cell(f"/Sheet1/{'ABCDE'[j]}{r}", text=str(val)))
doc.batch(data_items)
# ======================================================================
# Sheet: 1-Column Fundamentals
# ======================================================================
print("--- 1-Column Fundamentals ---")
s = "/1-Column Fundamentals"
items = [add_sheet("1-Column Fundamentals")]
# ------------------------------------------------------------------
# Chart 1: Basic column with dataRange and axis titles
# Features: chartType=column, dataRange, catTitle, axisTitle, axisfont,
# gridlines, colors
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Monthly Sales by Region",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="12", height="18",
catTitle="Month", axisTitle="Revenue",
axisfont="9:58626E:Arial",
gridlines="D9D9D9:0.5:dot",
colors="4472C4,ED7D31,70AD47,FFC000"))
# ------------------------------------------------------------------
# Chart 2: Inline series with custom colors and gap width
# Features: inline series (series1=Name:v1,v2,...), colors, gapwidth,
# legend=bottom
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Q1 Product Sales",
series1="Laptops:320,280,350,310",
series2="Phones:450,420,480,460",
series3="Tablets:180,160,200,190",
categories="Jan,Feb,Mar,Apr",
colors="2E75B6,C00000,70AD47",
x="13", y="0", width="12", height="18",
gapwidth="80",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 3: Dotted syntax with cell ranges
# Features: series.name/values/categories (cell range via dotted syntax),
# minorGridlines
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="East vs South (Cell Range)",
**{"series1.name": "East",
"series1.values": "Sheet1!B2:B13",
"series1.categories": "Sheet1!A2:A13",
"series2.name": "South",
"series2.values": "Sheet1!C2:C13",
"series2.categories": "Sheet1!A2:A13"},
x="0", y="19", width="12", height="18",
colors="4472C4,ED7D31",
gridlines="D9D9D9:0.5:dot",
minorGridlines="EEEEEE:0.3:dot"))
# ------------------------------------------------------------------
# Chart 4: data= shorthand format
# Features: data (inline shorthand Name:v1;Name2:v2), legend=right
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Weekly Output",
data="Team A:85,92,78,95,88;Team B:70,80,85,90,75",
categories="Mon,Tue,Wed,Thu,Fri",
colors="0070C0,FF6600",
x="13", y="19", width="12", height="18",
legend="right"))
doc.batch(items)
# ======================================================================
# Sheet: 2-Column Variants
# ======================================================================
print("--- 2-Column Variants ---")
s = "/2-Column Variants"
items = [add_sheet("2-Column Variants")]
# ------------------------------------------------------------------
# Chart 1: Stacked column with center data labels and series outline
# Features: columnStacked, dataLabels=center, series.outline
# ------------------------------------------------------------------
items.append(chart(s,
chartType="columnStacked",
title="Stacked Sales by Region",
dataRange="Sheet1!A1:E7",
x="0", y="0", width="12", height="18",
colors="4472C4,ED7D31,70AD47,FFC000",
dataLabels="center",
**{"series.outline": "FFFFFF-0.5"},
legend="bottom"))
# ------------------------------------------------------------------
# Chart 2: 100% stacked column with axis number format
# Features: columnPercentStacked, axisNumFmt=0%, legend=bottom
# ------------------------------------------------------------------
items.append(chart(s,
chartType="columnPercentStacked",
title="Regional Contribution %",
dataRange="Sheet1!A1:E7",
x="13", y="0", width="12", height="18",
colors="1F4E79,2E75B6,9DC3E6,BDD7EE",
axisNumFmt="0%",
legend="bottom",
gridlines="E0E0E0:0.5:solid"))
# ------------------------------------------------------------------
# Chart 3: 3D column with perspective and style
# Features: column3d, view3d (rotX,rotY,perspective), style (preset 1-48)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column3d",
title="3D Regional Trends",
dataRange="Sheet1!A1:E7",
x="0", y="19", width="12", height="18",
view3d="15,20,30",
colors="4472C4,ED7D31,70AD47,FFC000",
chartFill="F8F8F8",
style="3"))
# ------------------------------------------------------------------
# Chart 4: 3D stacked column with gap depth
# Features: column3d stacked, gapDepth=200 (3D depth spacing)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column3d",
title="3D Stacked with Gap Depth",
series1="East:120,135,148,162,155,178",
series2="South:95,108,115,128,142,155",
series3="North:88,92,105,118,125,138",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="19", width="12", height="18",
view3d="15,20,30",
gapDepth="200",
colors="2E75B6,ED7D31,70AD47",
legend="right"))
doc.batch(items)
# ======================================================================
# Sheet: 3-Column Styling
# ======================================================================
print("--- 3-Column Styling ---")
s = "/3-Column Styling"
items = [add_sheet("3-Column Styling")]
# ------------------------------------------------------------------
# Chart 1: Title styling — font, size, color, bold
# Features: title.font=Georgia, title.size=16, title.color=1F4E79,
# title.bold=true
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Styled Title Demo",
dataRange="Sheet1!A1:E7",
x="0", y="0", width="12", height="18",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"},
colors="4472C4,ED7D31,70AD47,FFC000",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 2: Series shadow and outline effects
# Features: series.shadow (color-blur-angle-dist-opacity),
# series.outline (color-width)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Shadow & Outline Effects",
series1="Revenue:320,280,350,310,340",
series2="Cost:210,195,230,220,215",
categories="Q1,Q2,Q3,Q4,Q5",
x="13", y="0", width="12", height="18",
colors="4472C4,C00000",
**{"series.shadow": "000000-4-315-2-40",
"series.outline": "FFFFFF-0.5"},
gapwidth="100",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 3: Per-series gradient fills
# Features: gradients (per-series gradient fills, start-end:angle)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Gradient Columns",
series1="East:120,135,148,162",
series2="South:95,108,115,128",
series3="North:88,92,105,118",
categories="Q1,Q2,Q3,Q4",
x="0", y="19", width="12", height="18",
gradients="4472C4-BDD7EE:90;ED7D31-FBE5D6:90;70AD47-C5E0B4:90",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 4: Transparency + plotFill gradient + chartFill + roundedCorners
# Features: transparency=30, plotFill gradient, chartFill, roundedCorners
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Transparent Columns on Gradient",
dataRange="Sheet1!A1:E7",
x="13", y="19", width="12", height="18",
transparency="30",
plotFill="F0F4F8-D6E4F0:90",
chartFill="FFFFFF",
colors="1F4E79,2E75B6,5B9BD5,9DC3E6",
roundedCorners="true",
legend="bottom"))
doc.batch(items)
# ======================================================================
# Sheet: 4-Axis & Gridlines
# ======================================================================
print("--- 4-Axis & Gridlines ---")
s = "/4-Axis & Gridlines"
items = [add_sheet("4-Axis & Gridlines")]
# ------------------------------------------------------------------
# Chart 1: Custom axis scaling — min, max, majorUnit, minorUnit
# Features: axisMin, axisMax, majorUnit, minorUnit,
# axisLine (value axis line styling), catAxisLine (category axis line)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Custom Axis Scale (50-250)",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="12", height="18",
axisMin="50", axisMax="250", majorUnit="50",
minorUnit="25",
gridlines="D0D0D0:0.5:solid",
minorGridlines="EEEEEE:0.3:dot",
axisLine="C00000:1.5:solid",
catAxisLine="2E75B6:1.5:solid",
colors="4472C4,ED7D31,70AD47,FFC000"))
# ------------------------------------------------------------------
# Chart 2: Logarithmic scale with reversed axis
# Features: logBase=10 (logarithmic scale), axisReverse=true
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Log Scale (Base 10)",
series1="Growth:1,10,100,1000,5000",
categories="Year 1,Year 2,Year 3,Year 4,Year 5",
x="13", y="0", width="12", height="18",
logBase="10",
axisReverse="true",
colors="C00000",
axisTitle="Value (log)",
catTitle="Year",
gridlines="E0E0E0:0.5:dash"))
# ------------------------------------------------------------------
# Chart 3: Display units and axis number format
# Features: dispUnits=thousands, axisNumFmt=#,##0,
# majorTickMark=outside, minorTickMark=inside
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Revenue (in Thousands)",
series1="Revenue:12000,18500,22000,31000,45000,52000",
series2="Cost:8000,11000,14000,19500,28000,33000",
categories="2020,2021,2022,2023,2024,2025",
x="0", y="19", width="12", height="18",
dispUnits="thousands",
axisNumFmt="#,##0",
colors="2E75B6,C00000",
catTitle="Year", axisTitle="Amount (K)",
majorTickMark="outside", minorTickMark="inside",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 4: Hidden axes with data table
# Features: gridlines=none, axisVisible=false, dataTable=true, legend=none
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Minimal Chart with Data Table",
dataRange="Sheet1!A1:E7",
x="13", y="19", width="12", height="18",
gridlines="none",
axisVisible="false",
dataTable="true",
legend="none",
colors="4472C4,ED7D31,70AD47,FFC000"))
doc.batch(items)
# ======================================================================
# Sheet: 5-Labels & Legend
# ======================================================================
print("--- 5-Labels & Legend ---")
s = "/5-Labels & Legend"
items = [add_sheet("5-Labels & Legend")]
# ------------------------------------------------------------------
# Chart 1: Data labels with number format and styled label font
# Features: dataLabels=true, labelPos=outsideEnd, labelFont (size:color:bold),
# dataLabels.numFmt
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Sales with Labels",
series1="Revenue:120,180,210,250,280",
categories="Jan,Feb,Mar,Apr,May",
x="0", y="0", width="12", height="18",
colors="4472C4",
dataLabels="true", labelPos="outsideEnd",
labelFont="9:333333:true",
**{"dataLabels.numFmt": "#,##0"}))
# ------------------------------------------------------------------
# Chart 2: Custom individual labels — delete some, highlight peak
# Features: dataLabel{N}.delete, dataLabel{N}.text, point{N}.color
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Peak Highlight",
series1="Sales:88,120,165,210,195,178",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="0", width="12", height="18",
colors="2E75B6",
dataLabels="true", labelPos="outsideEnd",
**{"dataLabel1.delete": "true", "dataLabel2.delete": "true",
"dataLabel3.delete": "true",
"point4.color": "C00000",
"dataLabel4.text": "Peak!",
"dataLabel5.delete": "true", "dataLabel6.delete": "true"}))
# ------------------------------------------------------------------
# Chart 3: Legend positioning and overlay with styled legend font
# Features: legend=right, legend.overlay=true, legendfont (size:color:fontname),
# plotFill
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Legend Overlay on Chart",
dataRange="Sheet1!A1:E7",
x="0", y="19", width="12", height="18",
colors="4472C4,ED7D31,70AD47,FFC000",
legend="right",
**{"legend.overlay": "true"},
legendfont="10:333333:Calibri",
plotFill="F5F5F5"))
# ------------------------------------------------------------------
# Chart 4: Manual layout — plotArea, title, and legend positioning
# Features: plotArea.x/y/w/h, title.x/y, legend.x/y/w/h (manual layout)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Manual Layout Control",
dataRange="Sheet1!A1:E7",
x="13", y="19", width="12", height="18",
colors="2E75B6,ED7D31,70AD47,FFC000",
**{"plotArea.x": "0.12", "plotArea.y": "0.18",
"plotArea.w": "0.82", "plotArea.h": "0.55",
"title.x": "0.25", "title.y": "0.02",
"legend.x": "0.15", "legend.y": "0.82",
"legend.w": "0.7", "legend.h": "0.12",
"title.font": "Arial", "title.size": "13",
"title.bold": "true"}))
doc.batch(items)
# ======================================================================
# Sheet: 6-Effects & Advanced
# ======================================================================
print("--- 6-Effects & Advanced ---")
s = "/6-Effects & Advanced"
items = [add_sheet("6-Effects & Advanced")]
# ------------------------------------------------------------------
# Chart 1: Secondary axis — dual Y-axis
# Features: secondaryAxis=2 (series 2 on right-hand axis)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Revenue vs Growth Rate",
series1="Revenue:120,180,250,310,380,420",
series2="Growth %:50,33,39,24,23,11",
categories="2020,2021,2022,2023,2024,2025",
x="0", y="0", width="12", height="18",
secondaryAxis="2",
colors="2E75B6,C00000",
catTitle="Year", axisTitle="Revenue",
dataLabels="true", labelPos="outsideEnd",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 2: Reference line (target/threshold)
# referenceLine format: value:color:width:dash
# Features: referenceLine (horizontal target line)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="vs Target (150)",
dataRange="Sheet1!A1:C13",
x="13", y="0", width="12", height="18",
colors="4472C4,70AD47",
referenceLine="150:FF0000:1.5:dash",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 3: Title glow and shadow effects
# Features: title.glow (color-radius-opacity), title.shadow,
# series.shadow on column charts
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Glow & Shadow Effects",
series1="East:120,135,148,162,155,178",
series2="West:110,118,130,145,138,162",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="0", y="19", width="12", height="18",
colors="4472C4,ED7D31",
**{"title.glow": "4472C4-8-60",
"title.shadow": "000000-3-315-2-40",
"title.font": "Calibri", "title.size": "16",
"title.bold": "true", "title.color": "1F4E79",
"series.shadow": "000000-3-315-1-30"},
plotFill="F0F4F8", chartFill="FFFFFF"))
# ------------------------------------------------------------------
# Chart 4: Conditional coloring with chart/plot borders
# colorRule format: threshold:belowColor:aboveColor
# Features: colorRule (threshold-based conditional coloring),
# chartArea.border, plotArea.border
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Profit: Conditional Colors",
series1="Profit:80,120,-30,160,-50,200,140,-20,180,90",
categories="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct",
x="13", y="19", width="12", height="18",
colors="2E75B6",
colorRule="0:C00000:70AD47",
referenceLine="0:888888:1:solid",
**{"chartArea.border": "D0D0D0:1:solid",
"plotArea.border": "E0E0E0:0.5:dot"},
dataLabels="true", labelPos="outsideEnd",
labelFont="8:666666:false"))
doc.batch(items)
# ======================================================================
# Sheet: 7-Bar Shape & Gap
# ======================================================================
print("--- 7-Bar Shape & Gap ---")
s = "/7-Bar Shape & Gap"
items = [add_sheet("7-Bar Shape & Gap")]
# ------------------------------------------------------------------
# Chart 1: Narrow gap width (bars close together)
# Features: gapwidth=30 (narrow gaps between column groups)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Narrow Gap (30%)",
dataRange="Sheet1!A1:E7",
x="0", y="0", width="12", height="18",
gapwidth="30",
colors="4472C4,ED7D31,70AD47,FFC000",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 2: Wide gap with negative overlap (separated bars within group)
# Features: gapwidth=200 (wide gap), overlap=-50 (negative = bars separated)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column",
title="Wide Gap + Negative Overlap",
dataRange="Sheet1!A1:E7",
x="13", y="0", width="12", height="18",
gapwidth="200",
overlap="-50",
colors="2E75B6,ED7D31,70AD47,FFC000",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 3: 3D column with cylinder shape
# Features: shape=cylinder (3D column bar shape)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column3d",
title="Cylinder Shape",
series1="East:120,135,148,162,155,178",
series2="South:95,108,115,128,142,155",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="0", y="19", width="12", height="18",
shape="cylinder",
view3d="15,20,30",
colors="4472C4,ED7D31",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 4: 3D column with cone/pyramid shapes
# Features: shape=cone (3D column bar shape — also supports pyramid)
# ------------------------------------------------------------------
items.append(chart(s,
chartType="column3d",
title="Cone Shape",
series1="North:88,92,105,118,125,138",
series2="West:110,118,130,145,138,162",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="19", width="12", height="18",
shape="cone",
view3d="15,20,30",
colors="70AD47,FFC000",
legend="bottom"))
doc.batch(items)
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"Generated: {FILE}")
print(" 8 sheets (Sheet1 data + 7 chart sheets, 28 charts total)")
+470
View File
@@ -0,0 +1,470 @@
#!/bin/bash
# Column & Bar Charts Showcase — column, columnStacked, columnPercentStacked, and column3d.
# CLI twin of charts-column.py (officecli Python SDK). Both produce an equivalent
# charts-column.xlsx.
#
# Every column chart feature officecli supports is demonstrated at least once:
# gap width, overlap, bar shapes, axis scaling, gridlines, data labels,
# legend positioning, reference lines, secondary axis, gradients,
# transparency, shadows, manual layout, and 3D rotation.
#
# 8 sheets (Sheet1 data + 7 chart sheets), 28 charts total.
#
# Usage:
# ./charts-column.sh
# 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")/charts-column.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Source data — shared across all charts (Sheet1)
# ==========================================================================
officecli set "$FILE" /Sheet1/A1 --prop text=Month --prop bold=true
officecli set "$FILE" /Sheet1/B1 --prop text=East --prop bold=true
officecli set "$FILE" /Sheet1/C1 --prop text=South --prop bold=true
officecli set "$FILE" /Sheet1/D1 --prop text=North --prop bold=true
officecli set "$FILE" /Sheet1/E1 --prop text=West --prop bold=true
officecli set "$FILE" /Sheet1/A2 --prop text=Jan; officecli set "$FILE" /Sheet1/B2 --prop text=120; officecli set "$FILE" /Sheet1/C2 --prop text=95; officecli set "$FILE" /Sheet1/D2 --prop text=88; officecli set "$FILE" /Sheet1/E2 --prop text=110
officecli set "$FILE" /Sheet1/A3 --prop text=Feb; officecli set "$FILE" /Sheet1/B3 --prop text=135; officecli set "$FILE" /Sheet1/C3 --prop text=108; officecli set "$FILE" /Sheet1/D3 --prop text=92; officecli set "$FILE" /Sheet1/E3 --prop text=118
officecli set "$FILE" /Sheet1/A4 --prop text=Mar; officecli set "$FILE" /Sheet1/B4 --prop text=148; officecli set "$FILE" /Sheet1/C4 --prop text=115; officecli set "$FILE" /Sheet1/D4 --prop text=105; officecli set "$FILE" /Sheet1/E4 --prop text=130
officecli set "$FILE" /Sheet1/A5 --prop text=Apr; officecli set "$FILE" /Sheet1/B5 --prop text=162; officecli set "$FILE" /Sheet1/C5 --prop text=128; officecli set "$FILE" /Sheet1/D5 --prop text=118; officecli set "$FILE" /Sheet1/E5 --prop text=145
officecli set "$FILE" /Sheet1/A6 --prop text=May; officecli set "$FILE" /Sheet1/B6 --prop text=155; officecli set "$FILE" /Sheet1/C6 --prop text=142; officecli set "$FILE" /Sheet1/D6 --prop text=125; officecli set "$FILE" /Sheet1/E6 --prop text=138
officecli set "$FILE" /Sheet1/A7 --prop text=Jun; officecli set "$FILE" /Sheet1/B7 --prop text=178; officecli set "$FILE" /Sheet1/C7 --prop text=155; officecli set "$FILE" /Sheet1/D7 --prop text=138; officecli set "$FILE" /Sheet1/E7 --prop text=162
officecli set "$FILE" /Sheet1/A8 --prop text=Jul; officecli set "$FILE" /Sheet1/B8 --prop text=195; officecli set "$FILE" /Sheet1/C8 --prop text=168; officecli set "$FILE" /Sheet1/D8 --prop text=145; officecli set "$FILE" /Sheet1/E8 --prop text=175
officecli set "$FILE" /Sheet1/A9 --prop text=Aug; officecli set "$FILE" /Sheet1/B9 --prop text=210; officecli set "$FILE" /Sheet1/C9 --prop text=175; officecli set "$FILE" /Sheet1/D9 --prop text=152; officecli set "$FILE" /Sheet1/E9 --prop text=190
officecli set "$FILE" /Sheet1/A10 --prop text=Sep; officecli set "$FILE" /Sheet1/B10 --prop text=188; officecli set "$FILE" /Sheet1/C10 --prop text=160; officecli set "$FILE" /Sheet1/D10 --prop text=140; officecli set "$FILE" /Sheet1/E10 --prop text=170
officecli set "$FILE" /Sheet1/A11 --prop text=Oct; officecli set "$FILE" /Sheet1/B11 --prop text=172; officecli set "$FILE" /Sheet1/C11 --prop text=148; officecli set "$FILE" /Sheet1/D11 --prop text=130; officecli set "$FILE" /Sheet1/E11 --prop text=155
officecli set "$FILE" /Sheet1/A12 --prop text=Nov; officecli set "$FILE" /Sheet1/B12 --prop text=165; officecli set "$FILE" /Sheet1/C12 --prop text=135; officecli set "$FILE" /Sheet1/D12 --prop text=122; officecli set "$FILE" /Sheet1/E12 --prop text=148
officecli set "$FILE" /Sheet1/A13 --prop text=Dec; officecli set "$FILE" /Sheet1/B13 --prop text=198; officecli set "$FILE" /Sheet1/C13 --prop text=158; officecli set "$FILE" /Sheet1/D13 --prop text=142; officecli set "$FILE" /Sheet1/E13 --prop text=180
# ==========================================================================
# Sheet: 1-Column Fundamentals
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Column Fundamentals"
# Chart 1: Basic column with dataRange and axis titles
# Features: chartType=column, dataRange, catTitle, axisTitle, axisfont, gridlines, colors
officecli add "$FILE" "/1-Column Fundamentals" --type chart \
--prop chartType=column \
--prop title="Monthly Sales by Region" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop catTitle=Month --prop axisTitle=Revenue \
--prop axisfont=9:58626E:Arial \
--prop gridlines=D9D9D9:0.5:dot \
--prop colors=4472C4,ED7D31,70AD47,FFC000
# Chart 2: Inline series with custom colors and gap width
# Features: inline series (series1=Name:v1,v2,...), colors, gapwidth, legend=bottom
officecli add "$FILE" "/1-Column Fundamentals" --type chart \
--prop chartType=column \
--prop title="Q1 Product Sales" \
--prop series1="Laptops:320,280,350,310" \
--prop series2="Phones:450,420,480,460" \
--prop series3="Tablets:180,160,200,190" \
--prop categories=Jan,Feb,Mar,Apr \
--prop colors=2E75B6,C00000,70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop gapwidth=80 \
--prop legend=bottom
# Chart 3: Dotted syntax with cell ranges
# Features: series.name/values/categories (cell range via dotted syntax), minorGridlines
officecli add "$FILE" "/1-Column Fundamentals" --type chart \
--prop chartType=column \
--prop title="East vs South (Cell Range)" \
--prop series1.name=East \
--prop series1.values=Sheet1!B2:B13 \
--prop series1.categories=Sheet1!A2:A13 \
--prop series2.name=South \
--prop series2.values=Sheet1!C2:C13 \
--prop series2.categories=Sheet1!A2:A13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31 \
--prop gridlines=D9D9D9:0.5:dot \
--prop minorGridlines=EEEEEE:0.3:dot
# Chart 4: data= shorthand format
# Features: data (inline shorthand Name:v1;Name2:v2), legend=right
officecli add "$FILE" "/1-Column Fundamentals" --type chart \
--prop chartType=column \
--prop title="Weekly Output" \
--prop 'data=Team A:85,92,78,95,88;Team B:70,80,85,90,75' \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop colors=0070C0,FF6600 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=right
# ==========================================================================
# Sheet: 2-Column Variants
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Column Variants"
# Chart 1: Stacked column with center data labels and series outline
# Features: columnStacked, dataLabels=center, series.outline
officecli add "$FILE" "/2-Column Variants" --type chart \
--prop chartType=columnStacked \
--prop title="Stacked Sales by Region" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop dataLabels=center \
--prop series.outline=FFFFFF-0.5 \
--prop legend=bottom
# Chart 2: 100% stacked column with axis number format
# Features: columnPercentStacked, axisNumFmt=0%, legend=bottom
officecli add "$FILE" "/2-Column Variants" --type chart \
--prop chartType=columnPercentStacked \
--prop title="Regional Contribution %" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=1F4E79,2E75B6,9DC3E6,BDD7EE \
--prop axisNumFmt=0% \
--prop legend=bottom \
--prop gridlines=E0E0E0:0.5:solid
# Chart 3: 3D column with perspective and style
# Features: column3d, view3d (rotX,rotY,perspective), style (preset 1-48)
officecli add "$FILE" "/2-Column Variants" --type chart \
--prop chartType=column3d \
--prop title="3D Regional Trends" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=15,20,30 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop chartFill=F8F8F8 \
--prop style=3
# Chart 4: 3D stacked column with gap depth
# Features: column3d stacked, gapDepth=200 (3D depth spacing)
officecli add "$FILE" "/2-Column Variants" --type chart \
--prop chartType=column3d \
--prop title="3D Stacked with Gap Depth" \
--prop series1="East:120,135,148,162,155,178" \
--prop series2="South:95,108,115,128,142,155" \
--prop series3="North:88,92,105,118,125,138" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=15,20,30 \
--prop gapDepth=200 \
--prop colors=2E75B6,ED7D31,70AD47 \
--prop legend=right
# ==========================================================================
# Sheet: 3-Column Styling
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Column Styling"
# Chart 1: Title styling — font, size, color, bold
# Features: title.font, title.size, title.color, title.bold
officecli add "$FILE" "/3-Column Styling" --type chart \
--prop chartType=column \
--prop title="Styled Title Demo" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop legend=bottom
# Chart 2: Series shadow and outline effects
# Features: series.shadow (color-blur-angle-dist-opacity), series.outline (color-width)
officecli add "$FILE" "/3-Column Styling" --type chart \
--prop chartType=column \
--prop title="Shadow & Outline Effects" \
--prop series1="Revenue:320,280,350,310,340" \
--prop series2="Cost:210,195,230,220,215" \
--prop categories=Q1,Q2,Q3,Q4,Q5 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4,C00000 \
--prop series.shadow=000000-4-315-2-40 \
--prop series.outline=FFFFFF-0.5 \
--prop gapwidth=100 \
--prop legend=bottom
# Chart 3: Per-series gradient fills
# Features: gradients (per-series gradient fills, start-end:angle)
officecli add "$FILE" "/3-Column Styling" --type chart \
--prop chartType=column \
--prop title="Gradient Columns" \
--prop series1="East:120,135,148,162" \
--prop series2="South:95,108,115,128" \
--prop series3="North:88,92,105,118" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90;70AD47-C5E0B4:90' \
--prop legend=bottom
# Chart 4: Transparency + plotFill gradient + chartFill + roundedCorners
# Features: transparency=30, plotFill gradient, chartFill, roundedCorners
officecli add "$FILE" "/3-Column Styling" --type chart \
--prop chartType=column \
--prop title="Transparent Columns on Gradient" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop transparency=30 \
--prop plotFill=F0F4F8-D6E4F0:90 \
--prop chartFill=FFFFFF \
--prop colors=1F4E79,2E75B6,5B9BD5,9DC3E6 \
--prop roundedCorners=true \
--prop legend=bottom
# ==========================================================================
# Sheet: 4-Axis & Gridlines
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="4-Axis & Gridlines"
# Chart 1: Custom axis scaling — min, max, majorUnit, minorUnit
# Features: axisMin, axisMax, majorUnit, minorUnit, axisLine, catAxisLine
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=column \
--prop title="Custom Axis Scale (50-250)" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop axisMin=50 --prop axisMax=250 --prop majorUnit=50 \
--prop minorUnit=25 \
--prop gridlines=D0D0D0:0.5:solid \
--prop minorGridlines=EEEEEE:0.3:dot \
--prop axisLine=C00000:1.5:solid \
--prop catAxisLine=2E75B6:1.5:solid \
--prop colors=4472C4,ED7D31,70AD47,FFC000
# Chart 2: Logarithmic scale with reversed axis
# Features: logBase=10 (logarithmic scale), axisReverse=true
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=column \
--prop title="Log Scale (Base 10)" \
--prop series1="Growth:1,10,100,1000,5000" \
--prop categories="Year 1,Year 2,Year 3,Year 4,Year 5" \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop logBase=10 \
--prop axisReverse=true \
--prop colors=C00000 \
--prop axisTitle="Value (log)" \
--prop catTitle=Year \
--prop gridlines=E0E0E0:0.5:dash
# Chart 3: Display units and axis number format
# Features: dispUnits=thousands, axisNumFmt=#,##0, majorTickMark=outside, minorTickMark=inside
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=column \
--prop title="Revenue (in Thousands)" \
--prop series1="Revenue:12000,18500,22000,31000,45000,52000" \
--prop series2="Cost:8000,11000,14000,19500,28000,33000" \
--prop categories=2020,2021,2022,2023,2024,2025 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop dispUnits=thousands \
--prop axisNumFmt=#,##0 \
--prop colors=2E75B6,C00000 \
--prop catTitle=Year --prop axisTitle="Amount (K)" \
--prop majorTickMark=outside --prop minorTickMark=inside \
--prop legend=bottom
# Chart 4: Hidden axes with data table
# Features: gridlines=none, axisVisible=false, dataTable=true, legend=none
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=column \
--prop title="Minimal Chart with Data Table" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop gridlines=none \
--prop axisVisible=false \
--prop dataTable=true \
--prop legend=none \
--prop colors=4472C4,ED7D31,70AD47,FFC000
# ==========================================================================
# Sheet: 5-Labels & Legend
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="5-Labels & Legend"
# Chart 1: Data labels with number format and styled label font
# Features: dataLabels=true, labelPos=outsideEnd, labelFont (size:color:bold), dataLabels.numFmt
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=column \
--prop title="Sales with Labels" \
--prop series1="Revenue:120,180,210,250,280" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4 \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop labelFont=9:333333:true \
--prop dataLabels.numFmt=#,##0
# Chart 2: Custom individual labels — delete some, highlight peak
# Features: dataLabel{N}.delete, dataLabel{N}.text, point{N}.color
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=column \
--prop title="Peak Highlight" \
--prop series1="Sales:88,120,165,210,195,178" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=2E75B6 \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop dataLabel1.delete=true --prop dataLabel2.delete=true \
--prop dataLabel3.delete=true \
--prop point4.color=C00000 \
--prop dataLabel4.text=Peak! \
--prop dataLabel5.delete=true --prop dataLabel6.delete=true
# Chart 3: Legend positioning and overlay with styled legend font
# Features: legend=right, legend.overlay=true, legendfont (size:color:fontname), plotFill
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=column \
--prop title="Legend Overlay on Chart" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop legend=right \
--prop legend.overlay=true \
--prop legendfont=10:333333:Calibri \
--prop plotFill=F5F5F5
# Chart 4: Manual layout — plotArea, title, and legend positioning
# Features: plotArea.x/y/w/h, title.x/y, legend.x/y/w/h (manual layout)
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=column \
--prop title="Manual Layout Control" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop plotArea.x=0.12 --prop plotArea.y=0.18 \
--prop plotArea.w=0.82 --prop plotArea.h=0.55 \
--prop title.x=0.25 --prop title.y=0.02 \
--prop legend.x=0.15 --prop legend.y=0.82 \
--prop legend.w=0.7 --prop legend.h=0.12 \
--prop title.font=Arial --prop title.size=13 \
--prop title.bold=true
# ==========================================================================
# Sheet: 6-Effects & Advanced
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="6-Effects & Advanced"
# Chart 1: Secondary axis — dual Y-axis
# Features: secondaryAxis=2 (series 2 on right-hand axis)
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=column \
--prop title="Revenue vs Growth Rate" \
--prop series1="Revenue:120,180,250,310,380,420" \
--prop series2="Growth %:50,33,39,24,23,11" \
--prop categories=2020,2021,2022,2023,2024,2025 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop secondaryAxis=2 \
--prop colors=2E75B6,C00000 \
--prop catTitle=Year --prop axisTitle=Revenue \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop legend=bottom
# Chart 2: Reference line (target/threshold)
# referenceLine format: value:color:width:dash
# Features: referenceLine (horizontal target line)
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=column \
--prop title="vs Target (150)" \
--prop dataRange=Sheet1!A1:C13 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4,70AD47 \
--prop referenceLine=150:FF0000:1.5:dash \
--prop legend=bottom
# Chart 3: Title glow and shadow effects
# Features: title.glow (color-radius-opacity), title.shadow, series.shadow on column charts
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=column \
--prop title="Glow & Shadow Effects" \
--prop series1="East:120,135,148,162,155,178" \
--prop series2="West:110,118,130,145,138,162" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31 \
--prop title.glow=4472C4-8-60 \
--prop title.shadow=000000-3-315-2-40 \
--prop title.font=Calibri --prop title.size=16 \
--prop title.bold=true --prop title.color=1F4E79 \
--prop series.shadow=000000-3-315-1-30 \
--prop plotFill=F0F4F8 --prop chartFill=FFFFFF
# Chart 4: Conditional coloring with chart/plot borders
# colorRule format: threshold:belowColor:aboveColor
# Features: colorRule, chartArea.border, plotArea.border
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=column \
--prop title="Profit: Conditional Colors" \
--prop series1="Profit:80,120,-30,160,-50,200,140,-20,180,90" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=2E75B6 \
--prop colorRule=0:C00000:70AD47 \
--prop referenceLine=0:888888:1:solid \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop labelFont=8:666666:false
# ==========================================================================
# Sheet: 7-Bar Shape & Gap
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="7-Bar Shape & Gap"
# Chart 1: Narrow gap width (bars close together)
# Features: gapwidth=30 (narrow gaps between column groups)
officecli add "$FILE" "/7-Bar Shape & Gap" --type chart \
--prop chartType=column \
--prop title="Narrow Gap (30%)" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop gapwidth=30 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop legend=bottom
# Chart 2: Wide gap with negative overlap (separated bars within group)
# Features: gapwidth=200 (wide gap), overlap=-50 (negative = bars separated)
officecli add "$FILE" "/7-Bar Shape & Gap" --type chart \
--prop chartType=column \
--prop title="Wide Gap + Negative Overlap" \
--prop dataRange=Sheet1!A1:E7 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop gapwidth=200 \
--prop overlap=-50 \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop legend=bottom
# Chart 3: 3D column with cylinder shape
# Features: shape=cylinder (3D column bar shape)
officecli add "$FILE" "/7-Bar Shape & Gap" --type chart \
--prop chartType=column3d \
--prop title="Cylinder Shape" \
--prop series1="East:120,135,148,162,155,178" \
--prop series2="South:95,108,115,128,142,155" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop shape=cylinder \
--prop view3d=15,20,30 \
--prop colors=4472C4,ED7D31 \
--prop legend=bottom
# Chart 4: 3D column with cone/pyramid shapes
# Features: shape=cone (3D column bar shape — also supports pyramid)
officecli add "$FILE" "/7-Bar Shape & Gap" --type chart \
--prop chartType=column3d \
--prop title="Cone Shape" \
--prop series1="North:88,92,105,118,125,138" \
--prop series2="West:110,118,130,145,138,162" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop shape=cone \
--prop view3d=15,20,30 \
--prop colors=70AD47,FFC000 \
--prop legend=bottom
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+152
View File
@@ -0,0 +1,152 @@
# Combo Charts Showcase
This demo consists of three files that work together:
- **charts-combo.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-combo.xlsx** — The generated workbook with 5 sheets (1 default + 4 chart sheets, 16 charts total).
- **charts-combo.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-combo.py
# -> charts-combo.xlsx
```
## Chart Sheets
### Sheet: 1-Combo Fundamentals
Four combo charts covering comboSplit, secondaryAxis, combotypes, and combined usage.
```bash
# Basic combo: 2 bar series + 1 line via comboSplit
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop series1="Revenue:120,145,160,180,195" \
--prop series2="Expenses:90,100,110,115,125" \
--prop series3="Margin %:25,31,31,36,36" \
--prop comboSplit=2 --prop legend=bottom
# Combo with secondary Y-axis for line series
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop comboSplit=1 --prop secondaryAxis=2 \
--prop catTitle=Year --prop axisTitle=Sales ($K)
# Per-series type control via combotypes
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop combotypes=column,column,line,area
# combotypes + secondaryAxis together
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop combotypes=column,column,line \
--prop secondaryAxis=3
```
**Features:** `combo`, `comboSplit`, `secondaryAxis`, `combotypes=column,column,line,area`, `catTitle`, `axisTitle`
### Sheet: 2-Combo Styling
Four styled combo charts with title fonts, gradients, data labels, and chart fills.
```bash
# Title, legend, axis font styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop legendfont=10:333333:Calibri --prop axisfont=9:666666
# Series shadow and gradients
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop 'gradients=1F4E79-5B9BD5:90;C55A11-F4B183:90' \
--prop series.shadow=000000-4-315-2-30
# Data labels on combo series
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=9:333333:true
# Chart area styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop plotFill=F0F4F8 --prop chartFill=FAFAFA \
--prop roundedCorners=true
```
**Features:** `title.font/size/color/bold`, `legendfont`, `axisfont`, `gradients`, `series.shadow`, `dataLabels`, `labelPos`, `labelFont`, `plotFill`, `chartFill`, `roundedCorners`
### Sheet: 3-Combo Advanced
Four advanced combo charts with reference lines, axis scaling, layout, and markers.
```bash
# Reference line and gridlines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop referenceLine=110:Target:C00000 \
--prop gridlines=D9D9D9:0.5
# Axis scaling and display units
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop axisMin=1000000 --prop axisMax=2000000 \
--prop dispUnits=thousands
# Manual plot layout
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop plotLayout=0.1,0.15,0.85,0.75
# Multiple line series with markers
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop comboSplit=1 --prop secondaryAxis=2,3,4 \
--prop markers=circle-6
```
**Features:** `referenceLine`, `gridlines`, `axisMin/Max`, `dispUnits`, `plotLayout`, `markers`, multiple secondary axis series
### Sheet: 4-Combo Effects
Four effect-heavy combo charts with glow, borders, color rules, and complex multi-series.
```bash
# Title glow and shadow effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop title.glow=4472C4-6 \
--prop title.shadow=000000-3-315-2-30
# Chart and plot area borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop chartArea.border=333333-1.5 \
--prop plotArea.border=999999-0.75
# Color rule (conditional bar coloring)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop colorRule=80:C00000:70AD47
# 5-series dashboard with mixed combotypes
officecli add data.xlsx /Sheet --type chart \
--prop chartType=combo \
--prop combotypes=column,column,column,area,line \
--prop secondaryAxis=5
```
**Features:** `title.glow`, `title.shadow`, `chartArea.border`, `plotArea.border`, `colorRule`, 5-series `combotypes`
## Inspect the Generated File
```bash
officecli query charts-combo.xlsx chart
officecli get charts-combo.xlsx "/1-Combo Fundamentals/chart[1]"
```
+337
View File
@@ -0,0 +1,337 @@
#!/usr/bin/env python3
"""
Combo Charts Showcase — column+line, column+area, secondary axes, and styling.
Generates: charts-combo.xlsx
SDK twin of charts-combo.sh (officecli CLI). Both produce an equivalent
charts-combo.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in a single `doc.batch(...)` round-trip.
Each item is the same `{"command","parent"/"path","type","props"}` dict you'd
put in an `officecli batch` list.
16 combo charts across 4 sheets:
1-Combo Fundamentals — comboSplit, secondaryAxis, combotypes per-series
2-Combo Styling — title.font, legendfont, axisfont, gradients, shadow,
dataLabels, plotFill/chartFill, roundedCorners
3-Combo Advanced — referenceLine, gridlines, axisMin/Max, dispUnits,
plotLayout, multi-line markers
4-Combo Effects — title.glow/shadow, chartArea/plotArea border,
colorRule, 5-series dashboard
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-combo.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-combo.xlsx")
def sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
items = [
# ==================================================================
# Sheet: 1-Combo Fundamentals
# ==================================================================
sheet("1-Combo Fundamentals"),
# Chart 1: Basic combo with comboSplit (2 bar series + 1 line)
# Features: chartType=combo, comboSplit=2 (first 2 as bars, rest as lines)
chart("/1-Combo Fundamentals",
chartType="combo",
title="Revenue vs Expenses vs Margin",
series1="Revenue:120,145,160,180,195",
series2="Expenses:90,100,110,115,125",
series3="Margin %:25,31,31,36,36",
categories="Q1,Q2,Q3,Q4,Q5",
comboSplit="2",
colors="4472C4,ED7D31,70AD47",
x="0", y="0", width="12", height="18",
legend="bottom"),
# Chart 2: Combo with secondaryAxis (line on right Y-axis)
# Features: secondaryAxis=2 (series 2 on right Y-axis), catTitle, axisTitle
chart("/1-Combo Fundamentals",
chartType="combo",
title="Sales & Growth Rate",
series1="Sales ($K):320,380,420,510,560",
series2="Growth %:8,19,11,21,10",
categories="2021,2022,2023,2024,2025",
comboSplit="1",
secondaryAxis="2",
colors="2E75B6,C00000",
x="13", y="0", width="12", height="18",
legend="bottom",
catTitle="Year", axisTitle="Sales ($K)"),
# Chart 3: combotypes per-series type control
# Features: combotypes=column,column,line,area (per-series type)
chart("/1-Combo Fundamentals",
chartType="combo",
title="Mixed Series Types",
series1="Product A:50,65,70,80,90",
series2="Product B:40,55,60,72,85",
series3="Trend:48,62,68,78,88",
series4="Forecast:30,40,50,55,65",
categories="Jan,Feb,Mar,Apr,May",
combotypes="column,column,line,area",
colors="4472C4,ED7D31,70AD47,BDD7EE",
x="0", y="19", width="12", height="18",
legend="bottom"),
# Chart 4: combotypes with secondaryAxis
# Features: combotypes + secondaryAxis together
chart("/1-Combo Fundamentals",
chartType="combo",
title="Revenue Mix & Margin",
series1="Domestic:200,220,250,270,300",
series2="Export:80,95,110,130,150",
series3="Net Margin %:18,20,22,24,26",
categories="2021,2022,2023,2024,2025",
combotypes="column,column,line",
secondaryAxis="3",
colors="4472C4,9DC3E6,C00000",
x="13", y="19", width="12", height="18",
legend="bottom",
catTitle="Year"),
# ==================================================================
# Sheet: 2-Combo Styling
# ==================================================================
sheet("2-Combo Styling"),
# Chart 1: Title, legend, axisfont styling
# Features: title.font/size/color/bold, legendfont, axisfont
chart("/2-Combo Styling",
chartType="combo",
title="Styled Combo Chart",
series1="Revenue:150,175,200,220",
series2="COGS:100,110,130,140",
series3="Profit %:33,37,35,36",
categories="Q1,Q2,Q3,Q4",
comboSplit="2",
colors="1F4E79,5B9BD5,70AD47",
x="0", y="0", width="12", height="18",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"},
legend="bottom", legendfont="10:333333:Calibri",
axisfont="9:666666"),
# Chart 2: Series shadow, gradients
# Features: gradients (per-bar-series), series.shadow
chart("/2-Combo Styling",
chartType="combo",
title="Gradient & Shadow Effects",
series1="Actual:85,92,105,120,135",
series2="Budget:80,90,100,110,120",
series3="Variance:5,2,5,10,15",
categories="Jan,Feb,Mar,Apr,May",
comboSplit="2",
x="13", y="0", width="12", height="18",
gradients="1F4E79-5B9BD5:90;C55A11-F4B183:90",
**{"series.shadow": "000000-4-315-2-30"},
legend="bottom"),
# Chart 3: dataLabels on line series
# Features: dataLabels=true, labelPos=top, labelFont
chart("/2-Combo Styling",
chartType="combo",
title="Data Labels on Lines",
series1="Units:500,620,710,800",
series2="Avg Price:45,48,52,55",
categories="Q1,Q2,Q3,Q4",
comboSplit="1",
secondaryAxis="2",
colors="4472C4,ED7D31",
x="0", y="19", width="12", height="18",
dataLabels="true", labelPos="top",
labelFont="9:333333:true",
legend="bottom"),
# Chart 4: plotFill, chartFill, roundedCorners
# Features: plotFill, chartFill, roundedCorners
chart("/2-Combo Styling",
chartType="combo",
title="Chart Area Styling",
series1="Online:180,210,240,260,290",
series2="Retail:150,140,135,130,120",
series3="Growth %:5,12,15,10,12",
categories="2021,2022,2023,2024,2025",
comboSplit="2",
colors="2E75B6,ED7D31,70AD47",
x="13", y="19", width="12", height="18",
plotFill="F0F4F8", chartFill="FAFAFA",
roundedCorners="true",
legend="bottom"),
# ==================================================================
# Sheet: 3-Combo Advanced
# ==================================================================
sheet("3-Combo Advanced"),
# Chart 1: referenceLine, gridlines
# Features: referenceLine=value:label:color, gridlines
chart("/3-Combo Advanced",
chartType="combo",
title="Target Reference Line",
series1="Actual:95,105,115,125,130",
series2="Forecast:90,100,110,120,130",
categories="Jan,Feb,Mar,Apr,May",
comboSplit="1",
colors="4472C4,BDD7EE",
x="0", y="0", width="12", height="18",
referenceLine="110:C00000:Target",
gridlines="D9D9D9:0.5",
legend="bottom"),
# Chart 2: axisMin/Max, dispUnits
# Features: axisMin/Max, dispUnits=thousands
chart("/3-Combo Advanced",
chartType="combo",
title="Axis Scaling & Units",
series1="Revenue:1200000,1450000,1600000,1800000",
series2="Profit %:18,22,25,28",
categories="2022,2023,2024,2025",
comboSplit="1",
secondaryAxis="2",
colors="2E75B6,70AD47",
x="13", y="0", width="12", height="18",
axisMin="1000000", axisMax="2000000",
dispUnits="thousands",
legend="bottom"),
# Chart 3: Manual layout
# Features: plotLayout=left,top,width,height (manual plot area)
chart("/3-Combo Advanced",
chartType="combo",
title="Manual Layout",
series1="Plan:100,120,140,160",
series2="Actual:95,125,135,170",
series3="Delta %:-5,4,-4,6",
categories="Q1,Q2,Q3,Q4",
comboSplit="2",
secondaryAxis="3",
colors="4472C4,ED7D31,70AD47",
x="0", y="19", width="12", height="18",
plotLayout="0.1,0.15,0.85,0.75",
legend="bottom"),
# Chart 4: Multiple line series with markers + bar series
# Features: multiple line series on secondary axis, markers
chart("/3-Combo Advanced",
chartType="combo",
title="Multi-Line with Markers",
series1="Units Sold:800,920,1050,1200,1350",
series2="North:30,35,38,42,45",
series3="South:25,28,32,36,40",
series4="West:20,24,28,32,35",
categories="Q1,Q2,Q3,Q4,Q5",
comboSplit="1",
secondaryAxis="2,3,4",
colors="4472C4,C00000,70AD47,FFC000",
x="13", y="19", width="12", height="18",
markers="circle-6",
legend="bottom"),
# ==================================================================
# Sheet: 4-Combo Effects
# ==================================================================
sheet("4-Combo Effects"),
# Chart 1: title.glow, title.shadow
# Features: title.glow=color-radius, title.shadow
chart("/4-Combo Effects",
chartType="combo",
title="Glowing Title",
series1="Metric A:60,72,85,90,100",
series2="Metric B:40,50,55,62,70",
series3="Ratio:67,69,65,69,70",
categories="W1,W2,W3,W4,W5",
comboSplit="2",
colors="4472C4,ED7D31,70AD47",
x="0", y="0", width="12", height="18",
**{"title.glow": "4472C4-6",
"title.shadow": "000000-3-315-2-30"},
legend="bottom"),
# Chart 2: chartArea.border, plotArea.border
# Features: chartArea.border=color-width, plotArea.border
chart("/4-Combo Effects",
chartType="combo",
title="Bordered Areas",
series1="Income:250,280,310,340",
series2="Costs:180,195,210,225",
series3="Margin %:28,30,32,34",
categories="Q1,Q2,Q3,Q4",
comboSplit="2",
colors="2E75B6,ED7D31,548235",
x="13", y="0", width="12", height="18",
**{"chartArea.border": "333333:1.5",
"plotArea.border": "999999:0.75"},
legend="bottom"),
# Chart 3: colorRule
# Features: colorRule=threshold:belowColor:aboveColor
chart("/4-Combo Effects",
chartType="combo",
title="Color Rule Combo",
series1="Performance:72,85,65,90,78",
series2="Target:80,80,80,80,80",
categories="Team A,Team B,Team C,Team D,Team E",
comboSplit="1",
colors="4472C4,C00000",
x="0", y="19", width="12", height="18",
colorRule="80:C00000:70AD47",
legend="bottom"),
# Chart 4: Complex combo with 5+ series
# Features: 5 series, mixed combotypes, secondary axis
chart("/4-Combo Effects",
chartType="combo",
title="Full Business Dashboard",
series1="Revenue:500,550,600,650,700",
series2="COGS:300,320,340,360,380",
series3="OpEx:100,105,110,115,120",
series4="Net Income:100,125,150,175,200",
series5="Margin %:20,23,25,27,29",
categories="2021,2022,2023,2024,2025",
combotypes="column,column,column,area,line",
secondaryAxis="5",
colors="4472C4,ED7D31,A5A5A5,BDD7EE,C00000",
x="13", y="19", width="12", height="18",
legend="bottom",
gridlines="E0E0E0:0.5"),
# Remove blank default Sheet1 (all data is inline)
{"command": "remove", "path": "/Sheet1"},
]
doc.batch(items)
print(f" added {len(items)} sheets/charts/ops")
doc.send({"command": "save"})
print(f"Generated: {FILE}")
print(" 4 chart sheets, 16 charts total")
+293
View File
@@ -0,0 +1,293 @@
#!/bin/bash
# Combo Charts Showcase — column+line, column+area, secondary axes, and styling.
# Generates: charts-combo.xlsx (16 combo charts across 4 sheets)
# CLI twin of charts-combo.py (officecli Python SDK).
# Usage: ./charts-combo.sh
# 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")/charts-combo.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet: 1-Combo Fundamentals
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Combo Fundamentals"
# Chart 1: Basic combo with comboSplit (2 bar series + 1 line)
# Features: chartType=combo, comboSplit=2 (first 2 as bars, rest as lines)
officecli add "$FILE" "/1-Combo Fundamentals" --type chart \
--prop chartType=combo \
--prop title="Revenue vs Expenses vs Margin" \
--prop series1="Revenue:120,145,160,180,195" \
--prop series2="Expenses:90,100,110,115,125" \
--prop series3="Margin %:25,31,31,36,36" \
--prop categories=Q1,Q2,Q3,Q4,Q5 \
--prop comboSplit=2 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=bottom
# Chart 2: Combo with secondaryAxis (line on right Y-axis)
# Features: secondaryAxis=2 (series 2 on right Y-axis), catTitle, axisTitle
officecli add "$FILE" "/1-Combo Fundamentals" --type chart \
--prop chartType=combo \
--prop title="Sales & Growth Rate" \
--prop series1="Sales ($K):320,380,420,510,560" \
--prop series2="Growth %:8,19,11,21,10" \
--prop categories=2021,2022,2023,2024,2025 \
--prop comboSplit=1 \
--prop secondaryAxis=2 \
--prop colors=2E75B6,C00000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop catTitle=Year --prop axisTitle="Sales ($K)"
# Chart 3: combotypes per-series type control
# Features: combotypes=column,column,line,area (per-series type)
officecli add "$FILE" "/1-Combo Fundamentals" --type chart \
--prop chartType=combo \
--prop title="Mixed Series Types" \
--prop series1="Product A:50,65,70,80,90" \
--prop series2="Product B:40,55,60,72,85" \
--prop series3="Trend:48,62,68,78,88" \
--prop series4="Forecast:30,40,50,55,65" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop combotypes=column,column,line,area \
--prop colors=4472C4,ED7D31,70AD47,BDD7EE \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom
# Chart 4: combotypes with secondaryAxis
# Features: combotypes + secondaryAxis together
officecli add "$FILE" "/1-Combo Fundamentals" --type chart \
--prop chartType=combo \
--prop title="Revenue Mix & Margin" \
--prop series1="Domestic:200,220,250,270,300" \
--prop series2="Export:80,95,110,130,150" \
--prop series3="Net Margin %:18,20,22,24,26" \
--prop categories=2021,2022,2023,2024,2025 \
--prop combotypes=column,column,line \
--prop secondaryAxis=3 \
--prop colors=4472C4,9DC3E6,C00000 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop catTitle=Year
# ==========================================================================
# Sheet: 2-Combo Styling
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Combo Styling"
# Chart 1: Title, legend, axisfont styling
# Features: title.font/size/color/bold, legendfont, axisfont
officecli add "$FILE" "/2-Combo Styling" --type chart \
--prop chartType=combo \
--prop title="Styled Combo Chart" \
--prop series1="Revenue:150,175,200,220" \
--prop series2="COGS:100,110,130,140" \
--prop series3="Profit %:33,37,35,36" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop comboSplit=2 \
--prop colors=1F4E79,5B9BD5,70AD47 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop legend=bottom --prop legendfont=10:333333:Calibri \
--prop axisfont=9:666666
# Chart 2: Series shadow, gradients
# Features: gradients (per-bar-series), series.shadow
officecli add "$FILE" "/2-Combo Styling" --type chart \
--prop chartType=combo \
--prop title="Gradient & Shadow Effects" \
--prop series1="Actual:85,92,105,120,135" \
--prop series2="Budget:80,90,100,110,120" \
--prop series3="Variance:5,2,5,10,15" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop comboSplit=2 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop gradients="1F4E79-5B9BD5:90;C55A11-F4B183:90" \
--prop series.shadow=000000-4-315-2-30 \
--prop legend=bottom
# Chart 3: dataLabels on line series
# Features: dataLabels=true, labelPos=top, labelFont
officecli add "$FILE" "/2-Combo Styling" --type chart \
--prop chartType=combo \
--prop title="Data Labels on Lines" \
--prop series1="Units:500,620,710,800" \
--prop series2="Avg Price:45,48,52,55" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop comboSplit=1 \
--prop secondaryAxis=2 \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=9:333333:true \
--prop legend=bottom
# Chart 4: plotFill, chartFill, roundedCorners
# Features: plotFill, chartFill, roundedCorners
officecli add "$FILE" "/2-Combo Styling" --type chart \
--prop chartType=combo \
--prop title="Chart Area Styling" \
--prop series1="Online:180,210,240,260,290" \
--prop series2="Retail:150,140,135,130,120" \
--prop series3="Growth %:5,12,15,10,12" \
--prop categories=2021,2022,2023,2024,2025 \
--prop comboSplit=2 \
--prop colors=2E75B6,ED7D31,70AD47 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop plotFill=F0F4F8 --prop chartFill=FAFAFA \
--prop roundedCorners=true \
--prop legend=bottom
# ==========================================================================
# Sheet: 3-Combo Advanced
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Combo Advanced"
# Chart 1: referenceLine, gridlines
# Features: referenceLine=value:label:color, gridlines
officecli add "$FILE" "/3-Combo Advanced" --type chart \
--prop chartType=combo \
--prop title="Target Reference Line" \
--prop series1="Actual:95,105,115,125,130" \
--prop series2="Forecast:90,100,110,120,130" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop comboSplit=1 \
--prop colors=4472C4,BDD7EE \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop referenceLine=110:C00000:Target \
--prop gridlines=D9D9D9:0.5 \
--prop legend=bottom
# Chart 2: axisMin/Max, dispUnits
# Features: axisMin/Max, dispUnits=thousands
officecli add "$FILE" "/3-Combo Advanced" --type chart \
--prop chartType=combo \
--prop title="Axis Scaling & Units" \
--prop series1="Revenue:1200000,1450000,1600000,1800000" \
--prop series2="Profit %:18,22,25,28" \
--prop categories=2022,2023,2024,2025 \
--prop comboSplit=1 \
--prop secondaryAxis=2 \
--prop colors=2E75B6,70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop axisMin=1000000 --prop axisMax=2000000 \
--prop dispUnits=thousands \
--prop legend=bottom
# Chart 3: Manual layout
# Features: plotLayout=left,top,width,height (manual plot area)
officecli add "$FILE" "/3-Combo Advanced" --type chart \
--prop chartType=combo \
--prop title="Manual Layout" \
--prop series1="Plan:100,120,140,160" \
--prop series2="Actual:95,125,135,170" \
--prop series3="Delta %:-5,4,-4,6" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop comboSplit=2 \
--prop secondaryAxis=3 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop plotLayout=0.1,0.15,0.85,0.75 \
--prop legend=bottom
# Chart 4: Multiple line series with markers + bar series
# Features: multiple line series on secondary axis, markers
officecli add "$FILE" "/3-Combo Advanced" --type chart \
--prop chartType=combo \
--prop title="Multi-Line with Markers" \
--prop series1="Units Sold:800,920,1050,1200,1350" \
--prop series2="North:30,35,38,42,45" \
--prop series3="South:25,28,32,36,40" \
--prop series4="West:20,24,28,32,35" \
--prop categories=Q1,Q2,Q3,Q4,Q5 \
--prop comboSplit=1 \
--prop secondaryAxis=2,3,4 \
--prop colors=4472C4,C00000,70AD47,FFC000 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop markers=circle-6 \
--prop legend=bottom
# ==========================================================================
# Sheet: 4-Combo Effects
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="4-Combo Effects"
# Chart 1: title.glow, title.shadow
# Features: title.glow=color-radius, title.shadow
officecli add "$FILE" "/4-Combo Effects" --type chart \
--prop chartType=combo \
--prop title="Glowing Title" \
--prop series1="Metric A:60,72,85,90,100" \
--prop series2="Metric B:40,50,55,62,70" \
--prop series3="Ratio:67,69,65,69,70" \
--prop categories=W1,W2,W3,W4,W5 \
--prop comboSplit=2 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.glow=4472C4-6 \
--prop title.shadow=000000-3-315-2-30 \
--prop legend=bottom
# Chart 2: chartArea.border, plotArea.border
# Features: chartArea.border=color-width, plotArea.border
officecli add "$FILE" "/4-Combo Effects" --type chart \
--prop chartType=combo \
--prop title="Bordered Areas" \
--prop series1="Income:250,280,310,340" \
--prop series2="Costs:180,195,210,225" \
--prop series3="Margin %:28,30,32,34" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop comboSplit=2 \
--prop colors=2E75B6,ED7D31,548235 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop chartArea.border=333333:1.5 \
--prop plotArea.border=999999:0.75 \
--prop legend=bottom
# Chart 3: colorRule
# Features: colorRule=threshold:belowColor:aboveColor
officecli add "$FILE" "/4-Combo Effects" --type chart \
--prop chartType=combo \
--prop title="Color Rule Combo" \
--prop series1="Performance:72,85,65,90,78" \
--prop series2="Target:80,80,80,80,80" \
--prop categories="Team A,Team B,Team C,Team D,Team E" \
--prop comboSplit=1 \
--prop colors=4472C4,C00000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colorRule=80:C00000:70AD47 \
--prop legend=bottom
# Chart 4: Complex combo with 5+ series
# Features: 5 series, mixed combotypes, secondary axis
officecli add "$FILE" "/4-Combo Effects" --type chart \
--prop chartType=combo \
--prop title="Full Business Dashboard" \
--prop series1="Revenue:500,550,600,650,700" \
--prop series2="COGS:300,320,340,360,380" \
--prop series3="OpEx:100,105,110,115,120" \
--prop series4="Net Income:100,125,150,175,200" \
--prop series5="Margin %:20,23,25,27,29" \
--prop categories=2021,2022,2023,2024,2025 \
--prop combotypes=column,column,column,area,line \
--prop secondaryAxis=5 \
--prop colors=4472C4,ED7D31,A5A5A5,BDD7EE,C00000 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop gridlines=E0E0E0:0.5
# Remove blank default Sheet1 (all data is inline)
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+334
View File
@@ -0,0 +1,334 @@
# Extended Chart Types Showcase
This demo consists of three files that work together:
- **charts-extended.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-extended.xlsx** — The generated workbook: 5 sheets, 18 charts, covering every property supported by the cx:chart family (waterfall, funnel, treemap, sunburst, histogram, boxWhisker) plus chart-meta properties (anchor, preset, autotitledeleted, plotvisonly).
- **charts-extended.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-extended.py
# → charts-extended.xlsx
```
## Feature Coverage Summary
Every extended-chart-specific knob is exercised by at least one chart:
| Chart type | Specific knobs | Covered by |
|---|---|---|
| waterfall | `increaseColor`, `decreaseColor`, `totalColor`, `chartFill`, `labelFont` | Sheet 1, Chart 12 |
| funnel | (generic styling only) | Sheet 1, Chart 34 |
| pareto | auto-sort desc, `ownerIdx` cumulative-% line, secondary % axis | Sheet 4, Chart 12 |
| treemap | `parentLabelLayout` = `overlapping` / `banner` / `none` | Sheet 2, Chart 1/2/3 |
| sunburst | (generic styling only) | Sheet 2, Chart 4 |
| histogram | `binCount`, `binSize`, `intervalClosed` = `r` / `l`, `underflowBin`, `overflowBin` | Sheet 3, Chart 14 |
| boxWhisker | `quartileMethod` = `exclusive` / `inclusive` | Sheet 3, Chart 56 |
| (all types) | `anchor`, `preset`, `autotitledeleted`, `plotvisonly` | Sheet 5, Chart 14 |
Generic cx styling exercised across the deck: `title.glow`, `title.shadow`, `title.bold`/`size`/`color`, `dataLabels`, `labelFont`, `legend` position, `legendfont`, `axisfont`, `colors` palette, `chartFill`, `plotFill`.
> **Notes on cx:chart styling:**
>
> - `chartFill` / `plotFill` accept a solid hex color, `none`, or a gradient — the same `C1-C2:angle` (and `c1,c2` stop-list) syntax as regular cChart.
> - `colors=` palette works **per-data-point** on single-series cx charts (funnel, treemap, sunburst): each segment gets the next palette color (emitted as `cx:dataPt` fills, cycling if there are more points than colors). On multi-series cx charts (boxWhisker) `colors=` is one color per series, as on regular cCharts.
---
## Sheet: 1-Waterfall & Funnel
Two waterfall charts (financial bridges) and two funnel charts (pipelines).
```bash
# Chart 1 — waterfall with increase/decrease/total colors + data labels + title glow
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
--prop chartType=waterfall \
--prop title="Cash Flow Bridge" \
--prop data="Start:1000,Revenue:500,Costs:-300,Tax:-100,Net:1100" \
--prop increaseColor=70AD47 --prop decreaseColor=FF0000 --prop totalColor=4472C4 \
--prop dataLabels=true \
--prop title.glow="00D2FF-6-60"
# Chart 2 — waterfall with legend + chartFill (solid) + custom label font
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
--prop chartType=waterfall \
--prop title="Budget vs Actual" \
--prop data="Budget:5000,Sales:2000,Marketing:-800,Ops:-600,Net:5600" \
--prop increaseColor=2E75B6 --prop decreaseColor=C00000 --prop totalColor=FFC000 \
--prop legend=bottom \
--prop chartFill=F0F4FA \
--prop dataLabels=true \
--prop labelFont="9:333333:true"
# Chart 3 — funnel (sales pipeline) with title shadow
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
--prop chartType=funnel \
--prop title="Sales Pipeline" \
--prop series1="Pipeline:1200,850,600,300,120" \
--prop categories=Leads,Qualified,Proposal,Negotiation,Won \
--prop dataLabels=true \
--prop title.shadow="000000-4-45-2-40"
# Chart 4 — funnel (marketing) with custom colors palette, legend/axis fonts
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
--prop chartType=funnel \
--prop title="Marketing Funnel" \
--prop series1="Users:10000,6500,3200,1800,900,450" \
--prop categories=Impressions,Clicks,Signups,Active,Paying,Retained \
--prop dataLabels=true \
--prop legendfont="9:8B949E:Helvetica Neue" \
--prop axisfont="10:58626E:Helvetica Neue"
```
**Features:** `chartType=waterfall`, `increaseColor`, `decreaseColor`, `totalColor`, `chartType=funnel`, descending pipeline values, `dataLabels`, `title.glow`, `title.shadow`, `legend=bottom`, `chartFill` (solid hex), `labelFont`, `colors` palette, `legendfont`, `axisfont`.
---
## Sheet: 2-Treemap & Sunburst
Three treemaps (one per `parentLabelLayout` value) and one sunburst.
```bash
# Chart 1 — treemap with parentLabelLayout=overlapping + dataLabels
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
--prop chartType=treemap \
--prop title="Revenue by Product" \
--prop series1="Revenue:450,380,310,280,210,180,150,120" \
--prop categories=Laptops,Phones,Tablets,TVs,Cameras,Audio,Gaming,Wearables \
--prop parentLabelLayout=overlapping \
--prop dataLabels=true
# Chart 2 — treemap with parentLabelLayout=banner + title styling
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
--prop chartType=treemap \
--prop title="Department Budget" \
--prop series1="Budget:900,750,600,500,420,350,280" \
--prop categories=Engineering,Sales,Marketing,Support,Finance,HR,Legal \
--prop parentLabelLayout=banner \
--prop title.bold=true --prop title.size=14 --prop title.color=2E5090
# Chart 3 — treemap with parentLabelLayout=none (flat, no parent header strip)
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
--prop chartType=treemap \
--prop title="Flat Treemap (no parent labels)" \
--prop series1="Units:250,200,180,160,140,120,100,80,60,40" \
--prop categories=A,B,C,D,E,F,G,H,I,J \
--prop parentLabelLayout=none \
--prop dataLabels=true
# Chart 4 — sunburst with chartFill + plotFill (solid) + colors palette
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
--prop chartType=sunburst \
--prop title="Market Share by Region" \
--prop series1="Share:35,25,20,15,30,25,20,10,15" \
--prop categories=North,South,East,West,Urban,Suburban,Rural,Online,Retail \
--prop chartFill=F8FAFC --prop plotFill=FFFFFF \
--prop dataLabels=true
```
**Features:** `chartType=treemap`, `parentLabelLayout=overlapping`, `parentLabelLayout=banner`, `parentLabelLayout=none`, `chartType=sunburst`, radial hierarchical layout, `colors` palette, `title.bold`/`size`/`color`, `dataLabels`, `chartFill` + `plotFill` (solid).
---
## Sheet: 3-Histogram & BoxWhisker
Four histograms covering every binning knob, and two box-and-whisker charts (one per quartile method).
```bash
# Chart 1 — histogram with auto-binning (no binCount/binSize)
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Test Scores (auto bins)" \
--prop series1="Scores:45,52,58,61,63,...,95,97,99"
# Chart 2 — histogram with explicit binCount=5 + title glow
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Sales (binCount=5)" \
--prop series1="Sales:120,135,...,620,700" \
--prop binCount=5 \
--prop title.glow="FFC000-6-50"
# Chart 3 — histogram with explicit binSize=50 (fixed bin width) + label font
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Sales (binSize=50)" \
--prop series1="Sales:120,135,...,620,700" \
--prop binSize=50 \
--prop dataLabels=true --prop labelFont="9:FFFFFF:true"
# Chart 4 — histogram with underflowBin + overflowBin + intervalClosed=l
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Response Time (outlier bins)" \
--prop series1="ms:40,55,68,75,...,220,280,350" \
--prop underflowBin=60 \
--prop overflowBin=200 \
--prop intervalClosed=l \
--prop dataLabels=true \
--prop legend=none
# Chart 5 — box & whisker, two teams, quartileMethod=exclusive
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=boxWhisker \
--prop title="Response Time by Team (ms)" \
--prop series1="TeamA:42,55,...,105,120" \
--prop series2="TeamB:30,38,...,92,110" \
--prop quartileMethod=exclusive \
--prop legend=bottom
# Chart 6 — box & whisker, three departments, quartileMethod=inclusive + title glow
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=boxWhisker \
--prop title="Salary Distribution (\$k)" \
--prop series1="Engineering:85,92,...,150,180" \
--prop series2="Marketing:60,65,...,98,110" \
--prop series3="Sales:55,62,...,160,190" \
--prop quartileMethod=inclusive \
--prop title.glow="00D2FF-6-60" \
--prop legend=bottom
```
**Features:** `chartType=histogram`, auto-binning, `binCount` (explicit count), `binSize` (explicit width — mutually exclusive with `binCount`), `underflowBin` (cutoff for `<N`), `overflowBin` (cutoff for `>N`), `intervalClosed=r` (default, `(a,b]`) vs `intervalClosed=l` (`[a,b)`), `chartType=boxWhisker`, `quartileMethod=exclusive`, `quartileMethod=inclusive`, multi-series grouping (2 or 3), `title.glow`, `legend=bottom`, `legend=none`, `labelFont`, `dataLabels`.
---
## Sheet: 4-Pareto
Two Pareto charts demonstrating automatic descending sort and cumulative-% overlay line.
```bash
# Chart 1 — categorical Pareto (defect analysis), pre-sorted input
officecli add charts-extended.xlsx "/4-Pareto" --type chart \
--prop chartType=pareto \
--prop title="Defect Pareto" \
--prop series1="Count:45,30,10,8,5,2" \
--prop categories=Scratches,Dents,Cracks,Chips,Stains,Other \
--prop dataLabels=true
# Chart 2 — Pareto with out-of-order input (auto-sorted desc by officecli)
officecli add charts-extended.xlsx "/4-Pareto" --type chart \
--prop chartType=pareto \
--prop title="Root Cause Pareto" \
--prop series1="Tickets:12,87,5,45,3,120,22,67,8,31" \
--prop categories=Network,Auth,DB,Cache,UI,Config,Deploy,Monitor,Queue,Storage \
--prop title.glow="FFC000-6-50" \
--prop legend=bottom
```
**Features:** `chartType=pareto`, automatic descending sort of values + categories, cumulative-% overlay line on secondary 0-100% axis (auto-generated via `ownerIdx`), `dataLabels`, `title.glow`, `legend=bottom`. Input is a SINGLE user series; officecli synthesizes the 2-series structure internally (clusteredColumn bars + paretoLine with `ownerIdx="0"` + secondary percentage axis).
---
## Sheet: 5-Chart Meta
Four charts demonstrating chart-level meta properties: cell-range anchor placement, named style presets, and display-control flags.
```bash
# anchor (cell-range placement) + preset=corporate
officecli add charts-extended.xlsx "/5-Chart Meta" --type chart \
--prop chartType=column \
--prop title="anchor + preset=corporate" \
--prop series1="Revenue:120,145,132,160" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop anchor="A1:M20" \
--prop preset=corporate
# autotitledeleted + plotvisonly
officecli add charts-extended.xlsx "/5-Chart Meta" --type chart \
--prop chartType=bar \
--prop series1="Sales:80,95,88,110" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=0 --prop y=22 --prop width=12 --prop height=18 \
--prop autotitledeleted=true \
--prop plotvisonly=true
# preset=minimal
officecli add charts-extended.xlsx "/5-Chart Meta" --type chart \
--prop chartType=line \
--prop title="preset=minimal" \
--prop series1="A:10,20,15,25" \
--prop series2="B:8,14,12,20" \
--prop categories=W1,W2,W3,W4 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop preset=minimal
# preset=dark
officecli add charts-extended.xlsx "/5-Chart Meta" --type chart \
--prop chartType=column \
--prop title="preset=dark" \
--prop series1="Sales:45,60,55,80" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=13 --prop y=22 --prop width=12 --prop height=18 \
--prop preset=dark
```
**Features:** `anchor="A1:M20"` (position chart at exact cell-range two-cell anchor instead of `x`/`y`/`width`/`height`), `preset=corporate` (named style bundle — sets colors, fonts, fill, border in one shot; values: `minimal`, `dark`, `corporate`, `magazine`, `dashboard`, `colorful`, `monochrome`), `autotitledeleted=true` (suppress the auto "Chart Title" placeholder that Excel inserts when no `title=` is given), `plotvisonly=true` (skip plotting data in hidden rows/columns — mirrors Excel's "Show data in hidden rows and columns" unchecked)
---
## Property Reference
| Property | Applies to | Example value | Sheet |
|---|---|---|---|
| `chartType=waterfall` | waterfall | `waterfall` | 1 |
| `chartType=funnel` | funnel | `funnel` | 1 |
| `chartType=treemap` | treemap | `treemap` | 2 |
| `chartType=sunburst` | sunburst | `sunburst` | 2 |
| `chartType=histogram` | histogram | `histogram` | 3 |
| `chartType=boxWhisker` | boxWhisker | `boxWhisker` | 3 |
| `chartType=pareto` | pareto | `pareto` | 4 |
| `data=` name:value pairs | waterfall | `Start:1000,Revenue:500,...` | 1 |
| `increaseColor` | waterfall | `70AD47` | 1 |
| `decreaseColor` | waterfall | `FF0000` | 1 |
| `totalColor` | waterfall | `4472C4` | 1 |
| `series1=Name:values`, `series2=...`, `series3=...` | all cx | `TeamA:42,55,...` | 1/2/3 |
| `categories` | all cx except histogram | `Leads,Qualified,...` | 1/2 |
| `parentLabelLayout` | treemap | `overlapping` \| `banner` \| `none` | 2 |
| `binCount` | histogram | `5` | 3 |
| `binSize` | histogram | `50` | 3 |
| `intervalClosed` | histogram | `r` (default) \| `l` | 3 |
| `underflowBin` | histogram | `60` | 3 |
| `overflowBin` | histogram | `200` | 3 |
| `quartileMethod` | boxWhisker | `exclusive` \| `inclusive` | 3 |
| `dataLabels` | all cx | `true` | 1/2/3 |
| `labelFont` | all cx | `"9:FFFFFF:true"` | 1/3 |
| `title.glow` | all cx | `"00D2FF-6-60"` | 1/3 |
| `title.shadow` | all cx | `"000000-4-45-2-40"` | 1 |
| `title.bold`/`size`/`color` | all cx | `true` / `14` / `2E5090` | 2 |
| `legend` | all cx | `bottom` \| `none` | 1/3 |
| `legendfont` | all cx | `"9:8B949E:Helvetica Neue"` | 1 |
| `axisfont` | all cx | `"10:58626E:Helvetica Neue"` | 1 |
| `colors` | per-point on single-series cx (funnel/treemap/sunburst); per-series on multi-series cx | `4472C4,5B9BD5,...` | — |
| `chartFill` (solid only) | all cx | `F8FAFC` | 1/2 |
| `plotFill` (solid only) | all cx | `FFFFFF` | 2 |
| `anchor` | all chart types | `"A1:M20"` | 5 |
| `preset` | all chart types | `minimal` \| `dark` \| `corporate` \| `magazine` \| `dashboard` \| `colorful` \| `monochrome` | 5 |
| `autotitledeleted` | all chart types | `true` | 5 |
| `plotvisonly` | all chart types | `true` | 5 |
---
## Known Validation Warning
`officecli validate charts-extended.xlsx` reports schema warnings on histogram charts' `binCount` / `binSize` elements:
```
[Schema] The element '...:binCount' has invalid value ''. The text value cannot be empty.
[Schema] The 'val' attribute is not declared.
```
This is expected. The Open XML SDK's generated schema models `cx:binCount` as a text-valued leaf (`<binCount>5</binCount>`), but **real Excel writes and requires** the attribute form (`<binCount val="5"/>`). OfficeCLI writes the Excel-compatible form via a raw unknown element; the SDK validator then complains. See `ChartExBuilder.cs:793801` for the rationale. Files open and render correctly in Excel.
---
## Inspect the Generated File
```bash
officecli query charts-extended.xlsx chart
officecli get charts-extended.xlsx "/1-Waterfall & Funnel/chart[1]"
officecli view charts-extended.xlsx outline
```
+414
View File
@@ -0,0 +1,414 @@
#!/usr/bin/env python3
"""
Extended Chart Types Showcase — full feature coverage for waterfall, funnel,
treemap, sunburst, histogram, boxWhisker (cx:chart family) plus pareto and
chart-meta knobs (anchor, preset, autotitledeleted, plotvisonly).
Covers every extended-chart-specific property plus representative generic
cx styling knobs (title.glow, chartFill, legendfont, dataLabels...).
SDK twin of charts-extended.sh (officecli CLI). Both produce an equivalent
charts-extended.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in `doc.batch(...)` round-trips. Each
item is the same `{"command","parent","type","props"}` dict you'd put in an
`officecli batch` list.
Generates: charts-extended.xlsx
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-extended.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-extended.xlsx")
def sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape (parent is the sheet path)."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet 1: Waterfall & Funnel
# ======================================================================
print("--- 1-Waterfall & Funnel ---")
S1 = "/1-Waterfall & Funnel"
items = [sheet("1-Waterfall & Funnel")]
# ------------------------------------------------------------------
# Chart 1: Waterfall — increase/decrease/total colors + data labels + title glow
# Features: chartType=waterfall, increaseColor, decreaseColor, totalColor,
# dataLabels, title.glow
# ------------------------------------------------------------------
items.append(chart(S1,
chartType="waterfall",
title="Cash Flow Bridge",
data="Start:1000,Revenue:500,Costs:-300,Tax:-100,Net:1100",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
dataLabels="true",
**{"title.glow": "00D2FF-6-60"},
x="0", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 2: Waterfall — chart-area fill + legend + custom label font
# Features: waterfall with legend=bottom, chartFill (solid hex — cx charts
# don't support gradient fills, use plain RGB), labelFont "size:color:bold"
# ------------------------------------------------------------------
items.append(chart(S1,
chartType="waterfall",
title="Budget vs Actual",
data="Budget:5000,Sales:2000,Marketing:-800,Ops:-600,Net:5600",
increaseColor="2E75B6",
decreaseColor="C00000",
totalColor="FFC000",
legend="bottom",
chartFill="F0F4FA",
dataLabels="true",
labelFont="9:333333:true",
x="14", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 3: Funnel — sales pipeline with title shadow
# Features: chartType=funnel, descending pipeline values, dataLabels,
# title.shadow "COLOR-BLUR-ANGLE-DIST-OPACITY"
# ------------------------------------------------------------------
items.append(chart(S1,
chartType="funnel",
title="Sales Pipeline",
series1="Pipeline:1200,850,600,300,120",
categories="Leads,Qualified,Proposal,Negotiation,Won",
dataLabels="true",
**{"title.shadow": "000000-4-45-2-40"},
x="0", y="19", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 4: Funnel — marketing conversion + legend/axis fonts
# Features: funnel, legendfont "size:color:fontname", axisfont,
# 6-stage pipeline, dataLabels
#
# NOTE: `colors=` palette is intentionally omitted here. On cx:chart single-
# series types (funnel/treemap/sunburst) the CLI only applies the first
# palette color to the whole series, so all bars would render the same
# color. Let Excel's theme pick the default accent color.
# ------------------------------------------------------------------
items.append(chart(S1,
chartType="funnel",
title="Marketing Funnel",
series1="Users:10000,6500,3200,1800,900,450",
categories="Impressions,Clicks,Signups,Active,Paying,Retained",
dataLabels="true",
legendfont="9:8B949E:Helvetica Neue",
axisfont="10:58626E:Helvetica Neue",
x="14", y="19", width="13", height="18"))
doc.batch(items)
# ======================================================================
# Sheet 2: Treemap & Sunburst
# ======================================================================
print("--- 2-Treemap & Sunburst ---")
S2 = "/2-Treemap & Sunburst"
items = [sheet("2-Treemap & Sunburst")]
# ------------------------------------------------------------------
# Chart 1: Treemap — parentLabelLayout=overlapping + dataLabels
# Features: chartType=treemap, parentLabelLayout=overlapping, dataLabels.
# NOTE: `colors=` is omitted — see Funnel Chart 4 note: cx single-series
# charts only pick up the first palette color. Excel's theme will auto-
# rainbow the tiles instead.
# ------------------------------------------------------------------
items.append(chart(S2,
chartType="treemap",
title="Revenue by Product",
series1="Revenue:450,380,310,280,210,180,150,120",
categories="Laptops,Phones,Tablets,TVs,Cameras,Audio,Gaming,Wearables",
parentLabelLayout="overlapping",
dataLabels="true",
x="0", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 2: Treemap — parentLabelLayout=banner + bold title
# Features: treemap parentLabelLayout=banner, title.bold/size/color
# ------------------------------------------------------------------
items.append(chart(S2,
chartType="treemap",
title="Department Budget",
series1="Budget:900,750,600,500,420,350,280",
categories="Engineering,Sales,Marketing,Support,Finance,HR,Legal",
parentLabelLayout="banner",
**{"title.bold": "true", "title.size": "14", "title.color": "2E5090"},
x="14", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 3: Treemap — parentLabelLayout=none (no parent label strip)
# Features: treemap parentLabelLayout=none (all labels inline, no header
# strip), dataLabels on leaf tiles
# ------------------------------------------------------------------
items.append(chart(S2,
chartType="treemap",
title="Flat Treemap (no parent labels)",
series1="Units:250,200,180,160,140,120,100,80,60,40",
categories="A,B,C,D,E,F,G,H,I,J",
parentLabelLayout="none",
dataLabels="true",
x="0", y="19", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 4: Sunburst — radial hierarchy + chartFill (solid) + plotFill
# Features: chartType=sunburst, radial hierarchical layout, chartFill (solid
# hex), plotFill (solid hex), dataLabels.
# NOTE 1: cx:chart's chart/plot fill only accepts solid color — not gradient
# (unlike regular cChart). Use a single hex like "F8FAFC" or "none".
# NOTE 2: `colors=` palette is omitted for the same reason as the funnel/
# treemap examples — cx single-series charts paint only the first palette
# entry. Let Excel's theme drive per-segment coloring.
# ------------------------------------------------------------------
items.append(chart(S2,
chartType="sunburst",
title="Market Share by Region",
series1="Share:35,25,20,15,30,25,20,10,15",
categories="North,South,East,West,Urban,Suburban,Rural,Online,Retail",
chartFill="F8FAFC",
plotFill="FFFFFF",
dataLabels="true",
x="14", y="19", width="13", height="18"))
doc.batch(items)
# ======================================================================
# Sheet 3: Histogram & Box Whisker
# ======================================================================
print("--- 3-Histogram & BoxWhisker ---")
S3 = "/3-Histogram & BoxWhisker"
items = [sheet("3-Histogram & BoxWhisker")]
# ------------------------------------------------------------------
# Chart 1: Histogram — auto-binning (Excel picks bin count)
# Features: chartType=histogram, no binning knobs → Excel auto-selects bins
# ------------------------------------------------------------------
items.append(chart(S3,
chartType="histogram",
title="Test Scores (auto bins)",
series1="Scores:45,52,58,61,63,65,67,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99",
x="0", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 2: Histogram — explicit binCount=5 with title glow
# Features: histogram binCount (explicit bin count), title.glow
# ------------------------------------------------------------------
items.append(chart(S3,
chartType="histogram",
title="Sales (binCount=5)",
series1="Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700",
binCount="5",
**{"title.glow": "FFC000-6-50"},
x="14", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 3: Histogram — explicit binSize=50 (fixed bin width) + label font
# Features: histogram binSize (explicit bin width — mutually exclusive with
# binCount), dataLabels, labelFont
# ------------------------------------------------------------------
items.append(chart(S3,
chartType="histogram",
title="Sales (binSize=50)",
series1="Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700",
binSize="50",
dataLabels="true",
labelFont="9:FFFFFF:true",
x="28", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 4: Histogram — overflow/underflow bins + intervalClosed=l
# Features: histogram underflowBin (cutoff for <N), overflowBin (cutoff for
# >N), intervalClosed=l (bins are [a,b) — left-closed; default "r" is
# (a,b]), legend=none
# ------------------------------------------------------------------
items.append(chart(S3,
chartType="histogram",
title="Response Time (outlier bins)",
series1="ms:40,55,68,75,82,88,95,102,110,118,125,135,150,175,220,280,350",
underflowBin="60",
overflowBin="200",
intervalClosed="l",
dataLabels="true",
legend="none",
x="0", y="19", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 5: Box & Whisker — two teams, quartileMethod=exclusive
# Features: chartType=boxWhisker, two-series comparison,
# quartileMethod=exclusive, legend=bottom, outlier detection (built-in)
# ------------------------------------------------------------------
items.append(chart(S3,
chartType="boxWhisker",
title="Response Time by Team (ms)",
series1="TeamA:42,55,61,68,72,75,78,81,85,88,92,97,105,120",
series2="TeamB:30,38,45,52,58,62,65,68,71,74,78,85,92,110",
quartileMethod="exclusive",
legend="bottom",
x="14", y="19", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 6: Box & Whisker — three departments, quartileMethod=inclusive + glow
# Features: boxWhisker three-series, quartileMethod=inclusive (different
# quartile formula from exclusive), title.glow, mean markers (default on)
# ------------------------------------------------------------------
items.append(chart(S3,
chartType="boxWhisker",
title="Salary Distribution ($k)",
series1="Engineering:85,92,95,98,102,105,108,112,118,125,135,150,180",
series2="Marketing:60,65,68,72,75,78,80,83,88,92,98,110",
series3="Sales:55,62,68,75,82,90,98,105,115,125,140,160,190",
quartileMethod="inclusive",
**{"title.glow": "00D2FF-6-60"},
legend="bottom",
x="28", y="19", width="13", height="18"))
doc.batch(items)
# ======================================================================
# Sheet 4: Pareto
# ======================================================================
print("--- 4-Pareto ---")
S4 = "/4-Pareto"
items = [sheet("4-Pareto")]
# ------------------------------------------------------------------
# Chart 1: Pareto — defect analysis, raw counts auto-sorted + cumul% overlay
# Features: chartType=pareto (2-series under the hood — clusteredColumn bars
# + paretoLine cumulative %), automatic descending sort, cumulative %
# computed server-side, dataLabels on both series.
# Input is a SINGLE user series; officecli pre-sorts by value desc and
# emits the two cx:series MSO expects (layoutId=clusteredColumn +
# layoutId=paretoLine with cx:binning intervalClosed="r").
# ------------------------------------------------------------------
items.append(chart(S4,
chartType="pareto",
title="Defect Pareto",
series1="Count:45,30,10,8,5,2",
categories="Scratches,Dents,Cracks,Chips,Stains,Other",
dataLabels="true",
x="0", y="0", width="13", height="18"))
# ------------------------------------------------------------------
# Chart 2: Pareto — root cause analysis, 10 categories, out-of-order input
# Features: pareto with unsorted input values (12, 87, 5, ...) — officecli
# re-sorts by value desc (120, 87, 67, ...) and re-aligns categories so
# the biggest contributor renders first. title.glow + legend=bottom
# demonstrate generic cx styling on pareto.
# ------------------------------------------------------------------
items.append(chart(S4,
chartType="pareto",
title="Root Cause Pareto",
series1="Tickets:12,87,5,45,3,120,22,67,8,31",
categories="Network,Auth,DB,Cache,UI,Config,Deploy,Monitor,Queue,Storage",
**{"title.glow": "FFC000-6-50"},
legend="bottom",
x="14", y="0", width="13", height="18"))
doc.batch(items)
# ======================================================================
# Sheet 5: Chart Meta
# ======================================================================
print("--- 5-Chart Meta ---")
S5 = "/5-Chart Meta"
items = [sheet("5-Chart Meta")]
# ------------------------------------------------------------------
# Chart 1: anchor (cell-range placement), preset (named style bundle)
# Features: anchor="A1:M20" (position chart at exact cell-range instead of
# x/y/width/height — accepts A1-notation two-cell anchor string),
# preset=corporate (named style bundle that sets colors, fonts, fill, border
# in one shot; values: minimal, dark, corporate, magazine, dashboard,
# colorful, monochrome)
# ------------------------------------------------------------------
items.append(chart(S5,
chartType="column",
title="anchor + preset=corporate",
series1="Revenue:120,145,132,160",
categories="Q1,Q2,Q3,Q4",
anchor="A1:M20",
preset="corporate"))
# ------------------------------------------------------------------
# Chart 2: autotitledeleted, plotvisonly
# Features: autotitledeleted=true (suppress the auto "Chart Title" placeholder
# that Excel inserts — use when you want no title at all without explicitly
# passing title=none),
# plotvisonly=true (skip plotting hidden rows/columns — mirrors Excel's
# "Show data in hidden rows and columns" unchecked)
# ------------------------------------------------------------------
items.append(chart(S5,
chartType="bar",
series1="Sales:80,95,88,110",
categories="Q1,Q2,Q3,Q4",
x="0", y="22", width="12", height="18",
autotitledeleted="true",
plotvisonly="true"))
# ------------------------------------------------------------------
# Chart 3: preset variants — minimal
# Features: preset=minimal (strip: removes gridlines, legend, border, most
# styling; exposes the data with minimal chrome)
# ------------------------------------------------------------------
items.append(chart(S5,
chartType="line",
title="preset=minimal",
series1="A:10,20,15,25",
series2="B:8,14,12,20",
categories="W1,W2,W3,W4",
x="13", y="0", width="12", height="18",
preset="minimal"))
# ------------------------------------------------------------------
# Chart 4: preset=dark
# Features: preset=dark (dark background, light-colored series and text)
# ------------------------------------------------------------------
items.append(chart(S5,
chartType="column",
title="preset=dark",
series1="Sales:45,60,55,80",
categories="Q1,Q2,Q3,Q4",
x="13", y="22", width="12", height="18",
preset="dark"))
doc.batch(items)
# Remove blank default Sheet1 (all data is inline)
doc.send({"command": "remove", "path": "/Sheet1"})
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nDone! Generated: {FILE}")
print(" 4 sheets, 16 charts total (full cx:chart feature coverage)")
print(" Sheet 1: Waterfall (2) + Funnel (2)")
print(" Sheet 2: Treemap (3: overlapping/banner/none) + Sunburst (1)")
print(" Sheet 3: Histogram (4: auto/binCount/binSize/overflow+underflow+intervalClosed=l) + BoxWhisker (2: exclusive/inclusive)")
print(" Sheet 4: Pareto (2: sorted input / out-of-order input)")
print(" Sheet 5: Chart Meta (4: anchor+preset / autotitledeleted+plotvisonly / minimal / dark)")
+304
View File
@@ -0,0 +1,304 @@
#!/bin/bash
# Extended Chart Types Showcase — full feature coverage for waterfall, funnel,
# treemap, sunburst, histogram, boxWhisker (cx:chart family) plus pareto and
# chart-meta knobs (anchor, preset, autotitledeleted, plotvisonly).
#
# CLI twin of charts-extended.py (officecli Python SDK). Both produce an
# equivalent charts-extended.xlsx.
#
# Usage:
# ./charts-extended.sh
# 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")/charts-extended.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet 1: Waterfall & Funnel
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Waterfall & Funnel"
# Chart 1: Waterfall — increase/decrease/total colors + data labels + title glow
# Features: chartType=waterfall, increaseColor, decreaseColor, totalColor,
# dataLabels, title.glow
officecli add "$FILE" "/1-Waterfall & Funnel" --type chart \
--prop chartType=waterfall \
--prop title="Cash Flow Bridge" \
--prop data=Start:1000,Revenue:500,Costs:-300,Tax:-100,Net:1100 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop dataLabels=true \
--prop title.glow=00D2FF-6-60 \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# Chart 2: Waterfall — chart-area fill + legend + custom label font
# Features: waterfall with legend=bottom, chartFill (solid hex — cx charts
# don't support gradient fills, use plain RGB), labelFont "size:color:bold"
officecli add "$FILE" "/1-Waterfall & Funnel" --type chart \
--prop chartType=waterfall \
--prop title="Budget vs Actual" \
--prop data=Budget:5000,Sales:2000,Marketing:-800,Ops:-600,Net:5600 \
--prop increaseColor=2E75B6 \
--prop decreaseColor=C00000 \
--prop totalColor=FFC000 \
--prop legend=bottom \
--prop chartFill=F0F4FA \
--prop dataLabels=true \
--prop labelFont=9:333333:true \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# Chart 3: Funnel — sales pipeline with title shadow
# Features: chartType=funnel, descending pipeline values, dataLabels,
# title.shadow "COLOR-BLUR-ANGLE-DIST-OPACITY"
officecli add "$FILE" "/1-Waterfall & Funnel" --type chart \
--prop chartType=funnel \
--prop title="Sales Pipeline" \
--prop series1=Pipeline:1200,850,600,300,120 \
--prop categories=Leads,Qualified,Proposal,Negotiation,Won \
--prop dataLabels=true \
--prop title.shadow=000000-4-45-2-40 \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# Chart 4: Funnel — marketing conversion + legend/axis fonts
# Features: funnel, legendfont "size:color:fontname", axisfont, 6-stage, dataLabels
# NOTE: `colors=` palette is intentionally omitted. On cx:chart single-series
# types (funnel/treemap/sunburst) the CLI only applies the first palette color
# to the whole series, so all bars would render the same color. Let Excel's
# theme pick the default accent color.
officecli add "$FILE" "/1-Waterfall & Funnel" --type chart \
--prop chartType=funnel \
--prop title="Marketing Funnel" \
--prop series1=Users:10000,6500,3200,1800,900,450 \
--prop categories=Impressions,Clicks,Signups,Active,Paying,Retained \
--prop dataLabels=true \
--prop "legendfont=9:8B949E:Helvetica Neue" \
--prop "axisfont=10:58626E:Helvetica Neue" \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 2: Treemap & Sunburst
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Treemap & Sunburst"
# Chart 1: Treemap — parentLabelLayout=overlapping + dataLabels
# Features: chartType=treemap, parentLabelLayout=overlapping, dataLabels.
# NOTE: `colors=` is omitted — see Funnel Chart 4 note: cx single-series
# charts only pick up the first palette color. Excel's theme auto-rainbows.
officecli add "$FILE" "/2-Treemap & Sunburst" --type chart \
--prop chartType=treemap \
--prop title="Revenue by Product" \
--prop series1=Revenue:450,380,310,280,210,180,150,120 \
--prop categories=Laptops,Phones,Tablets,TVs,Cameras,Audio,Gaming,Wearables \
--prop parentLabelLayout=overlapping \
--prop dataLabels=true \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# Chart 2: Treemap — parentLabelLayout=banner + bold title
# Features: treemap parentLabelLayout=banner, title.bold/size/color
officecli add "$FILE" "/2-Treemap & Sunburst" --type chart \
--prop chartType=treemap \
--prop title="Department Budget" \
--prop series1=Budget:900,750,600,500,420,350,280 \
--prop categories=Engineering,Sales,Marketing,Support,Finance,HR,Legal \
--prop parentLabelLayout=banner \
--prop title.bold=true \
--prop title.size=14 \
--prop title.color=2E5090 \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# Chart 3: Treemap — parentLabelLayout=none (no parent label strip)
# Features: treemap parentLabelLayout=none (all labels inline, no header strip),
# dataLabels on leaf tiles
officecli add "$FILE" "/2-Treemap & Sunburst" --type chart \
--prop chartType=treemap \
--prop title="Flat Treemap (no parent labels)" \
--prop series1=Units:250,200,180,160,140,120,100,80,60,40 \
--prop categories=A,B,C,D,E,F,G,H,I,J \
--prop parentLabelLayout=none \
--prop dataLabels=true \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# Chart 4: Sunburst — radial hierarchy + chartFill (solid) + plotFill
# Features: chartType=sunburst, radial hierarchical layout, chartFill (solid hex),
# plotFill (solid hex), dataLabels.
# NOTE 1: cx:chart's chart/plot fill only accepts solid color — not gradient
# (unlike regular cChart). Use a single hex like "F8FAFC" or "none".
# NOTE 2: `colors=` palette is omitted — cx single-series charts paint only
# the first palette entry. Let Excel's theme drive per-segment coloring.
officecli add "$FILE" "/2-Treemap & Sunburst" --type chart \
--prop chartType=sunburst \
--prop title="Market Share by Region" \
--prop series1=Share:35,25,20,15,30,25,20,10,15 \
--prop categories=North,South,East,West,Urban,Suburban,Rural,Online,Retail \
--prop chartFill=F8FAFC \
--prop plotFill=FFFFFF \
--prop dataLabels=true \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 3: Histogram & Box Whisker
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Histogram & BoxWhisker"
# Chart 1: Histogram — auto-binning (Excel picks bin count)
# Features: chartType=histogram, no binning knobs → Excel auto-selects bins
officecli add "$FILE" "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Test Scores (auto bins)" \
--prop series1=Scores:45,52,58,61,63,65,67,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99 \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# Chart 2: Histogram — explicit binCount=5 with title glow
# Features: histogram binCount (explicit bin count), title.glow
officecli add "$FILE" "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Sales (binCount=5)" \
--prop series1=Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700 \
--prop binCount=5 \
--prop title.glow=FFC000-6-50 \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# Chart 3: Histogram — explicit binSize=50 (fixed bin width) + label font
# Features: histogram binSize (explicit bin width — mutually exclusive with
# binCount), dataLabels, labelFont
officecli add "$FILE" "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Sales (binSize=50)" \
--prop series1=Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700 \
--prop binSize=50 \
--prop dataLabels=true \
--prop labelFont=9:FFFFFF:true \
--prop x=28 --prop y=0 --prop width=13 --prop height=18
# Chart 4: Histogram — overflow/underflow bins + intervalClosed=l
# Features: histogram underflowBin (cutoff for <N), overflowBin (cutoff for >N),
# intervalClosed=l (bins are [a,b) — left-closed; default "r" is (a,b]),
# legend=none
officecli add "$FILE" "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=histogram \
--prop title="Response Time (outlier bins)" \
--prop series1=ms:40,55,68,75,82,88,95,102,110,118,125,135,150,175,220,280,350 \
--prop underflowBin=60 \
--prop overflowBin=200 \
--prop intervalClosed=l \
--prop dataLabels=true \
--prop legend=none \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# Chart 5: Box & Whisker — two teams, quartileMethod=exclusive
# Features: chartType=boxWhisker, two-series comparison, quartileMethod=exclusive,
# legend=bottom, outlier detection (built-in)
officecli add "$FILE" "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=boxWhisker \
--prop title="Response Time by Team (ms)" \
--prop "series1=TeamA:42,55,61,68,72,75,78,81,85,88,92,97,105,120" \
--prop "series2=TeamB:30,38,45,52,58,62,65,68,71,74,78,85,92,110" \
--prop quartileMethod=exclusive \
--prop legend=bottom \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# Chart 6: Box & Whisker — three departments, quartileMethod=inclusive + glow
# Features: boxWhisker three-series, quartileMethod=inclusive (different quartile
# formula from exclusive), title.glow, mean markers (default on)
officecli add "$FILE" "/3-Histogram & BoxWhisker" --type chart \
--prop chartType=boxWhisker \
--prop title="Salary Distribution (\$k)" \
--prop "series1=Engineering:85,92,95,98,102,105,108,112,118,125,135,150,180" \
--prop "series2=Marketing:60,65,68,72,75,78,80,83,88,92,98,110" \
--prop "series3=Sales:55,62,68,75,82,90,98,105,115,125,140,160,190" \
--prop quartileMethod=inclusive \
--prop title.glow=00D2FF-6-60 \
--prop legend=bottom \
--prop x=28 --prop y=19 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 4: Pareto
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="4-Pareto"
# Chart 1: Pareto — defect analysis, raw counts auto-sorted + cumul% overlay
# Features: chartType=pareto (2-series under the hood — clusteredColumn bars +
# paretoLine cumulative %), automatic descending sort, cumulative % computed
# server-side, dataLabels on both series.
officecli add "$FILE" "/4-Pareto" --type chart \
--prop chartType=pareto \
--prop title="Defect Pareto" \
--prop series1=Count:45,30,10,8,5,2 \
--prop categories=Scratches,Dents,Cracks,Chips,Stains,Other \
--prop dataLabels=true \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# Chart 2: Pareto — root cause analysis, 10 categories, out-of-order input
# Features: pareto with unsorted input values (12, 87, 5, ...) — officecli
# re-sorts by value desc and re-aligns categories. title.glow + legend=bottom.
officecli add "$FILE" "/4-Pareto" --type chart \
--prop chartType=pareto \
--prop title="Root Cause Pareto" \
--prop series1=Tickets:12,87,5,45,3,120,22,67,8,31 \
--prop categories=Network,Auth,DB,Cache,UI,Config,Deploy,Monitor,Queue,Storage \
--prop title.glow=FFC000-6-50 \
--prop legend=bottom \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 5: Chart Meta
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="5-Chart Meta"
# Chart 1: anchor (cell-range placement), preset (named style bundle)
# Features: anchor="A1:M20" (position chart at exact cell-range instead of
# x/y/width/height), preset=corporate (named style bundle: colors, fonts,
# fill, border in one shot; values: minimal, dark, corporate, magazine,
# dashboard, colorful, monochrome)
officecli add "$FILE" "/5-Chart Meta" --type chart \
--prop chartType=column \
--prop title="anchor + preset=corporate" \
--prop series1=Revenue:120,145,132,160 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop anchor=A1:M20 \
--prop preset=corporate
# Chart 2: autotitledeleted, plotvisonly
# Features: autotitledeleted=true (suppress the auto "Chart Title" placeholder),
# plotvisonly=true (skip plotting hidden rows/columns)
officecli add "$FILE" "/5-Chart Meta" --type chart \
--prop chartType=bar \
--prop series1=Sales:80,95,88,110 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=0 --prop y=22 --prop width=12 --prop height=18 \
--prop autotitledeleted=true \
--prop plotvisonly=true
# Chart 3: preset variants — minimal
# Features: preset=minimal (strip: removes gridlines, legend, border, most
# styling; exposes the data with minimal chrome)
officecli add "$FILE" "/5-Chart Meta" --type chart \
--prop chartType=line \
--prop title="preset=minimal" \
--prop series1=A:10,20,15,25 \
--prop series2=B:8,14,12,20 \
--prop categories=W1,W2,W3,W4 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop preset=minimal
# Chart 4: preset=dark
# Features: preset=dark (dark background, light-colored series and text)
officecli add "$FILE" "/5-Chart Meta" --type chart \
--prop chartType=column \
--prop title="preset=dark" \
--prop series1=Sales:45,60,55,80 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=13 --prop y=22 --prop width=12 --prop height=18 \
--prop preset=dark
# Remove blank default Sheet1 (all data is inline)
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+278
View File
@@ -0,0 +1,278 @@
# Histogram Charts — Grand Showcase
The most thorough histogram demo officecli can produce. Every binning knob,
every styling vocabulary, every canonical distribution shape, six design
themes, four font-family type specimens, and a cohesive production-grade
ML dashboard.
This demo is three files that work together:
- **charts-histogram.py** — Python script that calls `officecli` to generate
the workbook. Each chart command is shown as a copyable shell command in
the comments.
- **charts-histogram.xlsx** — The generated workbook: 6 sheets, 29 charts.
- **charts-histogram.md** — This file. Maps each sheet to the features it
demonstrates and lists the full histogram property vocabulary.
## Regenerate
```bash
cd examples/excel
python3 charts-histogram.py
# → charts-histogram.xlsx
```
## Why a dedicated histogram showcase?
Histograms are Excel's cx-namespace "extended" chart type. The binning layer
(`layoutPr/binning`) is where all the interesting knobs live — auto vs
explicit count, bin width, interval-closed side, outlier cut-offs — and
getting them right takes some care because Excel rejects the file entirely
if the XML uses the wrong form of `cx:binCount` / `cx:binSize`.
Beyond binning, the cx pipeline in officecli has full parity with regular
cChart for typography, axis scaling, area fills/borders, drop shadows,
data labels, and legend styling. This file exercises every binning knob
AND every styling knob in one place, so you can copy-paste from whichever
row most matches the shape you want.
## Sheets at a glance
| Sheet | Charts | What it demonstrates |
|---|---|---|
| 0-Hero | 1 | Full-bleed magazine-grade poster using EVERY knob |
| 1-Binning Lab | 6 | Every binning strategy on one dataset, identical styling |
| 2-Distribution Zoo | 6 | Six canonical real-world distribution shapes |
| 3-Theme Gallery | 6 | Six complete design themes on the SAME dataset |
| 4-Typography | 4 | Four font-family type specimens |
| 5-ML Dashboard | 6 | Cohesive "Production ML Model Report" dashboard |
## Sheet 0: 0-Hero
One full-bleed 27×38-cell hero chart that combines EVERY histogram knob
into a single presentation-grade poster. Dark "Midnight Academia" palette
— navy plot area, gold bars, cream title, soft grid lines, locked Y axis,
dropped shadows on both title and series, data labels with number format,
top legend with compound font styling. If this chart renders correctly,
the entire histogram pipeline is healthy.
```bash
officecli add charts-histogram.xlsx "/0-Hero" --type chart \
--prop chartType=histogram \
--prop title="The Shape of Data · 200-sample bell curve" \
--prop title.color=F5F1E0 --prop title.size=22 --prop title.bold=true \
--prop title.font="Helvetica Neue" \
--prop "title.shadow=000000-8-45-4-70" \
--prop series1="Samples:<200 bell values>" \
--prop binCount=24 --prop intervalClosed=l \
--prop fill=F0C96A --prop "series.shadow=000000-8-45-4-60" \
--prop axismin=0 --prop axismax=28 --prop majorunit=4 \
--prop xAxisTitle="Score" --prop yAxisTitle="Frequency" \
--prop axisTitle.color=C9B87A --prop axisTitle.size=13 \
--prop axisTitle.bold=true --prop axisTitle.font="Helvetica Neue" \
--prop "axisfont=10:B8B090:Helvetica Neue" \
--prop "axisline=6A6448:1.5" \
--prop gridlineColor=2F3544 \
--prop plotareafill=1A1F2C --prop "plotarea.border=3A3E4E:1.25" \
--prop chartareafill=0B0F18 --prop "chartarea.border=2A2E3E:1" \
--prop dataLabels=true --prop "datalabels.numfmt=0" \
--prop legend=top --prop legend.overlay=false \
--prop "legendfont=11:D4C994:Helvetica Neue" \
--prop x=0 --prop y=0 --prop width=27 --prop height=38
```
**Features:** title.color / title.size / title.bold / title.font / title.shadow,
fill, series.shadow, binCount, intervalClosed, axismin/axismax/majorunit,
xAxisTitle / yAxisTitle, axisTitle.color / axisTitle.size / axisTitle.bold /
axisTitle.font, axisfont compound, axisline, gridlineColor, plotareafill,
plotarea.border, chartareafill, chartarea.border, dataLabels, datalabels.numfmt,
legend, legend.overlay, legendfont.
## Sheet 1: 1-Binning Lab
Six charts, SAME dataset (200 bell-curve samples), IDENTICAL typography and
frame — the ONLY thing that varies is the binning strategy. Put side by
side, this sheet is the binning Rosetta stone.
```bash
# 1. Auto-binning (no binCount, no binSize — Excel picks it)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=histogram --prop series1="Samples:<values>" \
--prop title="1 · Auto-binning (Excel default)" --prop fill=4472C4
# 2. Explicit binCount=8 (coarse)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=histogram --prop series1="Samples:<values>" \
--prop binCount=8 --prop title="2 · binCount=8 (coarse)"
# 3. Explicit binCount=32 (fine)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=histogram --prop series1="Samples:<values>" \
--prop binCount=32 --prop title="3 · binCount=32 (fine)"
# 4. Fixed bin width (binSize=5)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=histogram --prop series1="Samples:<values>" \
--prop binSize=5 --prop title="4 · binSize=5 (fixed-width bins)"
# 5. Outlier fencing (underflowBin=55, overflowBin=95)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=histogram --prop series1="Samples:<values>" \
--prop binSize=5 --prop underflowBin=55 --prop overflowBin=95
# 6. Left-closed intervals [a,b) with gapWidth=30 between bars
officecli add data.xlsx /Sheet --type chart \
--prop chartType=histogram --prop series1="Samples:<values>" \
--prop binCount=16 --prop intervalClosed=l --prop gapWidth=30
```
**Features:** `chartType=histogram`, auto-binning (default), `binCount=N`,
`binSize=W`, `underflowBin=N`, `overflowBin=M`, `intervalClosed=l`, `gapWidth=N`
Notes:
- If both `binCount` and `binSize` are given, `binCount` wins.
- Histograms default `gapWidth=0` (bars touch) to match Excel's native output.
- `intervalClosed=l` makes bins half-open `[a,b)` instead of the default `(a,b]`.
- `underflow` / `overflow` fences let the interesting bulk stay readable
when the tail is catastrophic.
## Sheet 2: 2-Distribution Zoo
A 2×3 visual gallery of canonical real-world distribution shapes. Pattern
recognition: if you ever see one of these shapes in a telemetry chart, you
know immediately what's going on. Every chart shares the same typography
and frame; only the fill color, data, and binning strategy change.
| Shape | Data | Fill | Binning |
|---|---|---|---|
| Normal · bell curve | 200 gauss(75, 12) | #2F5597 | binCount=18 |
| Bimodal · two cohorts | 80 gauss(55,6) + 80 gauss(88,5) | #ED7D31 | binCount=22 |
| Right-skewed · log-normal | 180 exp(gauss(3.2, 0.55)) | #70AD47 | binCount=20 |
| Left-skewed · retirement | 140 75 exp(gauss(1.6, 0.6)) | #7030A0 | binCount=18 |
| Uniform · flat floor | 160 uniform(0, 100) | #00B0F0 | binSize=10 |
| Heavy-tailed · Pareto | 200 paretovariate(1.6) × 20 | #C00000 | binSize=20, overflow=250 |
## Sheet 3: 3-Theme Gallery
Six complete design themes applied to the SAME bell-curve dataset. Each
theme is a coordinated palette: plot-area fill, chart-area fill, series
fill, gridline color, axis line color, tick-label color, title color,
title font — all chosen to read as one coherent mood.
| Theme | Mood | Plot BG | Bar | Title font |
|---|---|---|---|---|
| Midnight Academia | Dark, elegant | navy #1A1F2C | gold #F0C96A | Georgia |
| Sunset Terracotta | Warm, editorial | cream #FFF5E8 | coral #E85D4A | Georgia |
| Forest Parchment | Organic, retro | beige #F3EDD8 | forest #2F5D3A | Georgia |
| Editorial Mono | Pure grayscale | white #FFFFFF | dark #2A2A2A | Helvetica Neue |
| Neon Terminal | Cyberpunk | black #0A0A14 | cyan #00F0C8 | Courier New |
| Pastel Bloom | Soft, feminine | lavender #FDF4F8 | rose #F5A7C8 | Helvetica Neue |
Each chart uses the full parity-knob vocabulary: `plotareafill`,
`plotarea.border`, `chartareafill`, `chartarea.border`, `gridlineColor`,
`axisline`, `axisfont`, `title.color` / `title.font`, `axisTitle.color` /
`axisTitle.font`. This is the sheet to copy-paste from when you want to
build a specific look for a report.
## Sheet 4: 4-Typography
Four font-family type specimens. Same data, same geometry, nearly identical
color — only the font family varies. Side by side, this sheet shows how
typography alone can reshape a chart's tone.
| Font | Tone | Used for |
|---|---|---|
| Helvetica Neue | Modern sans | Dashboards, corporate reports |
| Georgia | Editorial serif | Magazines, long-form reports |
| Courier New | Data mono | Telemetry, engineering, terminals |
| Verdana | Friendly sans | Onboarding, public-facing UI |
Each specimen sets `title.font`, `axisTitle.font`, and the fontname segment
of the `axisfont` compound form to the same family, so the entire chart
lives in one typographic voice.
## Sheet 5: 5-ML Dashboard
A cohesive "Production ML Model Report" dashboard. Every chart wears the
same uniform — typography, frames, gridlines, axis line — but each shows
a different slice of the model's behavior, deliberately using a different
color, binning strategy, and (where relevant) outlier-fencing or axis
locking. The six read as one dashboard.
| Panel | Data shape | Color | Binning / parity knob |
|---|---|---|---|
| Inference Latency · p50p99 | heavy-tail | #EF4444 | binSize=25, overflowBin=300, series.shadow |
| Prediction Confidence | right-skewed | #10B981 | binSize=5, axismin=0, majorunit=50 |
| Residual magnitude | half-normal | #F59E0B | binSize=0.25, intervalClosed=l |
| Token length | bimodal | #6366F1 | binCount=24 |
| GPU utilization | normal (clipped) | #8B5CF6 | binSize=5, axismin=0 axismax=50 majorunit=10 |
| Cost per request | log-normal | #EC4899 | binSize=5, overflowBin=120, dataLabels+numfmt |
This sheet shows that one typographic uniform plus per-panel color and
binning choices is enough to build a production dashboard. Copy the
`DASH` style block from `charts-histogram.py` as a starting point.
## Histogram Property Reference
| Property | Default | Notes |
|---|---|---|
| `chartType` | — | Must be `histogram` |
| `title` | — | Chart title text |
| `series1` | — | `"name:v1,v2,v3,..."` — raw values, not pre-binned |
| `binCount` | auto | Integer: force exactly N bins |
| `binSize` | auto | Number: force fixed bin width |
| `intervalClosed` | `r` | `r` = (a,b], `l` = [a,b) |
| `underflowBin` | — | Group values < N into a single `<N` bar |
| `overflowBin` | — | Group values > M into a single `>M` bar |
| `gapWidth` | `0` | Space between bars (0 = touching) |
| `fill` | — | Single-color shortcut (HEX) |
| `colors` | — | Comma list of HEX (multi-series) |
| `dataLabels` | `false` | `true` puts value count above each bar |
| `datalabels.numfmt` | — | Excel format code (`0`, `0.0`, `0.00%`, `#,##0`) |
| `xAxisTitle` / `yAxisTitle` | — | Axis titles |
| `gridlines` | `true` | Value-axis major gridlines |
| `xGridlines` | `false` | Category-axis major gridlines |
| `tickLabels` | `true` | Show bin range labels on x-axis |
| `axismin` / `axismax` | — | Value-axis range (numeric) |
| `majorunit` / `minorunit` | — | Value-axis gridline interval |
| `axis.visible` / `cataxis.visible` / `valaxis.visible` | — | Axis hidden flags |
| `axisline` | — | Axis spine: `"color"` / `"color:width"` / `"color:width:dash"` / `"none"` |
| `cataxis.line` / `valaxis.line` | — | Per-axis spine styling |
| `plotareafill` / `plotfill` | — | Plot-area solid background color |
| `plotarea.border` / `plotborder` | — | Plot-area outline |
| `chartareafill` / `chartfill` | — | Chart-area solid background color |
| `chartarea.border` / `chartborder` | — | Chart-area outline |
| `series.shadow` | — | Outer shadow on bars: `"COLOR-BLUR-ANGLE-DIST-OPACITY"` |
| `title.shadow` | — | Outer shadow on title: `"COLOR-BLUR-ANGLE-DIST-OPACITY"` |
| `legend` | — | `top` / `bottom` / `left` / `right` / `none` |
| `legend.overlay` | `false` | Legend floats on top of plot area when `true` |
| `legendfont` | — | Compound `"size:color:fontname"` |
| `title.color` / `title.size` / `title.bold` / `title.font` | — | Chart title styling |
| `axisTitle.color` / `axisTitle.size` / `axisTitle.font` / `axisTitle.bold` | — | Axis title styling (both X and Y) |
| `axisfont` | — | Compound tick-label styling: `"size:color:fontname"` |
| `gridlineColor` | — | Value-axis major gridline color |
| `xGridlineColor` | — | Category-axis major gridline color (requires `xGridlines=true`) |
| `x` / `y` / `width` / `height` | — | Chart cell placement and size |
## Inspect the Generated File
```bash
# Count all charts across all sheets
officecli query charts-histogram.xlsx chart
# Introspect a single chart's bound properties
officecli get charts-histogram.xlsx "/0-Hero/chart[1]"
officecli get charts-histogram.xlsx "/5-ML Dashboard/chart[1]"
# Render any sheet to HTML preview
officecli view charts-histogram.xlsx html > preview.html
```
> Note: officecli's HTML preview renders the full parity vocabulary
> (plot-area / chart-area fills, gridline + axis line colors, tick
> label colors, data labels, locked axis scales, gapWidth, etc.),
> but does not currently reproduce custom axis-label font families —
> all tick labels fall back to the preview's default sans font. Excel
> renders the full styling including the font family. Use the preview
> for layout + color verification, use Excel (or Numbers / LibreOffice)
> for final typographic QA.
+560
View File
@@ -0,0 +1,560 @@
#!/usr/bin/env python3
"""
Histogram Charts — Grand Showcase
==================================
The most thorough, most visually polished histogram demo officecli can
produce. Every binning knob, every styling vocabulary, every canonical
distribution shape, six design themes on one dataset, four font type
specimens, and a cohesive production-grade ML dashboard — all driven by
the real officecli Python SDK.
SDK twin of charts-histogram.sh (officecli CLI). Both produce an equivalent
charts-histogram.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in `doc.batch(...)` round-trips. Each
item is the same `{"command","parent","type","props"}` dict you'd put in an
`officecli batch` list.
Generates: charts-histogram.xlsx (6 sheets, 29 histograms)
0-Hero 1 magazine-grade full-bleed hero poster chart
1-Binning Lab 6 charts — every binning knob, identical styling
2-Distribution Zoo 6 canonical real-world distribution shapes
3-Theme Gallery 6 design themes on the SAME dataset
4-Typography 4 font-family type specimens
5-ML Dashboard 6-chart "Production ML Model Report" dashboard
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-histogram.py
"""
import os
import sys
import random
import math
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-histogram.xlsx")
# --------------------------------------------------------------------------
# Batch-item helpers — each returns one dict you'd put in an `officecli batch`
# list. The SDK ships the whole list in a single named-pipe round-trip.
# --------------------------------------------------------------------------
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape."""
props.setdefault("chartType", "histogram")
return {"command": "add", "parent": parent, "type": "chart", "props": props}
# --------------------------------------------------------------------------
# Deterministic sample generators — same seed, same file every regeneration.
# All datasets are CSV-joined once here and reused across sheets.
# --------------------------------------------------------------------------
def csv(values):
return ",".join(str(v) for v in values)
# The "reference" bell curve — 200 samples around 75±12. Used by the hero,
# the binning lab, the theme gallery, the typography specimens, and the zoo.
random.seed(42)
BELL_200 = sorted(round(random.gauss(75, 12), 1) for _ in range(200))
BELL_CSV = csv(BELL_200)
# Bimodal: two cohorts (beginners ~55, experts ~88) glued together.
random.seed(7)
BIMODAL = sorted(
[round(random.gauss(55, 6), 1) for _ in range(80)]
+ [round(random.gauss(88, 5), 1) for _ in range(80)]
)
BIMODAL_CSV = csv(BIMODAL)
# Right-skewed / log-normal: classic income shape.
random.seed(11)
LOGNORM = sorted(round(math.exp(random.gauss(3.2, 0.55)), 1) for _ in range(180))
LOGNORM_CSV = csv(LOGNORM)
# Left-skewed: retirement ages — most cluster high, a few retire early.
random.seed(23)
LEFT_SKEW = sorted(round(75 - math.exp(random.gauss(1.6, 0.6)), 1) for _ in range(140))
LEFT_CSV = csv(LEFT_SKEW)
# Uniform: random draws evenly distributed across a range.
random.seed(31)
UNIFORM = sorted(round(random.uniform(0, 100), 1) for _ in range(160))
UNIFORM_CSV = csv(UNIFORM)
# Heavy-tailed (Pareto): most small, tiny fraction catastrophic.
random.seed(47)
PARETO = sorted(round(random.paretovariate(1.6) * 20, 1) for _ in range(200))
PARETO_CSV = csv(PARETO)
# --- ML Dashboard datasets (sheet 5) ---
random.seed(101)
LATENCY_MS = sorted(round(random.paretovariate(1.8) * 15 + 10, 1) for _ in range(250))
LATENCY_CSV = csv(LATENCY_MS)
random.seed(102)
CONFIDENCE = sorted(round(random.betavariate(6, 2) * 100, 2) for _ in range(240))
CONFIDENCE_CSV = csv(CONFIDENCE)
random.seed(103)
ERROR_MAG = sorted(round(abs(random.gauss(0, 1.5)), 3) for _ in range(180))
ERROR_MAG_CSV = csv(ERROR_MAG)
random.seed(104)
TOKEN_LEN = sorted(
[max(1, round(random.gauss(180, 40))) for _ in range(100)]
+ [max(1, round(random.gauss(520, 90))) for _ in range(80)]
)
TOKEN_CSV = csv(TOKEN_LEN)
random.seed(105)
GPU_UTIL = sorted(round(min(99.0, max(30.0, random.gauss(82, 8))), 1) for _ in range(200))
GPU_CSV = csv(GPU_UTIL)
random.seed(106)
COST_REQ = sorted(round(math.exp(random.gauss(-3.2, 0.9)) * 1000, 3) for _ in range(220))
COST_CSV = csv(COST_REQ)
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet 0: "0-Hero" — the full-bleed magazine hero poster
#
# A single giant chart using EVERY histogram knob at once:
# - Dark "Midnight Academia" palette: navy plot area, gold bars, cream text
# - title.* (color/size/bold/font/shadow)
# - series.shadow + fill
# - axisline + axisfont + axisTitle.*
# - plotareafill / plotarea.border / chartareafill / chartarea.border
# - axismin / axismax / majorunit (locked Y scale)
# - gridlineColor
# - dataLabels + datalabels.numfmt
# - legend=top + legend.overlay + legendfont
# - intervalClosed=l + explicit binCount
#
# This chart is the "representative sample" — if it renders correctly, the
# entire histogram pipeline is healthy.
# ======================================================================
print("\n--- 0-Hero ---")
doc.batch([
# rename the default Sheet1 → "0-Hero"
{"command": "set", "path": "/Sheet1", "props": {"name": "0-Hero"}},
# EVERY knob — title/series/axis/plotarea/chartarea/shadow/scaling/legend/datalabel
chart("/0-Hero",
title="The Shape of Data · 200-sample bell curve",
**{"title.color": "F5F1E0", "title.size": "22", "title.bold": "true",
"title.font": "Helvetica Neue",
"title.shadow": "000000-8-45-4-70"},
series1=f"Samples:{BELL_CSV}",
binCount="24", intervalClosed="l",
fill="F0C96A", **{"series.shadow": "000000-8-45-4-60"},
axismin="0", axismax="28", majorunit="4",
xAxisTitle="Score", yAxisTitle="Frequency",
**{"axisTitle.color": "C9B87A", "axisTitle.size": "13",
"axisTitle.bold": "true", "axisTitle.font": "Helvetica Neue",
"axisfont": "10:B8B090:Helvetica Neue",
"axisline": "6A6448:1.5"},
gridlineColor="2F3544",
plotareafill="1A1F2C", **{"plotarea.border": "3A3E4E:1.25"},
chartareafill="0B0F18", **{"chartarea.border": "2A2E3E:1"},
dataLabels="true", **{"datalabels.numfmt": "0"},
legend="top", **{"legend.overlay": "false",
"legendfont": "11:D4C994:Helvetica Neue"},
x="0", y="0", width="27", height="38"),
])
# ======================================================================
# Sheet 1: "1-Binning Lab"
#
# Six histograms, SAME dataset (BELL_200), IDENTICAL typography / colors /
# frames — the ONLY thing that varies is the binning strategy. Put side by
# side, this sheet is the "Rosetta stone": once you see how each binning
# knob reshapes the bars, you'll never be confused about which to use.
#
# ┌──────────┬──────────┐
# │ 1. auto │ 2. count │
# ├──────────┼──────────┤
# │ 3. fine │ 4. width │
# ├──────────┼──────────┤
# │ 5. fence │ 6. lclos │
# └──────────┴──────────┘
# ======================================================================
print("\n--- 1-Binning Lab ---")
# Shared "clean lab" style — every chart on this sheet wears the exact same
# outfit so the bin-shape difference is the only visible variable.
LAB = {
"fill": "4472C4",
"title.color": "1F2937", "title.size": "13", "title.bold": "true",
"title.font": "Helvetica Neue",
"xAxisTitle": "Score", "yAxisTitle": "Count",
"axisTitle.color": "6B7280", "axisTitle.size": "10",
"axisTitle.font": "Helvetica Neue",
"axisfont": "9:6B7280:Helvetica Neue",
"gridlineColor": "F0F0F0",
"plotareafill": "FFFFFF", "plotarea.border": "E5E7EB:0.75",
"chartareafill": "F9FAFB", "chartarea.border": "E5E7EB:0.75",
"axisline": "9CA3AF:0.75",
}
doc.batch([
add_sheet("1-Binning Lab"),
# no binCount, no binSize — Excel picks the bin count automatically.
chart("/1-Binning Lab", title="1 · Auto-binning (Excel default)",
series1=f"Samples:{BELL_CSV}", **LAB,
x="0", y="0", width="13", height="18"),
# binCount=8 — coarse. Fewer, wider bars. Good for "what's the mode?"
chart("/1-Binning Lab", title="2 · binCount=8 (coarse)",
series1=f"Samples:{BELL_CSV}", binCount="8", **LAB,
x="14", y="0", width="13", height="18"),
# binCount=32 — fine. Many narrow bars. Good for "is it really Gaussian?"
chart("/1-Binning Lab", title="3 · binCount=32 (fine)",
series1=f"Samples:{BELL_CSV}", binCount="32", **LAB,
x="0", y="19", width="13", height="18"),
# binSize=5 — fixed bin width. Use when you want human-friendly bin
# boundaries (multiples of 5, 10, etc) regardless of data range.
chart("/1-Binning Lab", title="4 · binSize=5 (fixed-width bins)",
series1=f"Samples:{BELL_CSV}", binSize="5", **LAB,
x="14", y="19", width="13", height="18"),
# underflowBin=55 + overflowBin=95 — outlier fencing. Everything below
# 55 or above 95 collapses into a single <55 / >95 bar.
chart("/1-Binning Lab", title="5 · underflow=55 · overflow=95 (fencing)",
series1=f"Samples:{BELL_CSV}", binSize="5", underflowBin="55",
overflowBin="95", **LAB,
x="0", y="38", width="13", height="18"),
# intervalClosed=l (half-open [a,b)) + gapWidth=30 — shows the
# "left-closed" variant AND pushes bars apart so you can see each one.
# Useful when the dataset has values lying exactly on a bin boundary.
chart("/1-Binning Lab", title="6 · [a,b) intervals + gapWidth=30",
series1=f"Samples:{BELL_CSV}", binCount="16", intervalClosed="l",
gapWidth="30", **LAB,
x="14", y="38", width="13", height="18"),
])
# ======================================================================
# Sheet 2: "2-Distribution Zoo"
#
# A cohesive 2x3 gallery of the canonical distribution shapes you'll see
# in production data. Pattern recognition: if you ever see one of these
# shapes in a telemetry chart, you know immediately what's going on.
#
# Every chart shares the same typography + plot/chart area frames; only
# the fill color and data change. Uses different binning strategies
# appropriate to each distribution.
# ======================================================================
print("\n--- 2-Distribution Zoo ---")
ZOO = {
"title.color": "1F2937", "title.size": "13", "title.bold": "true",
"title.font": "Helvetica Neue",
"axisTitle.color": "6B7280", "axisTitle.size": "10",
"axisTitle.font": "Helvetica Neue",
"axisfont": "9:6B7280:Helvetica Neue",
"gridlineColor": "EFEFEF",
"plotareafill": "FFFFFF", "plotarea.border": "E5E7EB:0.75",
"chartareafill": "F9FAFB", "chartarea.border": "E5E7EB:0.75",
"axisline": "9CA3AF:0.75",
}
doc.batch([
add_sheet("2-Distribution Zoo"),
# classic bell curve reference, binCount=18, midnight blue fill.
chart("/2-Distribution Zoo", title="Normal · bell curve (reference)",
series1=f"Samples:{BELL_CSV}", binCount="18", fill="2F5597",
xAxisTitle="Score", yAxisTitle="Count", **ZOO,
x="0", y="0", width="13", height="18"),
# bimodal — two hidden populations. Narrow bins reveal the split.
chart("/2-Distribution Zoo", title="Bimodal · two hidden cohorts",
series1=f"Score:{BIMODAL_CSV}", binCount="22", fill="ED7D31",
xAxisTitle="Test score", yAxisTitle="Students", **ZOO,
x="14", y="0", width="13", height="18"),
# right-skewed log-normal. Mean >> median, long tail to the right.
chart("/2-Distribution Zoo", title="Right-skewed · log-normal (income)",
series1=f"Income:{LOGNORM_CSV}", binCount="20", fill="70AD47",
xAxisTitle="Monthly income ($k)", yAxisTitle="People", **ZOO,
x="0", y="19", width="13", height="18"),
# left-skewed — retirement ages cluster high, tail stretches left.
chart("/2-Distribution Zoo", title="Left-skewed · retirement ages",
series1=f"Age:{LEFT_CSV}", binCount="18", fill="7030A0",
xAxisTitle="Age at retirement", yAxisTitle="Retirees", **ZOO,
x="14", y="19", width="13", height="18"),
# uniform — every value equally likely. binSize emphasizes the
# "flat floor" visual tell.
chart("/2-Distribution Zoo", title="Uniform · flat floor",
series1=f"Draws:{UNIFORM_CSV}", binSize="10", fill="00B0F0",
xAxisTitle="Random draw (0-100)", yAxisTitle="Count", **ZOO,
x="0", y="38", width="13", height="18"),
# heavy-tailed Pareto + overflowBin. Fences the catastrophic tail so
# the interesting bulk of the distribution stays readable.
chart("/2-Distribution Zoo", title="Heavy-tailed · Pareto (overflow=250)",
series1=f"Latency:{PARETO_CSV}", binSize="20", overflowBin="250",
fill="C00000",
xAxisTitle="Latency (ms)", yAxisTitle="Requests", **ZOO,
x="14", y="38", width="13", height="18"),
])
# ======================================================================
# Sheet 3: "3-Theme Gallery"
#
# Six complete design themes applied to the SAME bell-curve dataset. Each
# theme is a coordinated palette: plot-area fill, chart-area fill, series
# fill, gridline color, axis line color, tick-label color, title color,
# title font — all chosen to read as one coherent mood.
#
# Grid:
# ┌─────────────┬─────────────┐
# │ 1. Midnight │ 2. Sunset │
# ├─────────────┼─────────────┤
# │ 3. Forest │ 4. Mono │
# ├─────────────┼─────────────┤
# │ 5. Neon │ 6. Pastel │
# └─────────────┴─────────────┘
# ======================================================================
print("\n--- 3-Theme Gallery ---")
doc.batch([
add_sheet("3-Theme Gallery"),
# Theme 1 · Midnight Academia — dark plot area, gold bars, shadows
chart("/3-Theme Gallery", title="Midnight Academia",
**{"title.color": "F5F1E0", "title.size": "14", "title.bold": "true",
"title.font": "Georgia", "title.shadow": "000000-6-45-3-70"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="F0C96A",
**{"series.shadow": "000000-6-45-3-55"},
plotareafill="1A1F2C", **{"plotarea.border": "3A3E4E:1"},
chartareafill="0B0F18", **{"chartarea.border": "2A2E3E:0.75"},
gridlineColor="2F3544",
**{"axisfont": "9:B8B090:Georgia"},
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "C9B87A", "axisTitle.size": "10",
"axisTitle.font": "Georgia", "axisline": "5A5848:1"},
x="0", y="0", width="13", height="18"),
# Theme 2 · Sunset Terracotta (warm cream + coral, serif)
chart("/3-Theme Gallery", title="Sunset Terracotta",
**{"title.color": "3F2818", "title.size": "14", "title.bold": "true",
"title.font": "Georgia"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="E85D4A",
plotareafill="FFF5E8", **{"plotarea.border": "F0D8B0:1"},
chartareafill="FFE6C7", **{"chartarea.border": "E6BC88:1"},
gridlineColor="F5C98A",
**{"axisfont": "9:6B4A2A:Georgia"},
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "A8522C", "axisTitle.size": "10",
"axisTitle.font": "Georgia", "axisline": "C08050:1"},
x="14", y="0", width="13", height="18"),
# Theme 3 · Forest Parchment (beige + forest green, serif)
chart("/3-Theme Gallery", title="Forest Parchment",
**{"title.color": "1F3A1F", "title.size": "14", "title.bold": "true",
"title.font": "Georgia"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="2F5D3A",
plotareafill="F3EDD8", **{"plotarea.border": "C8B890:1"},
chartareafill="EADFBE", **{"chartarea.border": "A89858:1"},
gridlineColor="C0B888",
**{"axisfont": "9:4A5A3A:Georgia"},
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "3F5A2F", "axisTitle.size": "10",
"axisTitle.font": "Georgia", "axisline": "6A7A4A:1"},
x="0", y="19", width="13", height="18"),
# Theme 4 · Editorial Mono (pure grayscale, sans)
chart("/3-Theme Gallery", title="Editorial Mono",
**{"title.color": "111111", "title.size": "14", "title.bold": "true",
"title.font": "Helvetica Neue"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="2A2A2A",
plotareafill="FFFFFF", **{"plotarea.border": "CCCCCC:0.75"},
chartareafill="FAFAFA", **{"chartarea.border": "E0E0E0:0.75"},
gridlineColor="EEEEEE",
**{"axisfont": "9:555555:Helvetica Neue"},
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "333333", "axisTitle.size": "10",
"axisTitle.font": "Helvetica Neue", "axisline": "888888:1"},
x="14", y="19", width="13", height="18"),
# Theme 5 · Neon Terminal (black + electric cyan, mono)
chart("/3-Theme Gallery", title="Neon Terminal",
**{"title.color": "00F0C8", "title.size": "14", "title.bold": "true",
"title.font": "Courier New", "title.shadow": "00F0C8-6-45-0-40"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="00F0C8",
**{"series.shadow": "00F0C8-8-45-0-45"},
plotareafill="0A0A14", **{"plotarea.border": "1F2F3F:1"},
chartareafill="000008", **{"chartarea.border": "1F1F2F:1"},
gridlineColor="1A2A3A",
**{"axisfont": "9:00D0E8:Courier New"},
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "00D0E8", "axisTitle.size": "10",
"axisTitle.font": "Courier New", "axisline": "00707F:1"},
x="0", y="38", width="13", height="18"),
# Theme 6 · Pastel Bloom (lavender cream + rose, sans)
chart("/3-Theme Gallery", title="Pastel Bloom",
**{"title.color": "5A3C4A", "title.size": "14", "title.bold": "true",
"title.font": "Helvetica Neue"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="F5A7C8",
plotareafill="FDF4F8", **{"plotarea.border": "F0D0E0:1"},
chartareafill="FAEDF2", **{"chartarea.border": "F0C0D8:1"},
gridlineColor="F5D8E5",
**{"axisfont": "9:8A6878:Helvetica Neue"},
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "A04C6A", "axisTitle.size": "10",
"axisTitle.font": "Helvetica Neue", "axisline": "C888A0:1"},
x="14", y="38", width="13", height="18"),
])
# ======================================================================
# Sheet 4: "4-Typography"
#
# Four font-family "type specimens". Same data, same geometry, same colors —
# only the font varies. Side-by-side, this shows how typography alone reads
# as tone: Helvetica is corporate, Georgia is editorial, Courier is data,
# Verdana is approachable.
# ======================================================================
print("\n--- 4-Typography ---")
doc.batch([
add_sheet("4-Typography"),
# Specimen 1 · Helvetica Neue (modern sans — dashboards, corporate reports)
chart("/4-Typography", title="Helvetica Neue · modern sans",
**{"title.color": "1F2937", "title.size": "16", "title.bold": "true",
"title.font": "Helvetica Neue"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="4472C4",
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "4472C4", "axisTitle.size": "11",
"axisTitle.font": "Helvetica Neue",
"axisfont": "10:6B7280:Helvetica Neue"},
gridlineColor="EEEEEE",
plotareafill="FFFFFF", **{"plotarea.border": "E5E7EB:0.75"},
chartareafill="F9FAFB", **{"chartarea.border": "E5E7EB:0.75"},
x="0", y="0", width="13", height="18"),
# Specimen 2 · Georgia (editorial serif — magazines, long-form reports)
chart("/4-Typography", title="Georgia · editorial serif",
**{"title.color": "3F2818", "title.size": "16", "title.bold": "true",
"title.font": "Georgia"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="A8522C",
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "A8522C", "axisTitle.size": "11",
"axisTitle.font": "Georgia",
"axisfont": "10:6B4A2A:Georgia"},
gridlineColor="F0E8D8",
plotareafill="FFFBF3", **{"plotarea.border": "E8D8B8:0.75"},
chartareafill="FDF6E8", **{"chartarea.border": "E8D8B8:0.75"},
x="14", y="0", width="13", height="18"),
# Specimen 3 · Courier New (monospace — data, telemetry, engineering)
chart("/4-Typography", title="Courier New · data mono",
**{"title.color": "1A3A1A", "title.size": "16", "title.bold": "true",
"title.font": "Courier New"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="2F8F4F",
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "2F8F4F", "axisTitle.size": "11",
"axisTitle.font": "Courier New",
"axisfont": "10:3A5A3A:Courier New"},
gridlineColor="E0EDE0",
plotareafill="F7FBF7", **{"plotarea.border": "C8DCC8:0.75"},
chartareafill="F0F7F0", **{"chartarea.border": "C8DCC8:0.75"},
x="0", y="19", width="13", height="18"),
# Specimen 4 · Verdana (friendly sans — onboarding, public-facing UI)
chart("/4-Typography", title="Verdana · friendly sans",
**{"title.color": "4A2B6A", "title.size": "16", "title.bold": "true",
"title.font": "Verdana"},
series1=f"Samples:{BELL_CSV}", binCount="18", fill="8E4DBB",
xAxisTitle="Score", yAxisTitle="Count",
**{"axisTitle.color": "8E4DBB", "axisTitle.size": "11",
"axisTitle.font": "Verdana",
"axisfont": "10:6B4A8A:Verdana"},
gridlineColor="ECE0F4",
plotareafill="FCF7FF", **{"plotarea.border": "D8C4E8:0.75"},
chartareafill="F6EDFA", **{"chartarea.border": "D8C4E8:0.75"},
x="14", y="19", width="13", height="18"),
])
# ======================================================================
# Sheet 5: "5-ML Dashboard"
#
# A cohesive six-chart "Production ML Model Report". Every chart wears the
# same corporate dashboard uniform — same typography, same frames, same
# gridlines — but each shows a different slice of the model's behavior,
# deliberately using a different color + binning strategy so the six read
# as a single dashboard at a glance.
#
# Row 1: Inference latency (ms) | Prediction confidence (%)
# Row 2: |Residual| (logit) | Token length (chars)
# Row 3: GPU utilization (%) | Cost per request ($ × 0.001)
# ======================================================================
print("\n--- 5-ML Dashboard ---")
DASH = {
"title.color": "1F2937", "title.size": "12", "title.bold": "true",
"title.font": "Helvetica Neue",
"axisTitle.color": "6B7280", "axisTitle.size": "9",
"axisTitle.font": "Helvetica Neue",
"axisfont": "8:6B7280:Helvetica Neue",
"gridlineColor": "F0F0F0",
"plotareafill": "FFFFFF", "plotarea.border": "E5E7EB:0.75",
"chartareafill": "F9FAFB", "chartarea.border": "E5E7EB:0.75",
"axisline": "9CA3AF:0.75",
"dataLabels": "false",
}
doc.batch([
add_sheet("5-ML Dashboard"),
# 1 · Inference Latency — heavy-tail, overflow-fenced, red for "watch this"
chart("/5-ML Dashboard", title="Inference Latency · p50-p99 (ms)",
series1=f"Latency:{LATENCY_CSV}", binSize="25", overflowBin="300",
fill="EF4444", **{"series.shadow": "EF4444-4-45-2-25"},
xAxisTitle="Latency (ms)", yAxisTitle="Requests", **DASH,
x="0", y="0", width="13", height="18"),
# 2 · Prediction Confidence — beta-like, axismin/max locked to 0..100
chart("/5-ML Dashboard", title="Prediction Confidence",
series1=f"Confidence:{CONFIDENCE_CSV}", binSize="5", fill="10B981",
axismin="0", majorunit="50",
xAxisTitle="Softmax confidence (%)", yAxisTitle="Samples", **DASH,
x="14", y="0", width="13", height="18"),
# 3 · Residual Magnitude — half-normal, intervalClosed=l so bin=0 catches zeros
chart("/5-ML Dashboard", title="|Residual| · model calibration",
series1=f"Residual:{ERROR_MAG_CSV}", binSize="0.25",
intervalClosed="l", fill="F59E0B",
xAxisTitle="|y - ŷ| (logit)", yAxisTitle="Samples", **DASH,
x="0", y="19", width="13", height="18"),
# 4 · Token Length — bimodal (short prompts vs long prompts)
chart("/5-ML Dashboard", title="Token Length · short vs long prompts",
series1=f"Tokens:{TOKEN_CSV}", binCount="24", fill="6366F1",
xAxisTitle="Tokens", yAxisTitle="Requests", **DASH,
x="14", y="19", width="13", height="18"),
# 5 · GPU Utilization — locked axis range so dashboard charts share scale
chart("/5-ML Dashboard", title="GPU Utilization",
series1=f"GPU:{GPU_CSV}", binSize="5", fill="8B5CF6",
axismin="0", axismax="50", majorunit="10",
xAxisTitle="Utilization (%)", yAxisTitle="Samples", **DASH,
x="0", y="38", width="13", height="18"),
# 6 · Cost per Request — log-normal, overflow-fenced, data labels with numfmt
chart("/5-ML Dashboard", title="Cost per Request ($ × 0.001)",
series1=f"Cost:{COST_CSV}", binSize="5", overflowBin="120",
fill="EC4899", dataLabels="true", **{"datalabels.numfmt": "0"},
xAxisTitle="Cost (m$)", yAxisTitle="Requests",
# DASH carries dataLabels=false; override it on this one chart.
**{k: v for k, v in DASH.items() if k != "dataLabels"},
x="14", y="38", width="13", height="18"),
])
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nDone! Generated: {FILE}")
print(" 6 sheets, 29 histograms total")
print(" Sheet 0 (0-Hero): 1 magazine-grade full-bleed hero poster")
print(" Sheet 1 (1-Binning Lab): 6 charts — every binning knob, identical styling")
print(" Sheet 2 (2-Distribution Zoo): 6 canonical real-world distribution shapes")
print(" Sheet 3 (3-Theme Gallery): 6 design themes on the SAME dataset")
print(" Sheet 4 (4-Typography): 4 font-family type specimens")
print(" Sheet 5 (5-ML Dashboard): 6-chart Production ML Model Report")
+471
View File
@@ -0,0 +1,471 @@
#!/bin/bash
# Histogram Charts — Grand Showcase (officecli CLI twin of charts-histogram.py)
#
# The most thorough, most visually polished histogram demo officecli can
# produce. Every binning knob, every styling vocabulary, every canonical
# distribution shape, six design themes on one dataset, four font type
# specimens, and a cohesive production-grade ML dashboard — all driven by
# real copyable officecli CLI commands.
#
# Generates: charts-histogram.xlsx (6 sheets, 29 histograms)
#
# 0-Hero 1 magazine-grade full-bleed hero poster chart
# 1-Binning Lab 6 charts — every binning knob, identical styling
# 2-Distribution Zoo 6 canonical real-world distribution shapes
# 3-Theme Gallery 6 design themes on the SAME dataset
# 4-Typography 4 font-family type specimens
# 5-ML Dashboard 6-chart "Production ML Model Report" dashboard
#
# Usage:
# ./charts-histogram.sh
# 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")/charts-histogram.xlsx"
# --------------------------------------------------------------------------
# Deterministic sample generators — same seed, same file every regeneration.
# bash has no Gaussian RNG, so the datasets are produced once by an inline
# python3 helper (identical seeds/maths to charts-histogram.py) and read into
# shell variables. Everything below is plain `officecli ... --prop k=v`.
# --------------------------------------------------------------------------
eval "$(python3 - <<'PY'
import random, math
def csv(v): return ",".join(str(x) for x in v)
def emit(name, vals): print(f'{name}="{csv(vals)}"')
random.seed(42); emit("BELL_CSV", sorted(round(random.gauss(75,12),1) for _ in range(200)))
random.seed(7); emit("BIMODAL_CSV", sorted([round(random.gauss(55,6),1) for _ in range(80)]+[round(random.gauss(88,5),1) for _ in range(80)]))
random.seed(11); emit("LOGNORM_CSV", sorted(round(math.exp(random.gauss(3.2,0.55)),1) for _ in range(180)))
random.seed(23); emit("LEFT_CSV", sorted(round(75-math.exp(random.gauss(1.6,0.6)),1) for _ in range(140)))
random.seed(31); emit("UNIFORM_CSV", sorted(round(random.uniform(0,100),1) for _ in range(160)))
random.seed(47); emit("PARETO_CSV", sorted(round(random.paretovariate(1.6)*20,1) for _ in range(200)))
random.seed(101);emit("LATENCY_CSV", sorted(round(random.paretovariate(1.8)*15+10,1) for _ in range(250)))
random.seed(102);emit("CONFIDENCE_CSV", sorted(round(random.betavariate(6,2)*100,2) for _ in range(240)))
random.seed(103);emit("ERROR_MAG_CSV", sorted(round(abs(random.gauss(0,1.5)),3) for _ in range(180)))
random.seed(104);emit("TOKEN_CSV", sorted([max(1,round(random.gauss(180,40))) for _ in range(100)]+[max(1,round(random.gauss(520,90))) for _ in range(80)]))
random.seed(105);emit("GPU_CSV", sorted(round(min(99.0,max(30.0,random.gauss(82,8))),1) for _ in range(200)))
random.seed(106);emit("COST_CSV", sorted(round(math.exp(random.gauss(-3.2,0.9))*1000,3) for _ in range(220)))
PY
)"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet 0: "0-Hero" — the full-bleed magazine hero poster
#
# A single giant chart using EVERY histogram knob at once: dark "Midnight
# Academia" palette, title.*, series.shadow + fill, axisline + axisfont +
# axisTitle.*, plotarea/chartarea fill + border, axismin/axismax/majorunit,
# gridlineColor, dataLabels + datalabels.numfmt, legend=top + legend.overlay
# + legendfont, intervalClosed=l + explicit binCount. The "representative
# sample" — if it renders correctly, the entire histogram pipeline is healthy.
# ==========================================================================
echo "--- 0-Hero ---"
officecli set "$FILE" /Sheet1 --prop name="0-Hero"
officecli add "$FILE" "/0-Hero" --type chart \
--prop chartType=histogram \
--prop title="The Shape of Data · 200-sample bell curve" \
--prop title.color=F5F1E0 --prop title.size=22 --prop title.bold=true \
--prop title.font="Helvetica Neue" \
--prop title.shadow=000000-8-45-4-70 \
--prop series1="Samples:$BELL_CSV" \
--prop binCount=24 --prop intervalClosed=l \
--prop fill=F0C96A --prop series.shadow=000000-8-45-4-60 \
--prop axismin=0 --prop axismax=28 --prop majorunit=4 \
--prop xAxisTitle="Score" --prop yAxisTitle="Frequency" \
--prop axisTitle.color=C9B87A --prop axisTitle.size=13 \
--prop axisTitle.bold=true --prop axisTitle.font="Helvetica Neue" \
--prop axisfont="10:B8B090:Helvetica Neue" \
--prop axisline="6A6448:1.5" \
--prop gridlineColor=2F3544 \
--prop plotareafill=1A1F2C --prop plotarea.border="3A3E4E:1.25" \
--prop chartareafill=0B0F18 --prop chartarea.border="2A2E3E:1" \
--prop dataLabels=true --prop datalabels.numfmt=0 \
--prop legend=top --prop legend.overlay=false \
--prop legendfont="11:D4C994:Helvetica Neue" \
--prop x=0 --prop y=0 --prop width=27 --prop height=38
# ==========================================================================
# Sheet 1: "1-Binning Lab"
#
# Six histograms, SAME dataset (BELL), IDENTICAL typography / colors / frames
# — the ONLY thing that varies is the binning strategy. The "Rosetta stone":
# once you see how each binning knob reshapes the bars, you'll never be
# confused about which to use.
# ==========================================================================
echo "--- 1-Binning Lab ---"
officecli add "$FILE" / --type sheet --prop name="1-Binning Lab"
# Shared "clean lab" style — every chart wears the exact same outfit so the
# bin-shape difference is the only visible variable.
LAB=(
--prop fill=4472C4
--prop title.color=1F2937 --prop title.size=13 --prop title.bold=true
--prop title.font="Helvetica Neue"
--prop xAxisTitle="Score" --prop yAxisTitle="Count"
--prop axisTitle.color=6B7280 --prop axisTitle.size=10
--prop axisTitle.font="Helvetica Neue"
--prop axisfont="9:6B7280:Helvetica Neue"
--prop gridlineColor=F0F0F0
--prop plotareafill=FFFFFF --prop plotarea.border="E5E7EB:0.75"
--prop chartareafill=F9FAFB --prop chartarea.border="E5E7EB:0.75"
--prop axisline="9CA3AF:0.75"
)
# no binCount, no binSize — Excel picks the bin count automatically.
officecli add "$FILE" "/1-Binning Lab" --type chart \
--prop chartType=histogram --prop title="1 · Auto-binning (Excel default)" \
--prop series1="Samples:$BELL_CSV" "${LAB[@]}" \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# binCount=8 — coarse. Fewer, wider bars. Good for "what's the mode?"
officecli add "$FILE" "/1-Binning Lab" --type chart \
--prop chartType=histogram --prop title="2 · binCount=8 (coarse)" \
--prop series1="Samples:$BELL_CSV" --prop binCount=8 "${LAB[@]}" \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# binCount=32 — fine. Many narrow bars. Good for "is it really Gaussian?"
officecli add "$FILE" "/1-Binning Lab" --type chart \
--prop chartType=histogram --prop title="3 · binCount=32 (fine)" \
--prop series1="Samples:$BELL_CSV" --prop binCount=32 "${LAB[@]}" \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# binSize=5 — fixed bin width. Human-friendly bin boundaries regardless of range.
officecli add "$FILE" "/1-Binning Lab" --type chart \
--prop chartType=histogram --prop title="4 · binSize=5 (fixed-width bins)" \
--prop series1="Samples:$BELL_CSV" --prop binSize=5 "${LAB[@]}" \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# underflowBin=55 + overflowBin=95 — outlier fencing. Everything below 55 or
# above 95 collapses into a single <55 / >95 bar.
officecli add "$FILE" "/1-Binning Lab" --type chart \
--prop chartType=histogram --prop title="5 · underflow=55 · overflow=95 (fencing)" \
--prop series1="Samples:$BELL_CSV" --prop binSize=5 --prop underflowBin=55 --prop overflowBin=95 "${LAB[@]}" \
--prop x=0 --prop y=38 --prop width=13 --prop height=18
# intervalClosed=l (half-open [a,b)) + gapWidth=30 — left-closed variant AND
# bars pushed apart. Useful when values lie exactly on a bin boundary.
officecli add "$FILE" "/1-Binning Lab" --type chart \
--prop chartType=histogram --prop title="6 · [a,b) intervals + gapWidth=30" \
--prop series1="Samples:$BELL_CSV" --prop binCount=16 --prop intervalClosed=l --prop gapWidth=30 "${LAB[@]}" \
--prop x=14 --prop y=38 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 2: "2-Distribution Zoo"
#
# A cohesive 2x3 gallery of the canonical distribution shapes you'll see in
# production data. Same typography + frames; only the fill color, data, and
# binning strategy change.
# ==========================================================================
echo "--- 2-Distribution Zoo ---"
officecli add "$FILE" / --type sheet --prop name="2-Distribution Zoo"
ZOO=(
--prop title.color=1F2937 --prop title.size=13 --prop title.bold=true
--prop title.font="Helvetica Neue"
--prop axisTitle.color=6B7280 --prop axisTitle.size=10
--prop axisTitle.font="Helvetica Neue"
--prop axisfont="9:6B7280:Helvetica Neue"
--prop gridlineColor=EFEFEF
--prop plotareafill=FFFFFF --prop plotarea.border="E5E7EB:0.75"
--prop chartareafill=F9FAFB --prop chartarea.border="E5E7EB:0.75"
--prop axisline="9CA3AF:0.75"
)
# classic bell curve reference, binCount=18, midnight blue fill.
officecli add "$FILE" "/2-Distribution Zoo" --type chart \
--prop chartType=histogram --prop title="Normal · bell curve (reference)" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=2F5597 \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" "${ZOO[@]}" \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# bimodal — two hidden populations. Narrow bins reveal the split.
officecli add "$FILE" "/2-Distribution Zoo" --type chart \
--prop chartType=histogram --prop title="Bimodal · two hidden cohorts" \
--prop series1="Score:$BIMODAL_CSV" --prop binCount=22 --prop fill=ED7D31 \
--prop xAxisTitle="Test score" --prop yAxisTitle="Students" "${ZOO[@]}" \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# right-skewed log-normal. Mean >> median, long tail to the right.
officecli add "$FILE" "/2-Distribution Zoo" --type chart \
--prop chartType=histogram --prop title="Right-skewed · log-normal (income)" \
--prop series1="Income:$LOGNORM_CSV" --prop binCount=20 --prop fill=70AD47 \
--prop xAxisTitle="Monthly income (\$k)" --prop yAxisTitle="People" "${ZOO[@]}" \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# left-skewed — retirement ages cluster high, tail stretches left.
officecli add "$FILE" "/2-Distribution Zoo" --type chart \
--prop chartType=histogram --prop title="Left-skewed · retirement ages" \
--prop series1="Age:$LEFT_CSV" --prop binCount=18 --prop fill=7030A0 \
--prop xAxisTitle="Age at retirement" --prop yAxisTitle="Retirees" "${ZOO[@]}" \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# uniform — every value equally likely. binSize emphasizes the "flat floor".
officecli add "$FILE" "/2-Distribution Zoo" --type chart \
--prop chartType=histogram --prop title="Uniform · flat floor" \
--prop series1="Draws:$UNIFORM_CSV" --prop binSize=10 --prop fill=00B0F0 \
--prop xAxisTitle="Random draw (0-100)" --prop yAxisTitle="Count" "${ZOO[@]}" \
--prop x=0 --prop y=38 --prop width=13 --prop height=18
# heavy-tailed Pareto + overflowBin. Fences the catastrophic tail so the
# interesting bulk of the distribution stays readable.
officecli add "$FILE" "/2-Distribution Zoo" --type chart \
--prop chartType=histogram --prop title="Heavy-tailed · Pareto (overflow=250)" \
--prop series1="Latency:$PARETO_CSV" --prop binSize=20 --prop overflowBin=250 --prop fill=C00000 \
--prop xAxisTitle="Latency (ms)" --prop yAxisTitle="Requests" "${ZOO[@]}" \
--prop x=14 --prop y=38 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 3: "3-Theme Gallery"
#
# Six complete design themes applied to the SAME bell-curve dataset. Each
# theme is a coordinated palette chosen to read as one coherent mood.
# ==========================================================================
echo "--- 3-Theme Gallery ---"
officecli add "$FILE" / --type sheet --prop name="3-Theme Gallery"
# Theme 1 · Midnight Academia (dark plot area, gold bars, shadows)
officecli add "$FILE" "/3-Theme Gallery" --type chart \
--prop chartType=histogram --prop title="Midnight Academia" \
--prop title.color=F5F1E0 --prop title.size=14 --prop title.bold=true \
--prop title.font="Georgia" --prop title.shadow=000000-6-45-3-70 \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=F0C96A \
--prop series.shadow=000000-6-45-3-55 \
--prop plotareafill=1A1F2C --prop plotarea.border="3A3E4E:1" \
--prop chartareafill=0B0F18 --prop chartarea.border="2A2E3E:0.75" \
--prop gridlineColor=2F3544 \
--prop axisfont="9:B8B090:Georgia" \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=C9B87A --prop axisTitle.size=10 \
--prop axisTitle.font="Georgia" --prop axisline="5A5848:1" \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# Theme 2 · Sunset Terracotta (warm cream + coral, serif)
officecli add "$FILE" "/3-Theme Gallery" --type chart \
--prop chartType=histogram --prop title="Sunset Terracotta" \
--prop title.color=3F2818 --prop title.size=14 --prop title.bold=true \
--prop title.font="Georgia" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=E85D4A \
--prop plotareafill=FFF5E8 --prop plotarea.border="F0D8B0:1" \
--prop chartareafill=FFE6C7 --prop chartarea.border="E6BC88:1" \
--prop gridlineColor=F5C98A \
--prop axisfont="9:6B4A2A:Georgia" \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=A8522C --prop axisTitle.size=10 \
--prop axisTitle.font="Georgia" --prop axisline="C08050:1" \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# Theme 3 · Forest Parchment (beige + forest green, serif)
officecli add "$FILE" "/3-Theme Gallery" --type chart \
--prop chartType=histogram --prop title="Forest Parchment" \
--prop title.color=1F3A1F --prop title.size=14 --prop title.bold=true \
--prop title.font="Georgia" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=2F5D3A \
--prop plotareafill=F3EDD8 --prop plotarea.border="C8B890:1" \
--prop chartareafill=EADFBE --prop chartarea.border="A89858:1" \
--prop gridlineColor=C0B888 \
--prop axisfont="9:4A5A3A:Georgia" \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=3F5A2F --prop axisTitle.size=10 \
--prop axisTitle.font="Georgia" --prop axisline="6A7A4A:1" \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# Theme 4 · Editorial Mono (pure grayscale, sans)
officecli add "$FILE" "/3-Theme Gallery" --type chart \
--prop chartType=histogram --prop title="Editorial Mono" \
--prop title.color=111111 --prop title.size=14 --prop title.bold=true \
--prop title.font="Helvetica Neue" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=2A2A2A \
--prop plotareafill=FFFFFF --prop plotarea.border="CCCCCC:0.75" \
--prop chartareafill=FAFAFA --prop chartarea.border="E0E0E0:0.75" \
--prop gridlineColor=EEEEEE \
--prop axisfont="9:555555:Helvetica Neue" \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=333333 --prop axisTitle.size=10 \
--prop axisTitle.font="Helvetica Neue" --prop axisline="888888:1" \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# Theme 5 · Neon Terminal (black + electric cyan, mono)
officecli add "$FILE" "/3-Theme Gallery" --type chart \
--prop chartType=histogram --prop title="Neon Terminal" \
--prop title.color=00F0C8 --prop title.size=14 --prop title.bold=true \
--prop title.font="Courier New" --prop title.shadow=00F0C8-6-45-0-40 \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=00F0C8 \
--prop series.shadow=00F0C8-8-45-0-45 \
--prop plotareafill=0A0A14 --prop plotarea.border="1F2F3F:1" \
--prop chartareafill=000008 --prop chartarea.border="1F1F2F:1" \
--prop gridlineColor=1A2A3A \
--prop axisfont="9:00D0E8:Courier New" \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=00D0E8 --prop axisTitle.size=10 \
--prop axisTitle.font="Courier New" --prop axisline="00707F:1" \
--prop x=0 --prop y=38 --prop width=13 --prop height=18
# Theme 6 · Pastel Bloom (lavender cream + rose, sans)
officecli add "$FILE" "/3-Theme Gallery" --type chart \
--prop chartType=histogram --prop title="Pastel Bloom" \
--prop title.color=5A3C4A --prop title.size=14 --prop title.bold=true \
--prop title.font="Helvetica Neue" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=F5A7C8 \
--prop plotareafill=FDF4F8 --prop plotarea.border="F0D0E0:1" \
--prop chartareafill=FAEDF2 --prop chartarea.border="F0C0D8:1" \
--prop gridlineColor=F5D8E5 \
--prop axisfont="9:8A6878:Helvetica Neue" \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=A04C6A --prop axisTitle.size=10 \
--prop axisTitle.font="Helvetica Neue" --prop axisline="C888A0:1" \
--prop x=14 --prop y=38 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 4: "4-Typography"
#
# Four font-family "type specimens". Same data, same geometry, same colors —
# only the font varies. Helvetica is corporate, Georgia is editorial, Courier
# is data, Verdana is approachable.
# ==========================================================================
echo "--- 4-Typography ---"
officecli add "$FILE" / --type sheet --prop name="4-Typography"
# Specimen 1 · Helvetica Neue (modern sans — dashboards, corporate reports)
officecli add "$FILE" "/4-Typography" --type chart \
--prop chartType=histogram --prop title="Helvetica Neue · modern sans" \
--prop title.color=1F2937 --prop title.size=16 --prop title.bold=true \
--prop title.font="Helvetica Neue" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=4472C4 \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=4472C4 --prop axisTitle.size=11 \
--prop axisTitle.font="Helvetica Neue" \
--prop axisfont="10:6B7280:Helvetica Neue" \
--prop gridlineColor=EEEEEE \
--prop plotareafill=FFFFFF --prop plotarea.border="E5E7EB:0.75" \
--prop chartareafill=F9FAFB --prop chartarea.border="E5E7EB:0.75" \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# Specimen 2 · Georgia (editorial serif — magazines, long-form reports)
officecli add "$FILE" "/4-Typography" --type chart \
--prop chartType=histogram --prop title="Georgia · editorial serif" \
--prop title.color=3F2818 --prop title.size=16 --prop title.bold=true \
--prop title.font="Georgia" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=A8522C \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=A8522C --prop axisTitle.size=11 \
--prop axisTitle.font="Georgia" \
--prop axisfont="10:6B4A2A:Georgia" \
--prop gridlineColor=F0E8D8 \
--prop plotareafill=FFFBF3 --prop plotarea.border="E8D8B8:0.75" \
--prop chartareafill=FDF6E8 --prop chartarea.border="E8D8B8:0.75" \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# Specimen 3 · Courier New (monospace — data, telemetry, engineering)
officecli add "$FILE" "/4-Typography" --type chart \
--prop chartType=histogram --prop title="Courier New · data mono" \
--prop title.color=1A3A1A --prop title.size=16 --prop title.bold=true \
--prop title.font="Courier New" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=2F8F4F \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=2F8F4F --prop axisTitle.size=11 \
--prop axisTitle.font="Courier New" \
--prop axisfont="10:3A5A3A:Courier New" \
--prop gridlineColor=E0EDE0 \
--prop plotareafill=F7FBF7 --prop plotarea.border="C8DCC8:0.75" \
--prop chartareafill=F0F7F0 --prop chartarea.border="C8DCC8:0.75" \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# Specimen 4 · Verdana (friendly sans — onboarding, public-facing UI)
officecli add "$FILE" "/4-Typography" --type chart \
--prop chartType=histogram --prop title="Verdana · friendly sans" \
--prop title.color=4A2B6A --prop title.size=16 --prop title.bold=true \
--prop title.font="Verdana" \
--prop series1="Samples:$BELL_CSV" --prop binCount=18 --prop fill=8E4DBB \
--prop xAxisTitle="Score" --prop yAxisTitle="Count" \
--prop axisTitle.color=8E4DBB --prop axisTitle.size=11 \
--prop axisTitle.font="Verdana" \
--prop axisfont="10:6B4A8A:Verdana" \
--prop gridlineColor=ECE0F4 \
--prop plotareafill=FCF7FF --prop plotarea.border="D8C4E8:0.75" \
--prop chartareafill=F6EDFA --prop chartarea.border="D8C4E8:0.75" \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# ==========================================================================
# Sheet 5: "5-ML Dashboard"
#
# A cohesive six-chart "Production ML Model Report". Every chart wears the
# same corporate dashboard uniform but each shows a different slice of the
# model's behavior, with a different color + binning strategy.
#
# Row 1: Inference latency (ms) | Prediction confidence (%)
# Row 2: |Residual| (logit) | Token length (chars)
# Row 3: GPU utilization (%) | Cost per request ($ × 0.001)
# ==========================================================================
echo "--- 5-ML Dashboard ---"
officecli add "$FILE" / --type sheet --prop name="5-ML Dashboard"
DASH=(
--prop title.color=1F2937 --prop title.size=12 --prop title.bold=true
--prop title.font="Helvetica Neue"
--prop axisTitle.color=6B7280 --prop axisTitle.size=9
--prop axisTitle.font="Helvetica Neue"
--prop axisfont="8:6B7280:Helvetica Neue"
--prop gridlineColor=F0F0F0
--prop plotareafill=FFFFFF --prop plotarea.border="E5E7EB:0.75"
--prop chartareafill=F9FAFB --prop chartarea.border="E5E7EB:0.75"
--prop axisline="9CA3AF:0.75"
--prop dataLabels=false
)
# 1 · Inference Latency — heavy-tail, overflow-fenced, red for "watch this"
officecli add "$FILE" "/5-ML Dashboard" --type chart \
--prop chartType=histogram --prop title="Inference Latency · p50-p99 (ms)" \
--prop series1="Latency:$LATENCY_CSV" --prop binSize=25 --prop overflowBin=300 --prop fill=EF4444 \
--prop series.shadow=EF4444-4-45-2-25 \
--prop xAxisTitle="Latency (ms)" --prop yAxisTitle="Requests" "${DASH[@]}" \
--prop x=0 --prop y=0 --prop width=13 --prop height=18
# 2 · Prediction Confidence — beta-like, axismin/max locked to 0..100
officecli add "$FILE" "/5-ML Dashboard" --type chart \
--prop chartType=histogram --prop title="Prediction Confidence" \
--prop series1="Confidence:$CONFIDENCE_CSV" --prop binSize=5 --prop fill=10B981 \
--prop axismin=0 --prop majorunit=50 \
--prop xAxisTitle="Softmax confidence (%)" --prop yAxisTitle="Samples" "${DASH[@]}" \
--prop x=14 --prop y=0 --prop width=13 --prop height=18
# 3 · Residual Magnitude — half-normal, intervalClosed=l so bin=0 catches zeros
officecli add "$FILE" "/5-ML Dashboard" --type chart \
--prop chartType=histogram --prop title="|Residual| · model calibration" \
--prop series1="Residual:$ERROR_MAG_CSV" --prop binSize=0.25 --prop intervalClosed=l --prop fill=F59E0B \
--prop xAxisTitle="|y - ŷ| (logit)" --prop yAxisTitle="Samples" "${DASH[@]}" \
--prop x=0 --prop y=19 --prop width=13 --prop height=18
# 4 · Token Length — bimodal (short prompts vs long prompts)
officecli add "$FILE" "/5-ML Dashboard" --type chart \
--prop chartType=histogram --prop title="Token Length · short vs long prompts" \
--prop series1="Tokens:$TOKEN_CSV" --prop binCount=24 --prop fill=6366F1 \
--prop xAxisTitle="Tokens" --prop yAxisTitle="Requests" "${DASH[@]}" \
--prop x=14 --prop y=19 --prop width=13 --prop height=18
# 5 · GPU Utilization — locked axis range so dashboard charts share scale
officecli add "$FILE" "/5-ML Dashboard" --type chart \
--prop chartType=histogram --prop title="GPU Utilization" \
--prop series1="GPU:$GPU_CSV" --prop binSize=5 --prop fill=8B5CF6 \
--prop axismin=0 --prop axismax=50 --prop majorunit=10 \
--prop xAxisTitle="Utilization (%)" --prop yAxisTitle="Samples" "${DASH[@]}" \
--prop x=0 --prop y=38 --prop width=13 --prop height=18
# 6 · Cost per Request — log-normal, overflow-fenced, data labels with numfmt.
# DASH carries dataLabels=false; this chart overrides it to true (last wins).
officecli add "$FILE" "/5-ML Dashboard" --type chart \
--prop chartType=histogram --prop title="Cost per Request (\$ × 0.001)" \
--prop series1="Cost:$COST_CSV" --prop binSize=5 --prop overflowBin=120 --prop fill=EC4899 \
--prop xAxisTitle="Cost (m\$)" --prop yAxisTitle="Requests" "${DASH[@]}" \
--prop dataLabels=true --prop datalabels.numfmt=0 \
--prop x=14 --prop y=38 --prop width=13 --prop height=18
officecli close "$FILE"
officecli validate "$FILE"
echo "Done! Generated: $FILE"
echo " 6 sheets, 29 histograms total"
Binary file not shown.
+346
View File
@@ -0,0 +1,346 @@
# Line Charts Showcase
This demo consists of three files that work together:
- **charts-line.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-line.xlsx** — The generated workbook with 9 sheets (1 data + 8 chart sheets, 32 charts total).
- **charts-line.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-line.py
# → charts-line.xlsx
```
## Chart Sheets
### Sheet: 1-Line Fundamentals
Four basic line charts covering every data input method and marker fundamentals.
```bash
# Inline named series with axis titles
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop series1="Product A:120,180,210,250" \
--prop series2="Product B:90,140,160,200" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop catTitle=Quarter --prop axisTitle=Revenue \
--prop axisfont=9:58626E:Arial --prop gridlines=D9D9D9:0.5:dot
# Cell-range series (dotted syntax) with markers
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop series1.name=East \
--prop series1.values=Sheet1!B2:B13 \
--prop series1.categories=Sheet1!A2:A13 \
--prop showMarkers=true --prop marker=circle:6:2E75B6 \
--prop minorGridlines=EEEEEE:0.3:dot
# dataRange (auto-reads headers) with diamond markers
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop dataRange=Sheet1!A1:E13 \
--prop showMarkers=true --prop marker=diamond:5:333333 \
--prop legend=bottom --prop legendfont=9:58626E:Calibri
# Inline data shorthand with marker=none
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop 'data=Actual:80,120,160;Target:100,130,160' \
--prop marker=none --prop legend=right
```
**Features:** `series1=Name:v1,v2`, `series1.name`/`.values`/`.categories` (cell range), `dataRange`, `data` (shorthand), `categories`, `colors`, `catTitle`, `axisTitle`, `axisfont`, `gridlines`, `minorGridlines`, `showMarkers`, `marker` (circle, diamond, none), `legend` (bottom, right), `legendfont`
### Sheet: 2-Line Styles
Four charts demonstrating visual styling — smoothing, dash patterns, markers, and transparency.
```bash
# Smooth curves with shadow, axes hidden
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop smooth=true --prop lineWidth=2.5 \
--prop gridlines=none --prop axisVisible=false \
--prop series.shadow=000000-4-315-2-40
# Dashed lines (applies to all series)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop lineDash=dash --prop lineWidth=2
# Marker styles with series outline
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop showMarkers=true --prop marker=square:7:4472C4 \
--prop series.outline=FFFFFF-0.5
# Transparent lines on gradient plot area
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop lineWidth=3 --prop smooth=true \
--prop transparency=30 \
--prop plotFill=F0F4F8-D6E4F0:90 --prop chartFill=FFFFFF \
--prop title.font=Georgia --prop title.size=14 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop roundedCorners=true
```
**Features:** `smooth`, `lineWidth`, `lineDash` (solid/dot/dash/dashdot/longdash/longdashdot/longdashdotdot), `marker` (square), `series.shadow` (color-blur-angle-dist-opacity), `series.outline`, `transparency`, `plotFill` (gradient), `chartFill`, `title.font`/`.size`/`.color`/`.bold`, `roundedCorners`, `gridlines=none`, `axisVisible=false`
### Sheet: 3-Line Variants
Four charts covering all line chart type variants.
```bash
# Stacked line — cumulative values
officecli add data.xlsx /Sheet --type chart \
--prop chartType=lineStacked \
--prop majorTickMark=outside --prop tickLabelPos=low
# 100% stacked line — proportional
officecli add data.xlsx /Sheet --type chart \
--prop chartType=linePercentStacked \
--prop axisNumFmt=0%
# 3D line with perspective
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line3d \
--prop view3d=15,20,30 --prop style=3
# Stacked line with data table
officecli add data.xlsx /Sheet --type chart \
--prop chartType=lineStacked \
--prop dataTable=true --prop legend=none
```
**Features:** `lineStacked`, `linePercentStacked`, `line3d`, `majorTickMark`, `tickLabelPos`, `axisNumFmt`, `view3d` (rotX,rotY,perspective), `style` (preset 1-48), `dataTable`, `legend=none`
### Sheet: 4-Axis & Gridlines
Four charts demonstrating every axis and gridline configuration. The first chart also shows the axis sub-element `Set` path, which lets you modify an existing chart's axis after creation.
```bash
# Custom axis scaling with axis lines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop axisMin=80 --prop axisMax=220 \
--prop majorUnit=20 --prop minorUnit=10 \
--prop axisLine=C00000:1.5:solid --prop catAxisLine=2E75B6:1.5:solid
# Post-creation axis Set via sub-element path
officecli set data.xlsx "/4-Axis & Gridlines/chart[1]/axis[@role=value]" \
--prop min=80 --prop max=220 \
--prop format="#,##0" \
--prop majorGridlines=true \
--prop labelRotation=0
# Logarithmic scale
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop logBase=10 \
--prop marker=triangle:7:C00000
# Reversed value axis
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop axisReverse=true
# Display units with tick marks
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop dispUnits=thousands \
--prop majorTickMark=outside --prop minorTickMark=inside \
--prop marker=star:7:2E75B6
```
**Features:** `axisMin`, `axisMax`, `majorUnit`, `minorUnit`, `axisLine`, `catAxisLine`, `logBase` (logarithmic scale), `axisReverse` (flip direction), `dispUnits` (thousands/millions), `majorTickMark`, `minorTickMark`, `marker` (triangle, star); axis sub-element Set path `axis[@role=value]` with `min`, `max`, `format`, `majorGridlines`, `labelRotation`
### Sheet: 5-Labels & Legend
Four charts demonstrating data label and legend customization.
```bash
# Data labels with number format
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=9:333333:true \
--prop dataLabels.numFmt=#,##0 \
--prop dataLabels.separator=": "
# Custom individual data labels (hide some, highlight peak with color + label)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop dataLabels=true \
--prop dataLabel1.delete=true --prop dataLabel2.delete=true \
--prop point4.color=C00000 \
--prop dataLabel4.text="Peak: 210" --prop dataLabel4.y=0.15
# Legend overlay
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop legend=top --prop legend.overlay=true \
--prop legendfont=10:1F4E79:Calibri
# Manual layout — plotArea, title, legend positioning
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop plotArea.x=0.12 --prop plotArea.y=0.18 \
--prop plotArea.w=0.82 --prop plotArea.h=0.55 \
--prop title.x=0.25 --prop title.y=0.02 \
--prop legend.x=0.15 --prop legend.y=0.82 \
--prop legend.w=0.7 --prop legend.h=0.12
```
**Features:** `dataLabels`, `labelPos` (top/center/insideEnd/outsideEnd/bestFit), `labelFont`, `dataLabels.numFmt`, `dataLabels.separator`, `dataLabel{N}.delete`, `dataLabel{N}.text`, `dataLabel{N}.y` (manual label position), `point{N}.color` (individual point color), `legend` (top), `legend.overlay`, `legendfont`, `plotArea.x/y/w/h`, `title.x/y`, `legend.x/y/w/h`
### Sheet: 6-Effects & Advanced
Four charts demonstrating advanced features — secondary axis, reference lines, effects, and conditional coloring.
```bash
# Secondary axis (dual scale)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop secondaryAxis=2 \
--prop series1="Revenue:120,180,250,310" \
--prop series2="Growth %:50,33,39,24"
# Reference line with longdash style
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop referenceLine=150:FF0000:1.5:dash \
--prop lineDash=longdash
# Title glow/shadow effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop title.glow=4472C4-8-60 \
--prop title.shadow=000000-3-315-2-40 \
--prop series.shadow=000000-3-315-1-30
# Conditional coloring with chart/plot borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop colorRule=0:C00000:70AD47 \
--prop referenceLine=0:888888:1:solid \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot
```
**Features:** `secondaryAxis` (1-based series indices), `referenceLine` (value:color:width:dash), `title.glow` (color-radius-opacity), `title.shadow` (color-blur-angle-dist-opacity), `series.shadow`, `colorRule` (threshold:belowColor:aboveColor), `chartArea.border`, `plotArea.border`
### Sheet: 7-Line Elements
Four charts demonstrating line-chart-specific structural elements.
```bash
# Drop lines — vertical lines from points to X axis
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop dropLines=true
# High-low lines — connect highest and lowest series per category
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop hiLowLines=true
# Up-down bars with custom gain/loss colors
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop updownbars=100:70AD47:C00000
# 3D line with gap depth
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line3d \
--prop gapDepth=300
```
**Features:** `dropLines` (vertical drop to axis), `hiLowLines` (high-low connectors), `updownbars` (gapWidth:upColor:downColor), `gapDepth` (3D depth spacing 0-500)
### Sheet: 8-Axis Extras
Four charts demonstrating additional axis behaviour: category-axis crossing point, blank-cell handling, and value-axis position.
```bash
# crossesAt — value axis crosses category axis at a specific Y value
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop title="crossesAt — axis baseline at 50" \
--prop series1="Score:40,65,55,80,45,90,70" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun,Jul \
--prop crossesAt=50 \
--prop lineWidth=2 --prop marker=circle --prop markerSize=6
# dispBlanksAs=span — connect across null/blank data points
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop title="dispBlanksAs=span (connect gaps)" \
--prop series1="Revenue:100,120,130,150,160" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop dispBlanksAs=span \
--prop lineWidth=2 --prop marker=circle --prop markerSize=6
# dispBlanksAs=zero + crossesAt=0
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop title="dispBlanksAs=zero + crossesAt=0" \
--prop series1="Revenue:100,120,130,150,160" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop dispBlanksAs=zero \
--prop crossesAt=0 \
--prop lineWidth=2 --prop marker=circle --prop markerSize=6
# crosses=max — value axis appears at the far (right) end of the category axis
officecli add data.xlsx /Sheet --type chart \
--prop chartType=line \
--prop title="crosses=max (value axis at far end)" \
--prop series1="Index:45,60,52,75,80,68,90" \
--prop categories=Mon,Tue,Wed,Thu,Fri,Sat,Sun \
--prop crosses=max \
--prop lineWidth=2
```
**Features:** `crossesAt=50` (value axis crosses the category axis at y=50, shifting bars/lines that fall below that value below the midline), `dispBlanksAs=span` (connect across null data points with a straight line), `dispBlanksAs=zero` (render blank cells as zero), `dispBlanksAs=gap` (leave a hole), `crosses=max` (value axis at the far end of the category axis; also: `autoZero`, `min`)
## Complete Feature Coverage
| Feature | Sheet |
|---------|-------|
| **Chart types:** line, lineStacked, linePercentStacked, line3d | 1, 3 |
| **Data input:** series, dataRange, data, series.name/values/categories | 1 |
| **Line styling:** smooth, lineWidth, lineDash, colors | 2 |
| **Markers:** circle, diamond, square, triangle, star, none, auto | 1, 2, 4 |
| **Axis scaling:** axisMin/Max, majorUnit, minorUnit | 4 |
| **Axis features:** logBase, axisReverse, dispUnits, axisNumFmt | 3, 4 |
| **Axis lines:** axisLine, catAxisLine | 4 |
| **Axis visibility:** axisVisible | 2 |
| **Tick marks:** majorTickMark, minorTickMark, tickLabelPos | 3, 4 |
| **Gridlines:** gridlines, minorGridlines, gridlines=none | 1, 2, 4 |
| **Data labels:** dataLabels, labelPos, labelFont, numFmt, separator | 5 |
| **Custom labels:** dataLabel{N}.text, dataLabel{N}.delete, dataLabel{N}.y | 5 |
| **Point color:** point{N}.color | 5 |
| **Legend:** position, legendfont, legend.overlay, legend=none | 1, 3, 5 |
| **Layout:** plotArea.x/y/w/h, title.x/y, legend.x/y/w/h | 5 |
| **Effects:** series.shadow, series.outline, transparency | 2, 6 |
| **Title styling:** font, size, color, bold, glow, shadow | 2, 6 |
| **Fills:** plotFill, chartFill (solid + gradient) | 2, 3, 6 |
| **Borders:** chartArea.border, plotArea.border | 6 |
| **Advanced:** secondaryAxis, referenceLine, colorRule | 6 |
| **Line elements:** dropLines, hiLowLines, upDownBars | 7 |
| **3D:** view3d, gapDepth, style | 3, 7 |
| **Other:** dataTable, roundedCorners | 2, 3 |
| **Axis sub-element Set:** axis[@role=value] min/max/format/majorGridlines/labelRotation | 4 |
| **Axis extras:** crossesAt, crosses (max/autoZero/min), dispBlanksAs (span/zero/gap) | 8 |
## Inspect the Generated File
```bash
officecli query charts-line.xlsx chart
officecli get charts-line.xlsx "/1-Line Fundamentals/chart[1]"
```
+671
View File
@@ -0,0 +1,671 @@
#!/usr/bin/env python3
"""
Line Charts Showcase — line, lineStacked, linePercentStacked, and line3d with all variations.
Generates: charts-line.xlsx
Every line chart feature officecli supports is demonstrated at least once:
line styles, markers, smoothing, dash patterns, axis scaling, gridlines,
data labels, legend positioning, reference lines, secondary axis, error bars,
gradients, transparency, shadows, manual layout, data table, and 3D rotation.
9 sheets (Sheet1 data + 8 chart sheets), 32 charts total.
1-Line Fundamentals 4 charts — data input variants, markers, cell-range series
2-Line Styles 4 charts — lineWidth, lineDash, smooth, color palettes
3-Line Variants 4 charts — lineStacked, linePercentStacked, line3d
4-Axis & Gridlines 4 charts — axis scaling, log scale, reverse, tick marks
5-Labels & Legend 4 charts — data labels, custom labels, legend layout
6-Effects & Advanced 4 charts — shadows, gradients, secondary axis, reference lines
7-Line Elements 4 charts — drop lines, hi-low lines, up-down bars, 3D gap depth
8-Axis Extras 4 charts — crossesAt, dispBlanksAs, crosses=max
SDK twin of charts-line.sh (officecli CLI). Both produce an equivalent
charts-line.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every cell write and
chart is shipped over the named pipe via `doc.batch(...)` / `doc.send(...)`.
Each item is the same `{"command","parent"/"path","type","props"}` dict you'd
put in an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-line.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-line.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(sheet, **props):
"""One `add chart` item in batch-shape, parented to the named sheet."""
return {"command": "add", "parent": f"/{sheet}", "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Source data — shared across all charts
# ======================================================================
print("--- Populating source data ---")
data_items = []
for j, h in enumerate(["Month", "East", "South", "North", "West"]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}1",
"props": {"text": h, "bold": "true"}})
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
east = [120, 135, 148, 162, 155, 178, 195, 210, 188, 172, 165, 198]
south = [95, 108, 115, 128, 142, 155, 168, 175, 160, 148, 135, 158]
north = [88, 92, 105, 118, 125, 138, 145, 152, 140, 130, 122, 142]
west = [110, 118, 130, 145, 138, 162, 175, 190, 170, 155, 148, 180]
for i in range(12):
r = i + 2
for j, val in enumerate([months[i], east[i], south[i], north[i], west[i]]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCDE'[j]}{r}",
"props": {"text": str(val)}})
doc.batch(data_items)
# ======================================================================
# Sheet: 1-Line Fundamentals
# ======================================================================
print("--- 1-Line Fundamentals ---")
items = [add_sheet("1-Line Fundamentals")]
# Chart 1: Basic line with inline named series and categories
# Features: chartType=line, inline series (series1=Name:v1,v2,...),
# categories, colors, catTitle, axisTitle, axisfont, gridlines
items.append(chart("1-Line Fundamentals",
chartType="line",
title="Quarterly Revenue",
series1="Product A:120,180,210,250",
series2="Product B:90,140,160,200",
series3="Product C:60,85,110,145",
categories="Q1,Q2,Q3,Q4",
colors="4472C4,ED7D31,70AD47",
x="0", y="0", width="12", height="18",
catTitle="Quarter", axisTitle="Revenue",
axisfont="9:C00000:Arial",
gridlines="D9D9D9:0.5:dot"))
# Chart 2: Line with cell-range series (dotted syntax) and markers
# Features: series.name/values/categories (cell range via dotted syntax),
# showMarkers, marker (style:size:color), minorGridlines
items.append(chart("1-Line Fundamentals",
chartType="line",
title="East Region Trend",
**{"series1.name": "East",
"series1.values": "Sheet1!B2:B13",
"series1.categories": "Sheet1!A2:A13"},
x="13", y="0", width="12", height="18",
showMarkers="true", marker="circle:6:2E75B6",
gridlines="D9D9D9:0.5:dot",
minorGridlines="EEEEEE:0.3:dot"))
# Chart 3: Line from dataRange with all four regions
# Features: dataRange (auto-reads headers as series names), marker=diamond,
# lineWidth, legend=bottom, legendfont
items.append(chart("1-Line Fundamentals",
chartType="line",
title="All Regions — Full Year",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
colors="2E75B6,70AD47,FFC000,C00000",
showMarkers="true", marker="diamond:5:333333",
lineWidth="2",
legend="bottom",
legendfont="9:58626E:Calibri"))
# Chart 4: Line with inline data shorthand and marker=none
# Features: data (inline shorthand Name:v1;Name2:v2), marker=none, legend=right
items.append(chart("1-Line Fundamentals",
chartType="line",
title="Simple Two-Series",
data="Actual:80,120,160,200,240;Target:100,130,160,190,220",
categories="Week 1,Week 2,Week 3,Week 4,Week 5",
colors="0070C0,FF0000",
x="13", y="19", width="12", height="18",
marker="none",
legend="right"))
doc.batch(items)
# ======================================================================
# Sheet: 2-Line Styles
# ======================================================================
print("--- 2-Line Styles ---")
items = [add_sheet("2-Line Styles")]
# Chart 1: Smooth line with thick width and shadow
# Features: smooth=true (Bezier curves), lineWidth=2.5, gridlines=none,
# axisVisible=false (hide both axes for sparkline-like minimal look),
# series.shadow (color-blur-angle-dist-opacity)
items.append(chart("2-Line Styles",
chartType="line",
title="Smooth Curves with Shadow",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="12", height="18",
smooth="true", lineWidth="2.5",
colors="0070C0,00B050,FFC000,FF0000",
gridlines="none",
axisVisible="false",
**{"series.shadow": "000000-4-315-2-40"}))
# Chart 2: Dashed lines — all dash styles demonstrated
# Note: lineDash applies to ALL series. Supported values:
# solid, dot, dash, dashdot, longdash, longdashdot, longdashdotdot
# Features: lineDash (applied globally to all series), lineWidth
items.append(chart("2-Line Styles",
chartType="line",
title="Dash Pattern Gallery",
series1="solid:120,135,148,162,155",
series2="dot:95,108,115,128,142",
series3="dash:88,92,105,118,125",
series4="dashdot:110,118,130,145,138",
categories="Jan,Feb,Mar,Apr,May",
colors="2E75B6,ED7D31,70AD47,FFC000",
x="13", y="0", width="12", height="18",
lineDash="dash", lineWidth="2",
legend="bottom"))
# Chart 3: Multiple marker styles — circle, square, triangle, star
# Note: marker applies to ALL series. Supported styles:
# circle, diamond, square, triangle, star, x, plus, dash, dot, none
# Features: marker=square:7:color (style:size:fillColor),
# series.outline (white border around markers/lines)
items.append(chart("2-Line Styles",
chartType="line",
title="Marker Style Showcase",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
showMarkers="true", marker="square:7:4472C4",
lineWidth="1.5",
colors="4472C4,ED7D31,70AD47,FFC000",
**{"series.outline": "FFFFFF-0.5"}))
# Chart 4: Transparent lines with gradient plot area and styled title
# Features: transparency=30 (30% transparent), plotFill gradient,
# chartFill, title.font/size/color/bold, roundedCorners
items.append(chart("2-Line Styles",
chartType="line",
title="Translucent Lines on Gradient",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
lineWidth="3", smooth="true",
transparency="30",
plotFill="F0F4F8-D6E4F0:90",
chartFill="FFFFFF",
colors="1F4E79,2E75B6,5B9BD5,9DC3E6",
**{"title.font": "Georgia", "title.size": "14",
"title.color": "1F4E79", "title.bold": "true"},
roundedCorners="true"))
doc.batch(items)
# ======================================================================
# Sheet: 3-Line Variants
# ======================================================================
print("--- 3-Line Variants ---")
items = [add_sheet("3-Line Variants")]
# Chart 1: Stacked line chart
# Features: lineStacked (cumulative stacking), majorTickMark=outside, tickLabelPos=low
items.append(chart("3-Line Variants",
chartType="lineStacked",
title="Cumulative Sales by Region",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="12", height="18",
catTitle="Month", axisTitle="Cumulative",
colors="4472C4,ED7D31,70AD47,FFC000",
majorTickMark="outside", tickLabelPos="low"))
# Chart 2: 100% stacked line chart with axis number format
# Features: linePercentStacked (each month sums to 100%),
# axisNumFmt (value axis number format)
items.append(chart("3-Line Variants",
chartType="linePercentStacked",
title="Regional Contribution %",
dataRange="Sheet1!A1:E13",
x="13", y="0", width="12", height="18",
colors="1F4E79,2E75B6,9DC3E6,BDD7EE",
axisNumFmt="0%",
legend="right",
gridlines="E0E0E0:0.5:solid"))
# Chart 3: 3D line chart with perspective
# Features: line3d (3D line chart), view3d (rotX,rotY,perspective),
# style/styleId (preset chart style 1-48)
items.append(chart("3-Line Variants",
chartType="line3d",
title="3D Regional Trends",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
view3d="15,20,30",
colors="4472C4,ED7D31,70AD47,FFC000",
chartFill="F8F8F8",
style="3"))
# Chart 4: Stacked line with area fill and data table
# Features: dataTable=true (show value table below chart),
# legend=none (hidden because data table shows series names)
items.append(chart("3-Line Variants",
chartType="lineStacked",
title="Stacked with Data Table",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
dataTable="true",
legend="none",
lineWidth="1.5",
colors="2E75B6,ED7D31,70AD47,FFC000",
plotFill="FAFAFA"))
doc.batch(items)
# ======================================================================
# Sheet: 4-Axis & Gridlines
# ======================================================================
print("--- 4-Axis & Gridlines ---")
items = [add_sheet("4-Axis & Gridlines")]
# Chart 1: Custom axis scaling — min, max, majorUnit
# Features: axisMin, axisMax, majorUnit, minorUnit,
# axisLine (value axis line styling — red), catAxisLine (category axis line — blue)
items.append(chart("4-Axis & Gridlines",
chartType="line",
title="Custom Axis Scale (80220)",
dataRange="Sheet1!A1:E13",
x="0", y="0", width="12", height="18",
axisMin="80", axisMax="220", majorUnit="20",
minorUnit="10",
showMarkers="true", marker="circle:4:4472C4",
gridlines="D0D0D0:0.5:solid",
minorGridlines="EEEEEE:0.3:dot",
axisLine="C00000:1.5:solid",
catAxisLine="2E75B6:1.5:solid"))
doc.batch(items)
# Demonstrate chart-axis element (path: /SheetName/chart[N]/axis[@role=ROLE]).
# Properties: min, max, format, majorGridlines, labelRotation.
# These are the same semantics as axisMin/axisMax/gridlines/labelrotation at
# chart level but applied through the dedicated sub-element path, which also
# exposes role, dispUnits, majorUnit, title, visible, logBase.
doc.send({"command": "set",
"path": "/4-Axis & Gridlines/chart[1]/axis[@role=value]",
"props": {"min": "80", "max": "220",
"format": "#,##0",
"majorGridlines": "true",
"labelRotation": "0"}})
# Chart 2: Logarithmic scale with display units
# Features: logBase=10 (logarithmic scale), marker=triangle
items = [chart("4-Axis & Gridlines",
chartType="line",
title="Exponential Growth (Log Scale)",
series1="Growth:1,5,25,125,625,3125",
categories="Year 1,Year 2,Year 3,Year 4,Year 5,Year 6",
x="13", y="0", width="12", height="18",
logBase="10",
colors="C00000",
lineWidth="2.5",
showMarkers="true", marker="triangle:7:C00000",
axisTitle="Value (log)",
catTitle="Year",
gridlines="E0E0E0:0.5:dash")]
# Chart 3: Reversed axis and hidden axes
# Features: axisReverse=true (value axis direction flipped), smooth + markers together
items.append(chart("4-Axis & Gridlines",
chartType="line",
title="Reversed Value Axis",
series1="Depth:0,50,120,200,350,500",
categories="Station A,Station B,Station C,Station D,Station E,Station F",
x="0", y="19", width="12", height="18",
axisReverse="true",
colors="0070C0",
lineWidth="2",
showMarkers="true", marker="diamond:6:0070C0",
smooth="true",
axisTitle="Depth (m)",
gridlines="D9D9D9:0.5:solid"))
# Chart 4: Display units and tick mark styles
# Features: dispUnits=thousands (display units label),
# majorTickMark=outside, minorTickMark=inside, marker=star
items.append(chart("4-Axis & Gridlines",
chartType="line",
title="Revenue (in Thousands)",
series1="Revenue:12000,18500,22000,31000,45000,52000",
series2="Cost:8000,11000,14000,19500,28000,33000",
categories="2020,2021,2022,2023,2024,2025",
x="13", y="19", width="12", height="18",
dispUnits="thousands",
colors="2E75B6,C00000",
lineWidth="2",
majorTickMark="outside", minorTickMark="inside",
showMarkers="true", marker="star:7:2E75B6",
catTitle="Year", axisTitle="Amount (K)"))
doc.batch(items)
# ======================================================================
# Sheet: 5-Labels & Legend
# ======================================================================
print("--- 5-Labels & Legend ---")
items = [add_sheet("5-Labels & Legend")]
# Chart 1: Data labels at various positions with number format
# Features: dataLabels=true, labelPos=top, labelFont (size:color:bold),
# dataLabels.numFmt (number format), dataLabels.separator
items.append(chart("5-Labels & Legend",
chartType="line",
title="Sales with Labels",
series1="Revenue:120,180,210,250,280",
categories="Jan,Feb,Mar,Apr,May",
x="0", y="0", width="12", height="18",
colors="4472C4",
lineWidth="2",
showMarkers="true", marker="circle:6:4472C4",
dataLabels="true", labelPos="top",
labelFont="9:333333:true",
**{"dataLabels.numFmt": "#,##0",
"dataLabels.separator": ": "}))
# Chart 2: Custom individual data labels (highlight peak)
# Features: dataLabel{N}.delete (hide specific labels),
# dataLabel{N}.text (custom text on specific point),
# point{N}.color (highlight individual data point marker in red),
# dataLabel{N}.y (manual vertical position of individual label, 0-1 fraction)
items.append(chart("5-Labels & Legend",
chartType="line",
title="Peak Highlight",
series1="Sales:88,120,165,210,195,178",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="0", width="12", height="18",
colors="2E75B6",
lineWidth="2.5", smooth="true",
showMarkers="true", marker="circle:5:2E75B6",
dataLabels="true", labelPos="top",
**{"dataLabel1.delete": "true", "dataLabel2.delete": "true",
"point4.color": "C00000",
"dataLabel4.text": "Peak: 210",
"dataLabel4.y": "0.15",
"dataLabel5.delete": "true", "dataLabel6.delete": "true"}))
# Chart 3: Legend positioning and overlay
# Features: legend=top, legend.overlay=true (legend overlays chart area),
# legendfont (size:color:fontname)
items.append(chart("5-Labels & Legend",
chartType="line",
title="Legend Overlay on Chart",
dataRange="Sheet1!A1:E13",
x="0", y="19", width="12", height="18",
colors="4472C4,ED7D31,70AD47,FFC000",
lineWidth="2",
legend="top",
**{"legend.overlay": "true"},
legendfont="10:1F4E79:Calibri",
plotFill="F5F5F5"))
# Chart 4: Manual layout — plotArea, title, and legend positioning
# Features: plotArea.x/y/w/h (plot area manual layout, 0-1 fraction),
# title.x/y (title position), legend.x/y/w/h (legend position/size)
items.append(chart("5-Labels & Legend",
chartType="line",
title="Manual Layout Control",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
colors="2E75B6,ED7D31,70AD47,FFC000",
lineWidth="1.5",
**{"plotArea.x": "0.12", "plotArea.y": "0.18",
"plotArea.w": "0.82", "plotArea.h": "0.55",
"title.x": "0.25", "title.y": "0.02",
"legend.x": "0.15", "legend.y": "0.82",
"legend.w": "0.7", "legend.h": "0.12",
"title.font": "Arial", "title.size": "13",
"title.bold": "true"}))
doc.batch(items)
# ======================================================================
# Sheet: 6-Effects & Advanced
# ======================================================================
print("--- 6-Effects & Advanced ---")
items = [add_sheet("6-Effects & Advanced")]
# Chart 1: Secondary axis — two series on different scales
# Features: secondaryAxis=2 (series 2 on right-hand axis), dual-scale visualization
items.append(chart("6-Effects & Advanced",
chartType="line",
title="Revenue vs Growth Rate",
series1="Revenue:120,180,250,310,380,420",
series2="Growth %:50,33,39,24,23,11",
categories="2020,2021,2022,2023,2024,2025",
x="0", y="0", width="12", height="18",
secondaryAxis="2",
colors="2E75B6,C00000",
lineWidth="2.5",
showMarkers="true", marker="circle:6:2E75B6",
catTitle="Year", axisTitle="Revenue",
dataLabels="true", labelPos="top"))
# Chart 2: Reference line (target/threshold) with error bars
# referenceLine format: value:color:width:dash
# - value: the threshold/target value on the Y axis
# - color: hex RGB (no #)
# - width: line thickness in pt (default 1.5)
# - dash: solid/dot/dash/dashdot/longdash
# Features: referenceLine (horizontal target line), lineDash=longdash
items.append(chart("6-Effects & Advanced",
chartType="line",
title="vs Target (150)",
dataRange="Sheet1!A1:C13",
x="13", y="0", width="12", height="18",
colors="4472C4,70AD47",
lineWidth="1.5",
referenceLine="150:FF0000:1.5:dash",
showMarkers="true", marker="circle:4:4472C4",
legend="bottom",
lineDash="longdash"))
# Chart 3: Title glow/shadow effects with per-series gradients
# Features: title.glow (color-radius-opacity), title.shadow,
# series.shadow on line charts, plotFill + chartFill
items.append(chart("6-Effects & Advanced",
chartType="line",
title="Glow & Shadow Effects",
series1="East:120,135,148,162,155,178",
series2="West:110,118,130,145,138,162",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="0", y="19", width="12", height="18",
lineWidth="3", smooth="true",
colors="4472C4,ED7D31",
**{"title.glow": "4472C4-8-60",
"title.shadow": "000000-3-315-2-40",
"title.font": "Calibri", "title.size": "16",
"title.bold": "true", "title.color": "1F4E79",
"series.shadow": "000000-3-315-1-30"},
plotFill="F0F4F8", chartFill="FFFFFF"))
# Chart 4: Conditional coloring with chart/plot borders
# colorRule format: threshold:belowColor:aboveColor
# - values below 0 → red (C00000), above 0 → green (70AD47)
# Features: colorRule (threshold-based conditional coloring),
# chartArea.border, plotArea.border, referenceLine=0 (zero line)
items.append(chart("6-Effects & Advanced",
chartType="line",
title="Conditional Colors & Borders",
series1="Profit:80,120,-30,160,-50,200,140,-20,180,90",
categories="Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct",
x="13", y="19", width="12", height="18",
colors="2E75B6",
lineWidth="2",
showMarkers="true", marker="circle:6:2E75B6",
colorRule="0:C00000:70AD47",
referenceLine="0:888888:1:solid",
**{"chartArea.border": "D0D0D0:1:solid",
"plotArea.border": "E0E0E0:0.5:dot"},
dataLabels="true", labelPos="top",
labelFont="8:666666:false"))
doc.batch(items)
# ======================================================================
# Sheet: 7-Line Elements
# ======================================================================
print("--- 7-Line Elements ---")
items = [add_sheet("7-Line Elements")]
# Chart 1: Drop lines — vertical lines from data points to category axis
# Features: dropLines=true (simple toggle — default thin gray lines)
items.append(chart("7-Line Elements",
chartType="line",
title="Drop Lines",
dataRange="Sheet1!A1:C13",
x="0", y="0", width="12", height="18",
colors="4472C4,ED7D31",
showMarkers="true", marker="circle:5:4472C4",
dropLines="true",
legend="bottom"))
# Chart 2: High-low lines — connect highest and lowest series at each point
# Features: hiLowLines=true (lines connecting highest and lowest values)
items.append(chart("7-Line Elements",
chartType="line",
title="High-Low Lines",
series1="High:210,195,220,240,230,250",
series2="Low:150,135,160,170,155,180",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="0", width="12", height="18",
colors="2E75B6,C00000",
showMarkers="true", marker="diamond:5:2E75B6",
hiLowLines="true",
legend="bottom"))
# Chart 3: Up-down bars with custom colors — show gain/loss between series
# updownbars format: gapWidth:upColor:downColor
# - gapWidth: gap between bars (0-500, default 150)
# - upColor: fill color for increase (Close > Open)
# - downColor: fill color for decrease (Close < Open)
# Features: updownbars with custom colors (gain=green, loss=red)
items.append(chart("7-Line Elements",
chartType="line",
title="Up-Down Bars (Gain/Loss)",
series1="Open:120,135,148,130,155,162",
series2="Close:135,128,162,145,170,155",
categories="Mon,Tue,Wed,Thu,Fri,Sat",
x="0", y="19", width="12", height="18",
colors="4472C4,ED7D31",
showMarkers="true", marker="circle:4:4472C4",
updownbars="100:70AD47:C00000",
legend="bottom"))
# Chart 4: Auto markers + 3D line with gapDepth
# Features: gapDepth=300 (3D depth spacing, 0-500), line3d with custom perspective
items.append(chart("7-Line Elements",
chartType="line3d",
title="3D Line with Gap Depth",
dataRange="Sheet1!A1:E13",
x="13", y="19", width="12", height="18",
view3d="15,25,30",
gapDepth="300",
colors="4472C4,ED7D31,70AD47,FFC000",
chartFill="F5F5F5"))
doc.batch(items)
# ======================================================================
# Sheet: 8-Axis Extras
# ======================================================================
print("--- 8-Axis Extras ---")
items = [add_sheet("8-Axis Extras")]
# Chart 1: crossesAt — value axis crosses category axis at specific value
# Features: crossesAt=50 (value axis crosses the category axis at y=50,
# so bars/lines below 50 appear below the midline — great for threshold viz)
items.append(chart("8-Axis Extras",
chartType="line",
title="crossesAt — axis baseline at 50",
series1="Score:40,65,55,80,45,90,70",
categories="Jan,Feb,Mar,Apr,May,Jun,Jul",
colors="2E75B6",
x="0", y="0", width="12", height="18",
crossesAt="50",
lineWidth="2", marker="circle", markerSize="6"))
# Chart 2: dispBlanksAs — how missing/null data points are rendered
# Features: dispBlanksAs=span (connect across null/blank data points with a
# straight line — see also: gap=leave hole, zero=plot blank as 0)
items.append(chart("8-Axis Extras",
chartType="line",
title="dispBlanksAs=span (connect gaps)",
series1="Revenue:100,120,130,150,160",
categories="Jan,Feb,Mar,Apr,May",
colors="548235",
x="13", y="0", width="12", height="18",
dispBlanksAs="span",
lineWidth="2", marker="circle", markerSize="6"))
# Chart 3: dispBlanksAs=zero + crossesAt=0
# Features: dispBlanksAs=zero (missing cells rendered as zero),
# crossesAt=0 (axis crosses at y=0 — default for most charts).
# Note: dispBlanksAs affects rendering when the data source has blank cells;
# here it is shown as a metadata property on the chart (also accepts: gap, span).
items.append(chart("8-Axis Extras",
chartType="line",
title="dispBlanksAs=zero + crossesAt=0",
series1="Revenue:100,120,130,150,160",
categories="Jan,Feb,Mar,Apr,May",
colors="C00000",
x="0", y="19", width="12", height="18",
dispBlanksAs="zero",
crossesAt="0",
lineWidth="2", marker="circle", markerSize="6"))
# Chart 4: crosses=max (value axis on right side of plot)
# Features: crosses=max (value axis appears at the far end of the category
# axis — i.e. on the right side for a left-to-right chart; also: autoZero,
# min for the left/bottom edge)
items.append(chart("8-Axis Extras",
chartType="line",
title="crosses=max (value axis at far end)",
series1="Index:45,60,52,75,80,68,90",
categories="Mon,Tue,Wed,Thu,Fri,Sat,Sun",
colors="7030A0",
x="13", y="19", width="12", height="18",
crosses="max",
lineWidth="2"))
doc.batch(items)
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"Generated: {FILE}")
print(" 9 sheets (Sheet1 data + 8 chart sheets, 32 charts total)")
+602
View File
@@ -0,0 +1,602 @@
#!/bin/bash
# Line Charts Showcase — generates charts-line.xlsx exercising every line chart
# feature officecli supports: line, lineStacked, linePercentStacked, line3d, with
# markers, smoothing, dash patterns, axis scaling, gridlines, data labels, legend
# layout, reference lines, secondary axis, gradients, shadows, manual layout,
# data tables, drop/hi-low lines, up-down bars, and 3D rotation.
#
# CLI twin of charts-line.py (officecli Python SDK). Both produce an equivalent
# charts-line.xlsx.
#
# 9 sheets (Sheet1 data + 8 chart sheets), 32 charts total.
#
# Usage:
# ./charts-line.sh
# 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")/charts-line.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Source data — shared across all charts
# ==========================================================================
echo "--- Populating source data ---"
officecli set "$FILE" /Sheet1/A1 --prop text=Month --prop bold=true
officecli set "$FILE" /Sheet1/B1 --prop text=East --prop bold=true
officecli set "$FILE" /Sheet1/C1 --prop text=South --prop bold=true
officecli set "$FILE" /Sheet1/D1 --prop text=North --prop bold=true
officecli set "$FILE" /Sheet1/E1 --prop text=West --prop bold=true
officecli set "$FILE" /Sheet1/A2 --prop text=Jan; officecli set "$FILE" /Sheet1/B2 --prop text=120; officecli set "$FILE" /Sheet1/C2 --prop text=95; officecli set "$FILE" /Sheet1/D2 --prop text=88; officecli set "$FILE" /Sheet1/E2 --prop text=110
officecli set "$FILE" /Sheet1/A3 --prop text=Feb; officecli set "$FILE" /Sheet1/B3 --prop text=135; officecli set "$FILE" /Sheet1/C3 --prop text=108; officecli set "$FILE" /Sheet1/D3 --prop text=92; officecli set "$FILE" /Sheet1/E3 --prop text=118
officecli set "$FILE" /Sheet1/A4 --prop text=Mar; officecli set "$FILE" /Sheet1/B4 --prop text=148; officecli set "$FILE" /Sheet1/C4 --prop text=115; officecli set "$FILE" /Sheet1/D4 --prop text=105; officecli set "$FILE" /Sheet1/E4 --prop text=130
officecli set "$FILE" /Sheet1/A5 --prop text=Apr; officecli set "$FILE" /Sheet1/B5 --prop text=162; officecli set "$FILE" /Sheet1/C5 --prop text=128; officecli set "$FILE" /Sheet1/D5 --prop text=118; officecli set "$FILE" /Sheet1/E5 --prop text=145
officecli set "$FILE" /Sheet1/A6 --prop text=May; officecli set "$FILE" /Sheet1/B6 --prop text=155; officecli set "$FILE" /Sheet1/C6 --prop text=142; officecli set "$FILE" /Sheet1/D6 --prop text=125; officecli set "$FILE" /Sheet1/E6 --prop text=138
officecli set "$FILE" /Sheet1/A7 --prop text=Jun; officecli set "$FILE" /Sheet1/B7 --prop text=178; officecli set "$FILE" /Sheet1/C7 --prop text=155; officecli set "$FILE" /Sheet1/D7 --prop text=138; officecli set "$FILE" /Sheet1/E7 --prop text=162
officecli set "$FILE" /Sheet1/A8 --prop text=Jul; officecli set "$FILE" /Sheet1/B8 --prop text=195; officecli set "$FILE" /Sheet1/C8 --prop text=168; officecli set "$FILE" /Sheet1/D8 --prop text=145; officecli set "$FILE" /Sheet1/E8 --prop text=175
officecli set "$FILE" /Sheet1/A9 --prop text=Aug; officecli set "$FILE" /Sheet1/B9 --prop text=210; officecli set "$FILE" /Sheet1/C9 --prop text=175; officecli set "$FILE" /Sheet1/D9 --prop text=152; officecli set "$FILE" /Sheet1/E9 --prop text=190
officecli set "$FILE" /Sheet1/A10 --prop text=Sep; officecli set "$FILE" /Sheet1/B10 --prop text=188; officecli set "$FILE" /Sheet1/C10 --prop text=160; officecli set "$FILE" /Sheet1/D10 --prop text=140; officecli set "$FILE" /Sheet1/E10 --prop text=170
officecli set "$FILE" /Sheet1/A11 --prop text=Oct; officecli set "$FILE" /Sheet1/B11 --prop text=172; officecli set "$FILE" /Sheet1/C11 --prop text=148; officecli set "$FILE" /Sheet1/D11 --prop text=130; officecli set "$FILE" /Sheet1/E11 --prop text=155
officecli set "$FILE" /Sheet1/A12 --prop text=Nov; officecli set "$FILE" /Sheet1/B12 --prop text=165; officecli set "$FILE" /Sheet1/C12 --prop text=135; officecli set "$FILE" /Sheet1/D12 --prop text=122; officecli set "$FILE" /Sheet1/E12 --prop text=148
officecli set "$FILE" /Sheet1/A13 --prop text=Dec; officecli set "$FILE" /Sheet1/B13 --prop text=198; officecli set "$FILE" /Sheet1/C13 --prop text=158; officecli set "$FILE" /Sheet1/D13 --prop text=142; officecli set "$FILE" /Sheet1/E13 --prop text=180
# ==========================================================================
# Sheet: 1-Line Fundamentals
# ==========================================================================
echo "--- 1-Line Fundamentals ---"
officecli add "$FILE" / --type sheet --prop name="1-Line Fundamentals"
# Chart 1: Basic line with inline named series and categories
# Features: chartType=line, inline series (series1=Name:v1,v2,...),
# categories, colors, catTitle, axisTitle, axisfont, gridlines
officecli add "$FILE" "/1-Line Fundamentals" --type chart \
--prop chartType=line \
--prop title="Quarterly Revenue" \
--prop series1="Product A:120,180,210,250" \
--prop series2="Product B:90,140,160,200" \
--prop series3="Product C:60,85,110,145" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop catTitle=Quarter --prop axisTitle=Revenue \
--prop axisfont=9:C00000:Arial \
--prop gridlines=D9D9D9:0.5:dot
# Chart 2: Line with cell-range series (dotted syntax) and markers
# Features: series.name/values/categories (cell range via dotted syntax),
# showMarkers, marker (style:size:color), minorGridlines
officecli add "$FILE" "/1-Line Fundamentals" --type chart \
--prop chartType=line \
--prop title="East Region Trend" \
--prop series1.name=East \
--prop series1.values=Sheet1!B2:B13 \
--prop series1.categories=Sheet1!A2:A13 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop showMarkers=true --prop marker=circle:6:2E75B6 \
--prop gridlines=D9D9D9:0.5:dot \
--prop minorGridlines=EEEEEE:0.3:dot
# Chart 3: Line from dataRange with all four regions
# Features: dataRange (auto-reads headers as series names), marker=diamond,
# lineWidth, legend=bottom, legendfont
officecli add "$FILE" "/1-Line Fundamentals" --type chart \
--prop chartType=line \
--prop title="All Regions — Full Year" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=2E75B6,70AD47,FFC000,C00000 \
--prop showMarkers=true --prop marker=diamond:5:333333 \
--prop lineWidth=2 \
--prop legend=bottom \
--prop legendfont=9:58626E:Calibri
# Chart 4: Line with inline data shorthand and marker=none
# Features: data (inline shorthand Name:v1;Name2:v2), marker=none, legend=right
officecli add "$FILE" "/1-Line Fundamentals" --type chart \
--prop chartType=line \
--prop title="Simple Two-Series" \
--prop "data=Actual:80,120,160,200,240;Target:100,130,160,190,220" \
--prop categories="Week 1,Week 2,Week 3,Week 4,Week 5" \
--prop colors=0070C0,FF0000 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=none \
--prop legend=right
# ==========================================================================
# Sheet: 2-Line Styles
# ==========================================================================
echo "--- 2-Line Styles ---"
officecli add "$FILE" / --type sheet --prop name="2-Line Styles"
# Chart 1: Smooth line with thick width and shadow
# Features: smooth=true (Bezier curves), lineWidth=2.5, gridlines=none,
# axisVisible=false (hide both axes for sparkline-like minimal look),
# series.shadow (color-blur-angle-dist-opacity)
officecli add "$FILE" "/2-Line Styles" --type chart \
--prop chartType=line \
--prop title="Smooth Curves with Shadow" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop smooth=true --prop lineWidth=2.5 \
--prop colors=0070C0,00B050,FFC000,FF0000 \
--prop gridlines=none \
--prop axisVisible=false \
--prop series.shadow=000000-4-315-2-40
# Chart 2: Dashed lines — all dash styles demonstrated
# Note: lineDash applies to ALL series. Supported values:
# solid, dot, dash, dashdot, longdash, longdashdot, longdashdotdot
# Features: lineDash (applied globally to all series), lineWidth
officecli add "$FILE" "/2-Line Styles" --type chart \
--prop chartType=line \
--prop title="Dash Pattern Gallery" \
--prop series1="solid:120,135,148,162,155" \
--prop series2="dot:95,108,115,128,142" \
--prop series3="dash:88,92,105,118,125" \
--prop series4="dashdot:110,118,130,145,138" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop lineDash=dash --prop lineWidth=2 \
--prop legend=bottom
# Chart 3: Multiple marker styles — circle, square, triangle, star
# Note: marker applies to ALL series. Supported styles:
# circle, diamond, square, triangle, star, x, plus, dash, dot, none
# Features: marker=square:7:color (style:size:fillColor),
# series.outline (white border around markers/lines)
officecli add "$FILE" "/2-Line Styles" --type chart \
--prop chartType=line \
--prop title="Marker Style Showcase" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop showMarkers=true --prop marker=square:7:4472C4 \
--prop lineWidth=1.5 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop series.outline=FFFFFF-0.5
# Chart 4: Transparent lines with gradient plot area and styled title
# Features: transparency=30 (30% transparent), plotFill gradient,
# chartFill, title.font/size/color/bold, roundedCorners
officecli add "$FILE" "/2-Line Styles" --type chart \
--prop chartType=line \
--prop title="Translucent Lines on Gradient" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop lineWidth=3 --prop smooth=true \
--prop transparency=30 \
--prop plotFill=F0F4F8-D6E4F0:90 \
--prop chartFill=FFFFFF \
--prop colors=1F4E79,2E75B6,5B9BD5,9DC3E6 \
--prop title.font=Georgia --prop title.size=14 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop roundedCorners=true
# ==========================================================================
# Sheet: 3-Line Variants
# ==========================================================================
echo "--- 3-Line Variants ---"
officecli add "$FILE" / --type sheet --prop name="3-Line Variants"
# Chart 1: Stacked line chart
# Features: lineStacked (cumulative stacking), majorTickMark=outside, tickLabelPos=low
officecli add "$FILE" "/3-Line Variants" --type chart \
--prop chartType=lineStacked \
--prop title="Cumulative Sales by Region" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop catTitle=Month --prop axisTitle=Cumulative \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop majorTickMark=outside --prop tickLabelPos=low
# Chart 2: 100% stacked line chart with axis number format
# Features: linePercentStacked (each month sums to 100%),
# axisNumFmt (value axis number format)
officecli add "$FILE" "/3-Line Variants" --type chart \
--prop chartType=linePercentStacked \
--prop title="Regional Contribution %" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=1F4E79,2E75B6,9DC3E6,BDD7EE \
--prop axisNumFmt=0% \
--prop legend=right \
--prop gridlines=E0E0E0:0.5:solid
# Chart 3: 3D line chart with perspective
# Features: line3d (3D line chart), view3d (rotX,rotY,perspective),
# style/styleId (preset chart style 1-48)
officecli add "$FILE" "/3-Line Variants" --type chart \
--prop chartType=line3d \
--prop title="3D Regional Trends" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=15,20,30 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop chartFill=F8F8F8 \
--prop style=3
# Chart 4: Stacked line with area fill and data table
# Features: dataTable=true (show value table below chart),
# legend=none (hidden because data table shows series names)
officecli add "$FILE" "/3-Line Variants" --type chart \
--prop chartType=lineStacked \
--prop title="Stacked with Data Table" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop dataTable=true \
--prop legend=none \
--prop lineWidth=1.5 \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop plotFill=FAFAFA
# ==========================================================================
# Sheet: 4-Axis & Gridlines
# ==========================================================================
echo "--- 4-Axis & Gridlines ---"
officecli add "$FILE" / --type sheet --prop name="4-Axis & Gridlines"
# Chart 1: Custom axis scaling — min, max, majorUnit
# Features: axisMin, axisMax, majorUnit, minorUnit,
# axisLine (value axis line styling — red), catAxisLine (category axis line — blue)
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=line \
--prop title="Custom Axis Scale (80220)" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop axisMin=80 --prop axisMax=220 --prop majorUnit=20 \
--prop minorUnit=10 \
--prop showMarkers=true --prop marker=circle:4:4472C4 \
--prop gridlines=D0D0D0:0.5:solid \
--prop minorGridlines=EEEEEE:0.3:dot \
--prop axisLine=C00000:1.5:solid \
--prop catAxisLine=2E75B6:1.5:solid
# Demonstrate chart-axis element (path: /SheetName/chart[N]/axis[@role=ROLE]).
# Properties: min, max, format, majorGridlines, labelRotation.
# These are the same semantics as axisMin/axisMax/gridlines/labelrotation at
# chart level but applied through the dedicated sub-element path, which also
# exposes role, dispUnits, majorUnit, title, visible, logBase.
officecli set "$FILE" "/4-Axis & Gridlines/chart[1]/axis[@role=value]" \
--prop min=80 --prop max=220 \
--prop format="#,##0" \
--prop majorGridlines=true \
--prop labelRotation=0
# Chart 2: Logarithmic scale with display units
# Features: logBase=10 (logarithmic scale), marker=triangle
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=line \
--prop title="Exponential Growth (Log Scale)" \
--prop series1="Growth:1,5,25,125,625,3125" \
--prop categories="Year 1,Year 2,Year 3,Year 4,Year 5,Year 6" \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop logBase=10 \
--prop colors=C00000 \
--prop lineWidth=2.5 \
--prop showMarkers=true --prop marker=triangle:7:C00000 \
--prop axisTitle="Value (log)" \
--prop catTitle=Year \
--prop gridlines=E0E0E0:0.5:dash
# Chart 3: Reversed axis and hidden axes
# Features: axisReverse=true (value axis direction flipped), smooth + markers together
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=line \
--prop title="Reversed Value Axis" \
--prop series1="Depth:0,50,120,200,350,500" \
--prop categories="Station A,Station B,Station C,Station D,Station E,Station F" \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop axisReverse=true \
--prop colors=0070C0 \
--prop lineWidth=2 \
--prop showMarkers=true --prop marker=diamond:6:0070C0 \
--prop smooth=true \
--prop axisTitle="Depth (m)" \
--prop gridlines=D9D9D9:0.5:solid
# Chart 4: Display units and tick mark styles
# Features: dispUnits=thousands (display units label),
# majorTickMark=outside, minorTickMark=inside, marker=star
officecli add "$FILE" "/4-Axis & Gridlines" --type chart \
--prop chartType=line \
--prop title="Revenue (in Thousands)" \
--prop series1="Revenue:12000,18500,22000,31000,45000,52000" \
--prop series2="Cost:8000,11000,14000,19500,28000,33000" \
--prop categories=2020,2021,2022,2023,2024,2025 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop dispUnits=thousands \
--prop colors=2E75B6,C00000 \
--prop lineWidth=2 \
--prop majorTickMark=outside --prop minorTickMark=inside \
--prop showMarkers=true --prop marker=star:7:2E75B6 \
--prop catTitle=Year --prop axisTitle="Amount (K)"
# ==========================================================================
# Sheet: 5-Labels & Legend
# ==========================================================================
echo "--- 5-Labels & Legend ---"
officecli add "$FILE" / --type sheet --prop name="5-Labels & Legend"
# Chart 1: Data labels at various positions with number format
# Features: dataLabels=true, labelPos=top, labelFont (size:color:bold),
# dataLabels.numFmt (number format), dataLabels.separator
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=line \
--prop title="Sales with Labels" \
--prop series1="Revenue:120,180,210,250,280" \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4 \
--prop lineWidth=2 \
--prop showMarkers=true --prop marker=circle:6:4472C4 \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=9:333333:true \
--prop dataLabels.numFmt=#,##0 \
--prop "dataLabels.separator=: "
# Chart 2: Custom individual data labels (highlight peak)
# Features: dataLabel{N}.delete (hide specific labels),
# dataLabel{N}.text (custom text on specific point),
# point{N}.color (highlight individual data point marker in red),
# dataLabel{N}.y (manual vertical position of individual label, 0-1 fraction)
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=line \
--prop title="Peak Highlight" \
--prop series1="Sales:88,120,165,210,195,178" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=2E75B6 \
--prop lineWidth=2.5 --prop smooth=true \
--prop showMarkers=true --prop marker=circle:5:2E75B6 \
--prop dataLabels=true --prop labelPos=top \
--prop dataLabel1.delete=true --prop dataLabel2.delete=true \
--prop point4.color=C00000 \
--prop dataLabel4.text="Peak: 210" \
--prop dataLabel4.y=0.15 \
--prop dataLabel5.delete=true --prop dataLabel6.delete=true
# Chart 3: Legend positioning and overlay
# Features: legend=top, legend.overlay=true (legend overlays chart area),
# legendfont (size:color:fontname)
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=line \
--prop title="Legend Overlay on Chart" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop lineWidth=2 \
--prop legend=top \
--prop legend.overlay=true \
--prop legendfont=10:1F4E79:Calibri \
--prop plotFill=F5F5F5
# Chart 4: Manual layout — plotArea, title, and legend positioning
# Features: plotArea.x/y/w/h (plot area manual layout, 0-1 fraction),
# title.x/y (title position), legend.x/y/w/h (legend position/size)
officecli add "$FILE" "/5-Labels & Legend" --type chart \
--prop chartType=line \
--prop title="Manual Layout Control" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop lineWidth=1.5 \
--prop plotArea.x=0.12 --prop plotArea.y=0.18 \
--prop plotArea.w=0.82 --prop plotArea.h=0.55 \
--prop title.x=0.25 --prop title.y=0.02 \
--prop legend.x=0.15 --prop legend.y=0.82 \
--prop legend.w=0.7 --prop legend.h=0.12 \
--prop title.font=Arial --prop title.size=13 \
--prop title.bold=true
# ==========================================================================
# Sheet: 6-Effects & Advanced
# ==========================================================================
echo "--- 6-Effects & Advanced ---"
officecli add "$FILE" / --type sheet --prop name="6-Effects & Advanced"
# Chart 1: Secondary axis — two series on different scales
# Features: secondaryAxis=2 (series 2 on right-hand axis), dual-scale visualization
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=line \
--prop title="Revenue vs Growth Rate" \
--prop series1="Revenue:120,180,250,310,380,420" \
--prop series2="Growth %:50,33,39,24,23,11" \
--prop categories=2020,2021,2022,2023,2024,2025 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop secondaryAxis=2 \
--prop colors=2E75B6,C00000 \
--prop lineWidth=2.5 \
--prop showMarkers=true --prop marker=circle:6:2E75B6 \
--prop catTitle=Year --prop axisTitle=Revenue \
--prop dataLabels=true --prop labelPos=top
# Chart 2: Reference line (target/threshold) with error bars
# referenceLine format: value:color:width:dash
# - value: the threshold/target value on the Y axis
# - color: hex RGB (no #)
# - width: line thickness in pt (default 1.5)
# - dash: solid/dot/dash/dashdot/longdash
# Features: referenceLine (horizontal target line), lineDash=longdash
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=line \
--prop title="vs Target (150)" \
--prop dataRange=Sheet1!A1:C13 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4,70AD47 \
--prop referenceLine=150:FF0000:1.5:dash \
--prop showMarkers=true --prop marker=circle:4:4472C4 \
--prop legend=bottom \
--prop lineDash=longdash --prop lineWidth=1.5
# Chart 3: Title glow/shadow effects with per-series gradients
# Features: title.glow (color-radius-opacity), title.shadow,
# series.shadow on line charts, plotFill + chartFill
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=line \
--prop title="Glow & Shadow Effects" \
--prop series1="East:120,135,148,162,155,178" \
--prop series2="West:110,118,130,145,138,162" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop lineWidth=3 --prop smooth=true \
--prop colors=4472C4,ED7D31 \
--prop title.glow=4472C4-8-60 \
--prop title.shadow=000000-3-315-2-40 \
--prop title.font=Calibri --prop title.size=16 \
--prop title.bold=true --prop title.color=1F4E79 \
--prop series.shadow=000000-3-315-1-30 \
--prop plotFill=F0F4F8 --prop chartFill=FFFFFF
# Chart 4: Conditional coloring with chart/plot borders
# colorRule format: threshold:belowColor:aboveColor
# - values below 0 → red (C00000), above 0 → green (70AD47)
# Features: colorRule (threshold-based conditional coloring),
# chartArea.border, plotArea.border, referenceLine=0 (zero line)
officecli add "$FILE" "/6-Effects & Advanced" --type chart \
--prop chartType=line \
--prop title="Conditional Colors & Borders" \
--prop series1="Profit:80,120,-30,160,-50,200,140,-20,180,90" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=2E75B6 \
--prop lineWidth=2 \
--prop showMarkers=true --prop marker=circle:6:2E75B6 \
--prop colorRule=0:C00000:70AD47 \
--prop referenceLine=0:888888:1:solid \
--prop chartArea.border=D0D0D0:1:solid \
--prop plotArea.border=E0E0E0:0.5:dot \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=8:666666:false
# ==========================================================================
# Sheet: 7-Line Elements
# ==========================================================================
echo "--- 7-Line Elements ---"
officecli add "$FILE" / --type sheet --prop name="7-Line Elements"
# Chart 1: Drop lines — vertical lines from data points to category axis
# Features: dropLines=true (simple toggle — default thin gray lines)
officecli add "$FILE" "/7-Line Elements" --type chart \
--prop chartType=line \
--prop title="Drop Lines" \
--prop dataRange=Sheet1!A1:C13 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31 \
--prop showMarkers=true --prop marker=circle:5:4472C4 \
--prop dropLines=true \
--prop legend=bottom
# Chart 2: High-low lines — connect highest and lowest series at each point
# Features: hiLowLines=true (lines connecting highest and lowest values)
officecli add "$FILE" "/7-Line Elements" --type chart \
--prop chartType=line \
--prop title="High-Low Lines" \
--prop series1="High:210,195,220,240,230,250" \
--prop series2="Low:150,135,160,170,155,180" \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop colors=2E75B6,C00000 \
--prop showMarkers=true --prop marker=diamond:5:2E75B6 \
--prop hiLowLines=true \
--prop legend=bottom
# Chart 3: Up-down bars with custom colors — show gain/loss between series
# updownbars format: gapWidth:upColor:downColor
# - gapWidth: gap between bars (0-500, default 150)
# - upColor: fill color for increase (Close > Open)
# - downColor: fill color for decrease (Close < Open)
# Features: updownbars with custom colors (gain=green, loss=red)
officecli add "$FILE" "/7-Line Elements" --type chart \
--prop chartType=line \
--prop title="Up-Down Bars (Gain/Loss)" \
--prop series1="Open:120,135,148,130,155,162" \
--prop series2="Close:135,128,162,145,170,155" \
--prop categories=Mon,Tue,Wed,Thu,Fri,Sat \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop colors=4472C4,ED7D31 \
--prop showMarkers=true --prop marker=circle:4:4472C4 \
--prop updownbars=100:70AD47:C00000 \
--prop legend=bottom
# Chart 4: Auto markers + 3D line with gapDepth
# Features: gapDepth=300 (3D depth spacing, 0-500), line3d with custom perspective
officecli add "$FILE" "/7-Line Elements" --type chart \
--prop chartType=line3d \
--prop title="3D Line with Gap Depth" \
--prop dataRange=Sheet1!A1:E13 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=15,25,30 \
--prop gapDepth=300 \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop chartFill=F5F5F5
# ==========================================================================
# Sheet: 8-Axis Extras
# ==========================================================================
echo "--- 8-Axis Extras ---"
officecli add "$FILE" / --type sheet --prop name="8-Axis Extras"
# Chart 1: crossesAt — value axis crosses category axis at specific value
# Features: crossesAt=50 (value axis crosses the category axis at y=50,
# so bars/lines below 50 appear below the midline — great for threshold viz)
officecli add "$FILE" "/8-Axis Extras" --type chart \
--prop chartType=line \
--prop title="crossesAt — axis baseline at 50" \
--prop series1=Score:40,65,55,80,45,90,70 \
--prop categories=Jan,Feb,Mar,Apr,May,Jun,Jul \
--prop colors=2E75B6 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop crossesAt=50 \
--prop lineWidth=2 --prop marker=circle --prop markerSize=6
# Chart 2: dispBlanksAs — how missing/null data points are rendered
# Features: dispBlanksAs=span (connect across null/blank data points with a
# straight line — see also: gap=leave hole, zero=plot blank as 0)
officecli add "$FILE" "/8-Axis Extras" --type chart \
--prop chartType=line \
--prop title="dispBlanksAs=span (connect gaps)" \
--prop series1=Revenue:100,120,130,150,160 \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop colors=548235 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop dispBlanksAs=span \
--prop lineWidth=2 --prop marker=circle --prop markerSize=6
# Chart 3: dispBlanksAs=zero + crossesAt=0
# Features: dispBlanksAs=zero (missing cells rendered as zero),
# crossesAt=0 (axis crosses at y=0 — default for most charts).
# Note: dispBlanksAs affects rendering when the data source has blank cells;
# here it is shown as a metadata property on the chart (also accepts: gap, span).
officecli add "$FILE" "/8-Axis Extras" --type chart \
--prop chartType=line \
--prop title="dispBlanksAs=zero + crossesAt=0" \
--prop series1=Revenue:100,120,130,150,160 \
--prop categories=Jan,Feb,Mar,Apr,May \
--prop colors=C00000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop dispBlanksAs=zero \
--prop crossesAt=0 \
--prop lineWidth=2 --prop marker=circle --prop markerSize=6
# Chart 4: crosses=max (value axis on right side of plot)
# Features: crosses=max (value axis appears at the far end of the category
# axis — i.e. on the right side for a left-to-right chart; also: autoZero,
# min for the left/bottom edge)
officecli add "$FILE" "/8-Axis Extras" --type chart \
--prop chartType=line \
--prop title="crosses=max (value axis at far end)" \
--prop series1=Index:45,60,52,75,80,68,90 \
--prop categories=Mon,Tue,Wed,Thu,Fri,Sat,Sun \
--prop colors=7030A0 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop crosses=max \
--prop lineWidth=2
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+172
View File
@@ -0,0 +1,172 @@
# Pie & Doughnut Charts Showcase
This demo consists of three files that work together:
- **charts-pie.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-pie.xlsx** — The generated workbook with 3 sheets (3 chart sheets, 12 charts total).
- **charts-pie.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-pie.py
# → charts-pie.xlsx
```
## Chart Sheets
### Sheet: 1-Pie Charts
Four pie chart variants covering flat, 3D, exploded, and gradient fills.
```bash
# Basic pie with colors and data labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=pie \
--prop series1="Share:40,25,20,15" \
--prop categories=Product A,Product B,Product C,Product D \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop dataLabels=true --prop labelPos=outsideEnd
# Exploded pie with per-point colors and percentage labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=pie \
--prop explosion=15 \
--prop point1.color=1F4E79 --prop point2.color=2E75B6 \
--prop dataLabels.numFmt=0.0"%" --prop labelPos=bestFit
# 3D pie with tilt angle and styled title
officecli add data.xlsx /Sheet --type chart \
--prop chartType=pie3d \
--prop view3d=30,0,0 \
--prop title.font=Georgia --prop title.size=16 \
--prop labelFont=12:FFFFFF:true --prop labelPos=center
# Pie with per-slice gradients and leader lines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=pie \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90;...' \
--prop dataLabels.showLeaderLines=true \
--prop legend=right --prop legendfont=10:333333:Helvetica
```
**Features:** `pie`, `pie3d`, `explosion`, `point{N}.color`, `view3d`, `labelPos=bestFit`, `dataLabels.numFmt`, `labelFont`, `title.font/size/color/bold`, `gradients` (per-slice), `dataLabels.showLeaderLines`, `legendfont`, `chartFill`, `roundedCorners`
### Sheet: 2-Doughnut Charts
Four doughnut chart variants including multi-ring and styled effects.
```bash
# Basic doughnut with center labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=doughnut \
--prop dataLabels=true --prop labelPos=center \
--prop labelFont=14:FFFFFF:true
# Multi-ring doughnut (multiple series = concentric rings)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=doughnut \
--prop series1="2024:40,35,25" \
--prop series2="2025:45,30,25" \
--prop series.outline=FFFFFF-1
# Styled doughnut with shadow effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=doughnut \
--prop series.shadow=000000-4-315-2-30 \
--prop title.shadow=000000-3-315-2-30 \
--prop plotFill=F5F5F5
# Doughnut with explosion and per-slice gradients
officecli add data.xlsx /Sheet --type chart \
--prop chartType=doughnut \
--prop explosion=8 \
--prop 'gradients=1F4E79-5B9BD5:90;C55A11-F4B183:90;...'
```
**Features:** `doughnut`, multi-ring (multiple `series`), `labelPos=center`, `labelFont`, `series.outline`, `series.shadow`, `title.shadow`, `plotFill`, `explosion`, `gradients`
### Sheet: 3-Pie Advanced
Four charts demonstrating advanced pie/doughnut-specific properties: automatic slice coloring, rotation, hole size, leader lines, and title overlay.
```bash
# Pie — varyColors + firstSliceAngle
officecli add charts-pie.xlsx "/3-Pie Advanced" --type chart \
--prop chartType=pie \
--prop title="Pie — varyColors + firstSliceAngle" \
--prop series1="Share:40,30,20,10" \
--prop categories=Q1,Q2,Q3,Q4 \
--prop varyColors=true \
--prop firstSliceAngle=45 \
--prop dataLabels=true --prop labelPos=bestFit
# Doughnut — holeSize + leaderlines
officecli add charts-pie.xlsx "/3-Pie Advanced" --type chart \
--prop chartType=doughnut \
--prop title="Doughnut — holeSize + leaderlines" \
--prop series1="Revenue:35,28,22,15" \
--prop categories=North,South,East,West \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop holeSize=65 \
--prop leaderlines=true \
--prop dataLabels=true --prop labelPos=outsideEnd
# Pie — title.overlay (title floats over plot area)
officecli add charts-pie.xlsx "/3-Pie Advanced" --type chart \
--prop chartType=pie \
--prop title="Overlaid Title" \
--prop title.overlay=true \
--prop series1="Mix:50,30,20" \
--prop categories=Online,Retail,Partner \
--prop colors=4472C4,70AD47,FFC000 \
--prop varyColors=false \
--prop dataLabels=percent --prop labelPos=center
# Doughnut — holeSize + firstSliceAngle + title.overlay combined
officecli add charts-pie.xlsx "/3-Pie Advanced" --type chart \
--prop chartType=doughnut \
--prop title="Doughnut — Combined" \
--prop title.overlay=true \
--prop series1="Split:45,35,20" \
--prop categories=A,B,C \
--prop colors=C00000,FFC000,548235 \
--prop holeSize=50 \
--prop varyColors=false \
--prop dataLabels=true --prop labelPos=center \
--prop labelFont=12:FFFFFF:true
```
**Features:** `varyColors=true/false` (each slice gets a distinct theme color automatically), `firstSliceAngle=45` (rotate first slice start angle, 0360 degrees), `holeSize=65` (% of total radius — larger value = thinner doughnut ring), `leaderlines=true` (connecting lines from outside-end labels to their slices), `title.overlay=true` (title floats over the plot area maximizing chart area)
## Complete Feature Coverage
| Feature | Sheet |
|---------|-------|
| `pie`, `pie3d`, `doughnut` | 1, 2 |
| `explosion` (slice separation %) | 1, 2 |
| `point{N}.color` (per-slice colors) | 1 |
| `view3d` (tilt angle on 3D pie) | 1 |
| `dataLabels`, `labelPos` (outsideEnd/bestFit/center/percent) | 1, 2, 3 |
| `dataLabels.numFmt` | 2 |
| `dataLabels.showLeaderLines` | 1 |
| `leaderlines` | 3 |
| `labelFont` (size:color:bold) | 1, 2, 3 |
| `gradients` (per-slice gradient fills) | 1, 2 |
| `legend`, `legendfont` | 1, 2 |
| `series.outline` (white slice separator) | 2 |
| `series.shadow`, `title.shadow` | 2 |
| `plotFill`, `chartFill`, `roundedCorners` | 1, 2 |
| `title.font`, `title.size`, `title.color`, `title.bold` | 1, 2 |
| `varyColors` | 3 |
| `firstSliceAngle` | 3 |
| `holeSize` | 3 |
| `title.overlay` | 3 |
## Inspect the Generated File
```bash
officecli query charts-pie.xlsx chart
officecli get charts-pie.xlsx "/1-Pie Charts/chart[1]"
```
+266
View File
@@ -0,0 +1,266 @@
#!/usr/bin/env python3
"""
Pie & Doughnut Charts Showcase — pie, pie3d, and doughnut with all variations.
Generates: charts-pie.xlsx
SDK twin of charts-pie.sh (officecli CLI). Both produce an equivalent
charts-pie.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in a single `doc.batch(...)` round-trip.
Each item is the same `{"command","parent","type","props"}` dict you'd put in
an `officecli batch` list.
3 chart sheets, 12 charts total: 4 pie/pie3d, 4 doughnut, 4 advanced.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-pie.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-pie.xlsx")
def sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
items = [
# ==================================================================
# Sheet: 1-Pie Charts
# ==================================================================
sheet("1-Pie Charts"),
# ------------------------------------------------------------------
# Chart 1: Basic pie chart with inline data and custom colors
# Features: chartType=pie, inline series, categories, colors, dataLabels
# ------------------------------------------------------------------
chart("/1-Pie Charts",
chartType="pie",
title="Market Share",
series1="Share:40,25,20,15",
categories="Product A,Product B,Product C,Product D",
colors="4472C4,ED7D31,70AD47,FFC000",
x="0", y="0", width="12", height="18",
dataLabels="true", labelPos="outsideEnd"),
# ------------------------------------------------------------------
# Chart 2: Pie with exploded slice and per-point colors
# Features: explosion (slice separation %), point{N}.color,
# labelPos=bestFit, dataLabels=percent
# ------------------------------------------------------------------
chart("/1-Pie Charts",
chartType="pie",
title="Revenue by Region",
series1="Revenue:35,28,22,15",
categories="North,South,East,West",
x="13", y="0", width="12", height="18",
explosion="15",
**{"point1.color": "1F4E79", "point2.color": "2E75B6",
"point3.color": "9DC3E6", "point4.color": "BDD7EE"},
dataLabels="percent", labelPos="bestFit"),
# ------------------------------------------------------------------
# Chart 3: 3D pie with perspective and title styling
# Features: pie3d, view3d on pie (tilt angle), title.font/size/color/bold,
# labelFont (size:color:bold)
# ------------------------------------------------------------------
chart("/1-Pie Charts",
chartType="pie3d",
title="3D Category Split",
series1="Sales:45,30,25",
categories="Electronics,Clothing,Food",
colors="2E75B6,70AD47,FFC000",
x="0", y="19", width="12", height="18",
view3d="30,0,0",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"},
dataLabels="true", labelPos="center",
labelFont="12:FFFFFF:true"),
# ------------------------------------------------------------------
# Chart 4: Pie with gradient fills, leader lines, and legend positioning
# Features: gradients (per-slice), legend=right, legendfont,
# dataLabels.showLeaderLines, chartFill, roundedCorners
# ------------------------------------------------------------------
chart("/1-Pie Charts",
chartType="pie",
title="Q4 Distribution",
series1="Q4:198,158,142,180",
categories="East,South,North,West",
x="13", y="19", width="12", height="18",
gradients="4472C4-BDD7EE:90;ED7D31-FBE5D6:90;70AD47-C5E0B4:90;FFC000-FFF2CC:90",
legend="right", legendfont="10:333333:Helvetica",
dataLabels="true",
**{"dataLabels.showLeaderLines": "true"},
chartFill="FAFAFA", roundedCorners="true"),
# ==================================================================
# Sheet: 2-Doughnut Charts
# ==================================================================
sheet("2-Doughnut Charts"),
# ------------------------------------------------------------------
# Chart 1: Basic doughnut chart
# Features: chartType=doughnut, center labels
# ------------------------------------------------------------------
chart("/2-Doughnut Charts",
chartType="doughnut",
title="Channel Mix",
series1="Channel:55,45",
categories="Online,Retail",
colors="4472C4,ED7D31",
x="0", y="0", width="12", height="18",
dataLabels="true", labelPos="center",
labelFont="14:FFFFFF:true"),
# ------------------------------------------------------------------
# Chart 2: Multi-ring doughnut (multiple series)
# Features: multi-ring doughnut (multiple series = concentric rings),
# series.outline (white separator between slices)
# ------------------------------------------------------------------
chart("/2-Doughnut Charts",
chartType="doughnut",
title="Year-over-Year Comparison",
series1="2024:40,35,25",
series2="2025:45,30,25",
categories="Electronics,Clothing,Food",
colors="4472C4,70AD47,FFC000",
x="13", y="0", width="12", height="18",
**{"series.outline": "FFFFFF-1"},
legend="bottom"),
# ------------------------------------------------------------------
# Chart 3: Styled doughnut with shadow and custom data labels
# Features: series.shadow on doughnut, title.shadow, plotFill
# ------------------------------------------------------------------
chart("/2-Doughnut Charts",
chartType="doughnut",
title="Priority Breakdown",
series1="Priority:50,30,20",
categories="High,Medium,Low",
colors="C00000,FFC000,70AD47",
x="0", y="19", width="12", height="18",
**{"series.shadow": "000000-4-315-2-30"},
dataLabels="true", labelPos="outsideEnd",
**{"dataLabels.numFmt": '0"%"', "title.shadow": "000000-3-315-2-30"},
plotFill="F5F5F5"),
# ------------------------------------------------------------------
# Chart 4: Doughnut with per-slice gradient and explosion
# Features: explosion on doughnut, 5-slice gradients
# ------------------------------------------------------------------
chart("/2-Doughnut Charts",
chartType="doughnut",
title="Product Revenue",
series1="Revenue:35,25,20,12,8",
categories="Laptop,Phone,Tablet,Jacket,Coffee",
x="13", y="19", width="12", height="18",
explosion="8",
gradients="1F4E79-5B9BD5:90;C55A11-F4B183:90;548235-A9D18E:90;7F6000-FFD966:90;843C0B-DDA15E:90",
legend="right",
dataLabels="true", labelPos="bestFit"),
# ==================================================================
# Sheet: 3-Pie Advanced
# ==================================================================
sheet("3-Pie Advanced"),
# ------------------------------------------------------------------
# Chart 1: varyColors=true + firstSliceAngle on pie
# Features: varyColors=true (each slice gets a distinct color
# automatically), firstSliceAngle=45 (rotates the first slice start
# angle, 0-360 degrees)
# ------------------------------------------------------------------
chart("/3-Pie Advanced",
chartType="pie",
title="Pie — varyColors + firstSliceAngle",
series1="Share:40,30,20,10",
categories="Q1,Q2,Q3,Q4",
x="0", y="0", width="12", height="18",
varyColors="true",
firstSliceAngle="45",
dataLabels="true", labelPos="bestFit"),
# ------------------------------------------------------------------
# Chart 2: holeSize + leaderlines on doughnut
# Features: holeSize=65 (% of total radius — larger value = thinner
# ring), leaderlines=true (connecting lines from labels to slices,
# pie/doughnut)
# ------------------------------------------------------------------
chart("/3-Pie Advanced",
chartType="doughnut",
title="Doughnut — holeSize + leaderlines",
series1="Revenue:35,28,22,15",
categories="North,South,East,West",
colors="2E75B6,ED7D31,70AD47,FFC000",
x="13", y="0", width="12", height="18",
holeSize="65",
leaderlines="true",
dataLabels="true", labelPos="outsideEnd"),
# ------------------------------------------------------------------
# Chart 3: title.overlay on pie (title floats over plot area)
# Features: title.overlay=true (title overlays the plot area,
# maximizing chart area — contrast with default where title reserves
# space above)
# ------------------------------------------------------------------
chart("/3-Pie Advanced",
chartType="pie",
title="Overlaid Title",
**{"title.overlay": "true"},
series1="Mix:50,30,20",
categories="Online,Retail,Partner",
colors="4472C4,70AD47,FFC000",
x="0", y="19", width="12", height="18",
varyColors="false",
dataLabels="percent", labelPos="center"),
# ------------------------------------------------------------------
# Chart 4: Doughnut — holeSize + firstSliceAngle + title.overlay combined
# Features: three doughnut-specific props together —
# holeSize, varyColors, title.overlay
# ------------------------------------------------------------------
chart("/3-Pie Advanced",
chartType="doughnut",
title="Doughnut — Combined",
**{"title.overlay": "true"},
series1="Split:45,35,20",
categories="A,B,C",
colors="C00000,FFC000,548235",
x="13", y="19", width="12", height="18",
holeSize="50",
varyColors="false",
dataLabels="true", labelPos="center",
labelFont="12:FFFFFF:true"),
# Remove blank default Sheet1 (all data is inline)
{"command": "remove", "path": "/Sheet1"},
]
doc.batch(items)
print(f" applied {len(items)} items (3 sheets, 12 charts)")
doc.send({"command": "save"})
print(f"Generated: {FILE}")
print(" 3 sheets (3 chart sheets, 12 charts total)")
+203
View File
@@ -0,0 +1,203 @@
#!/bin/bash
# Pie & Doughnut Charts Showcase — pie, pie3d, and doughnut with all variations.
# Generates: charts-pie.xlsx (3 chart sheets, 12 charts total)
#
# CLI twin of charts-pie.py (officecli Python SDK). Both produce an
# equivalent charts-pie.xlsx.
# 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")/charts-pie.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet: 1-Pie Charts
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Pie Charts"
# Chart 1: Basic pie chart with inline data and custom colors
# Features: chartType=pie, inline series, categories, colors, dataLabels
officecli add "$FILE" "/1-Pie Charts" --type chart \
--prop chartType=pie \
--prop title="Market Share" \
--prop series1=Share:40,25,20,15 \
--prop categories=Product A,Product B,Product C,Product D \
--prop colors=4472C4,ED7D31,70AD47,FFC000 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=outsideEnd
# Chart 2: Pie with exploded slice and per-point colors
# Features: explosion (slice separation %), point{N}.color,
# labelPos=bestFit, dataLabels=percent
officecli add "$FILE" "/1-Pie Charts" --type chart \
--prop chartType=pie \
--prop title="Revenue by Region" \
--prop series1=Revenue:35,28,22,15 \
--prop categories=North,South,East,West \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop explosion=15 \
--prop point1.color=1F4E79 --prop point2.color=2E75B6 \
--prop point3.color=9DC3E6 --prop point4.color=BDD7EE \
--prop dataLabels=percent --prop labelPos=bestFit
# Chart 3: 3D pie with perspective and title styling
# Features: pie3d, view3d on pie (tilt angle), title.font/size/color/bold,
# labelFont (size:color:bold)
officecli add "$FILE" "/1-Pie Charts" --type chart \
--prop chartType=pie3d \
--prop title="3D Category Split" \
--prop series1=Sales:45,30,25 \
--prop categories=Electronics,Clothing,Food \
--prop colors=2E75B6,70AD47,FFC000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop view3d=30,0,0 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop dataLabels=true --prop labelPos=center \
--prop labelFont=12:FFFFFF:true
# Chart 4: Pie with gradient fills, leader lines, and legend positioning
# Features: gradients (per-slice), legend=right, legendfont,
# dataLabels.showLeaderLines, chartFill, roundedCorners
officecli add "$FILE" "/1-Pie Charts" --type chart \
--prop chartType=pie \
--prop title="Q4 Distribution" \
--prop series1=Q4:198,158,142,180 \
--prop categories=East,South,North,West \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90;70AD47-C5E0B4:90;FFC000-FFF2CC:90' \
--prop legend=right --prop legendfont=10:333333:Helvetica \
--prop dataLabels=true \
--prop dataLabels.showLeaderLines=true \
--prop chartFill=FAFAFA --prop roundedCorners=true
# ==========================================================================
# Sheet: 2-Doughnut Charts
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Doughnut Charts"
# Chart 1: Basic doughnut chart
# Features: chartType=doughnut, center labels
officecli add "$FILE" "/2-Doughnut Charts" --type chart \
--prop chartType=doughnut \
--prop title="Channel Mix" \
--prop series1=Channel:55,45 \
--prop categories=Online,Retail \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=center \
--prop labelFont=14:FFFFFF:true
# Chart 2: Multi-ring doughnut (multiple series)
# Features: multi-ring doughnut (multiple series = concentric rings),
# series.outline (white separator between slices)
officecli add "$FILE" "/2-Doughnut Charts" --type chart \
--prop chartType=doughnut \
--prop title="Year-over-Year Comparison" \
--prop series1=2024:40,35,25 \
--prop series2=2025:45,30,25 \
--prop categories=Electronics,Clothing,Food \
--prop colors=4472C4,70AD47,FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop series.outline=FFFFFF-1 \
--prop legend=bottom
# Chart 3: Styled doughnut with shadow and custom data labels
# Features: series.shadow on doughnut, title.shadow, plotFill
officecli add "$FILE" "/2-Doughnut Charts" --type chart \
--prop chartType=doughnut \
--prop title="Priority Breakdown" \
--prop series1=Priority:50,30,20 \
--prop categories=High,Medium,Low \
--prop colors=C00000,FFC000,70AD47 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop series.shadow=000000-4-315-2-30 \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop 'dataLabels.numFmt=0"%"' \
--prop title.shadow=000000-3-315-2-30 \
--prop plotFill=F5F5F5
# Chart 4: Doughnut with per-slice gradient and explosion
# Features: explosion on doughnut, 5-slice gradients
officecli add "$FILE" "/2-Doughnut Charts" --type chart \
--prop chartType=doughnut \
--prop title="Product Revenue" \
--prop series1=Revenue:35,25,20,12,8 \
--prop categories=Laptop,Phone,Tablet,Jacket,Coffee \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop explosion=8 \
--prop 'gradients=1F4E79-5B9BD5:90;C55A11-F4B183:90;548235-A9D18E:90;7F6000-FFD966:90;843C0B-DDA15E:90' \
--prop legend=right \
--prop dataLabels=true --prop labelPos=bestFit
# ==========================================================================
# Sheet: 3-Pie Advanced
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Pie Advanced"
# Chart 1: varyColors=true + firstSliceAngle on pie
# Features: varyColors=true (each slice gets a distinct color automatically),
# firstSliceAngle=45 (rotates the first slice start angle, 0-360 degrees)
officecli add "$FILE" "/3-Pie Advanced" --type chart \
--prop chartType=pie \
--prop title="Pie — varyColors + firstSliceAngle" \
--prop series1=Share:40,30,20,10 \
--prop categories=Q1,Q2,Q3,Q4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop varyColors=true \
--prop firstSliceAngle=45 \
--prop dataLabels=true --prop labelPos=bestFit
# Chart 2: holeSize + leaderlines on doughnut
# Features: holeSize=65 (% of total radius — larger value = thinner ring),
# leaderlines=true (connecting lines from labels to slices, pie/doughnut)
officecli add "$FILE" "/3-Pie Advanced" --type chart \
--prop chartType=doughnut \
--prop title="Doughnut — holeSize + leaderlines" \
--prop series1=Revenue:35,28,22,15 \
--prop categories=North,South,East,West \
--prop colors=2E75B6,ED7D31,70AD47,FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop holeSize=65 \
--prop leaderlines=true \
--prop dataLabels=true --prop labelPos=outsideEnd
# Chart 3: title.overlay on pie (title floats over plot area)
# Features: title.overlay=true (title overlays the plot area, maximizing
# chart area — contrast with default where title reserves space above)
officecli add "$FILE" "/3-Pie Advanced" --type chart \
--prop chartType=pie \
--prop title="Overlaid Title" \
--prop title.overlay=true \
--prop series1=Mix:50,30,20 \
--prop categories=Online,Retail,Partner \
--prop colors=4472C4,70AD47,FFC000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop varyColors=false \
--prop dataLabels=percent --prop labelPos=center
# Chart 4: Doughnut — holeSize + firstSliceAngle + title.overlay combined
# Features: three doughnut-specific props together —
# holeSize, varyColors, title.overlay
officecli add "$FILE" "/3-Pie Advanced" --type chart \
--prop chartType=doughnut \
--prop title="Doughnut — Combined" \
--prop title.overlay=true \
--prop series1=Split:45,35,20 \
--prop categories=A,B,C \
--prop colors=C00000,FFC000,548235 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop holeSize=50 \
--prop varyColors=false \
--prop dataLabels=true --prop labelPos=center \
--prop labelFont=12:FFFFFF:true
# Remove blank default Sheet1 (all data is inline)
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+174
View File
@@ -0,0 +1,174 @@
# Radar Charts Showcase
This demo consists of three files that work together:
- **charts-radar.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-radar.xlsx** — The generated workbook with 5 sheets (1 default + 4 chart sheets, 16 charts total).
- **charts-radar.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-radar.py
# → charts-radar.xlsx
```
## Chart Sheets
### Sheet: 1-Radar Fundamentals
Four radar chart variants covering standard, marker, and filled styles.
```bash
# Basic radar (standard) with 3 series
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=standard \
--prop series1="Alice:85,70,90,60,75" \
--prop series2="Bob:65,90,70,80,85" \
--prop categories=Speed,Strength,Stamina,Agility,Accuracy \
--prop colors=4472C4,ED7D31,70AD47
# Radar with markers and data labels
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop marker=circle:6:2E75B6 \
--prop dataLabels=true
# Filled radar with transparency
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop transparency=40
# Filled radar with white outline separators
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop series.outline=FFFFFF-0.5 \
--prop transparency=35
```
**Features:** `radar`, `radarStyle=standard/marker/filled`, `marker=circle:6:color`, `transparency`, `series.outline`, `dataLabels`, `legend=bottom`
### Sheet: 2-Radar Styling
Four charts demonstrating title styling, shadows, axis fonts, gridlines, and chart area decoration.
```bash
# Title styling with font, size, color, bold, shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop title.font=Georgia --prop title.size=18 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30
# Series shadow on filled radar
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop series.shadow=000000-4-315-2-30 \
--prop transparency=30
# Axis font and gridlines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop axisfont=10:333333:Calibri \
--prop gridlines=D9D9D9:0.5
# Chart area styling with fills, corners, borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop plotFill=F5F5F5 --prop chartFill=FAFAFA \
--prop roundedCorners=true \
--prop chartArea.border=BFBFBF:0.5 \
--prop plotArea.border=D9D9D9:0.25
```
**Features:** `title.font/size/color/bold/shadow`, `series.shadow`, `axisfont`, `gridlines`, `plotFill`, `chartFill`, `roundedCorners`, `chartArea.border`, `plotArea.border`
### Sheet: 3-Labels & Legend
Four charts covering data labels, legend positioning, manual layout, and multi-series comparison.
```bash
# Data labels with font styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop labelFont=9:333333:true \
--prop marker=circle:6:2E75B6
# Legend positioning with overlay
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop legend=right \
--prop legendfont=10:1F4E79:Calibri \
--prop legend.overlay=true
# Manual plot area layout (fractional)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop plotArea.x=0.1 --prop plotArea.y=0.15 \
--prop plotArea.w=0.8 --prop plotArea.h=0.75
# Five series comparison
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop series1="Dev:90,70,80,65,75" \
--prop series2="QA:60,85,70,80,90" \
--prop series3="Design:75,80,85,70,60" \
--prop series4="PM:80,65,75,90,70" \
--prop series5="DevOps:70,75,60,85,80" \
--prop colors=4472C4,ED7D31,70AD47,FFC000,7030A0
```
**Features:** `dataLabels`, `labelPos=outsideEnd`, `labelFont`, `legend=right`, `legendfont`, `legend.overlay`, `plotArea.x/y/w/h`, 5+ series on single radar
### Sheet: 4-Advanced
Four charts with advanced effects: title glow, many-spoke layouts, themed styling, and overlap visualization.
```bash
# Title with glow and shadow effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop title.glow=4472C4-8 \
--prop title.shadow=000000-3-315-2-30 \
--prop marker=diamond:7:2E75B6
# 8-spoke radar with benchmark overlay
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop categories=Technical,Communication,Leadership,Creativity,Analytical,Teamwork,Adaptability,Initiative \
--prop gridlines=D9D9D9:0.25 --prop transparency=35
# Single-series with themed purple styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop colors=7030A0 --prop marker=square:7:7030A0 \
--prop title.color=7030A0 --prop plotFill=F8F0FF \
--prop chartArea.border=7030A0:0.5 --prop roundedCorners=true
# Before/After comparison with low transparency overlap
officecli add data.xlsx /Sheet --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop transparency=20 \
--prop series.outline=FFFFFF-0.75 \
--prop chartFill=FAFAFA --prop plotFill=F5F5F5
```
**Features:** `title.glow`, `title.shadow`, `marker=diamond/square`, 8-category spokes, themed color scheme, low-transparency overlap visualization, before/after comparison pattern
## Inspect the Generated File
```bash
officecli query charts-radar.xlsx chart
officecli get charts-radar.xlsx "/1-Radar Fundamentals/chart[1]"
```
+329
View File
@@ -0,0 +1,329 @@
#!/usr/bin/env python3
"""
Radar Charts Showcase — radar with standard, filled, and marker styles.
Generates: charts-radar.xlsx (16 charts across 4 sheets)
SDK twin of charts-radar.sh (officecli CLI). Both produce an equivalent
charts-radar.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in a single `doc.batch(...)` round-trip.
Each item is the same `{"command","parent","type","props"}` dict you'd put in
an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-radar.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-radar.xlsx")
def sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape (parent = the sheet path)."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
items = [
# ==================================================================
# Sheet: 1-Radar Fundamentals
# ==================================================================
sheet("1-Radar Fundamentals"),
# Chart 1: Basic radar (standard style) with 3 series
# Features: chartType=radar, radarStyle=standard, 3 series,
# categories as spokes
chart("/1-Radar Fundamentals",
chartType="radar",
radarStyle="standard",
title="Athlete Comparison",
series1="Alice:85,70,90,60,75",
series2="Bob:65,90,70,80,85",
series3="Carol:75,80,80,70,65",
categories="Speed,Strength,Stamina,Agility,Accuracy",
colors="4472C4,ED7D31,70AD47",
x="0", y="0", width="12", height="18",
legend="bottom"),
# Chart 2: Radar with markers (marker style)
# Features: radarStyle=marker, marker=circle:6:color, dataLabels
chart("/1-Radar Fundamentals",
chartType="radar",
radarStyle="marker",
title="Product Ratings",
series1="Product A:9,7,8,6,8",
series2="Product B:6,9,7,8,5",
categories="Quality,Price,Design,Support,Delivery",
colors="2E75B6,C00000",
marker="circle:6:2E75B6",
x="13", y="0", width="12", height="18",
legend="bottom",
dataLabels="true"),
# Chart 3: Filled radar with transparency
# Features: radarStyle=filled, transparency=40 (semi-transparent fill)
chart("/1-Radar Fundamentals",
chartType="radar",
radarStyle="filled",
title="Skills Assessment",
series1="Junior:50,40,60,70,55",
series2="Senior:85,80,75,90,80",
categories="Coding,Design,Testing,Communication,Leadership",
colors="4472C4,70AD47",
transparency="40",
x="0", y="19", width="12", height="18",
legend="bottom"),
# Chart 4: Filled radar with per-series colors and white outline
# Features: filled radar, series.outline (white border between areas),
# 3 overlapping series with transparency
chart("/1-Radar Fundamentals",
chartType="radar",
radarStyle="filled",
title="Department Scores",
series1="Engineering:90,75,60,85,70",
series2="Marketing:60,85,80,70,90",
series3="Sales:70,80,75,65,85",
categories="Innovation,Teamwork,Efficiency,Quality,Growth",
colors="4472C4,ED7D31,70AD47",
**{"series.outline": "FFFFFF-0.5"},
transparency="35",
x="13", y="19", width="12", height="18",
legend="bottom"),
# ==================================================================
# Sheet: 2-Radar Styling
# ==================================================================
sheet("2-Radar Styling"),
# Chart 1: Title styling (font, size, color, bold, shadow)
# Features: title.font, title.size, title.color, title.bold,
# title.shadow
chart("/2-Radar Styling",
chartType="radar",
radarStyle="marker",
title="Styled Title Demo",
series1="Team A:80,65,90,70,85",
categories="Attack,Defense,Speed,Skill,Stamina",
colors="2E75B6",
x="0", y="0", width="12", height="18",
**{"title.font": "Georgia", "title.size": "18",
"title.color": "1F4E79", "title.bold": "true",
"title.shadow": "000000-3-315-2-30"}),
# Chart 2: Series shadow effects
# Features: series.shadow on filled radar, transparency with shadow
chart("/2-Radar Styling",
chartType="radar",
radarStyle="filled",
title="Shadow Effects",
series1="Region A:75,80,65,90,70",
series2="Region B:60,70,85,75,80",
categories="Revenue,Profit,Growth,Retention,Satisfaction",
colors="4472C4,ED7D31",
**{"series.shadow": "000000-4-315-2-30"},
transparency="30",
x="13", y="0", width="12", height="18",
legend="bottom"),
# Chart 3: Axis font and gridlines styling
# Features: axisfont (size:color:fontFamily), gridlines (color-width)
chart("/2-Radar Styling",
chartType="radar",
radarStyle="marker",
title="Axis & Gridlines",
series1="Actual:70,85,60,75,80",
series2="Target:80,80,80,80,80",
categories="KPI 1,KPI 2,KPI 3,KPI 4,KPI 5",
colors="4472C4,C00000",
axisfont="10:333333:Calibri",
gridlines="D9D9D9:0.5",
x="0", y="19", width="12", height="18",
legend="bottom"),
# Chart 4: Plot fill, chart fill, rounded corners, borders
# Features: plotFill, chartFill, roundedCorners, chartArea.border,
# plotArea.border
chart("/2-Radar Styling",
chartType="radar",
radarStyle="filled",
title="Chart Area Styling",
series1="Score:85,70,90,60,75",
categories="Speed,Power,Technique,Endurance,Flexibility",
colors="4472C4",
transparency="25",
x="13", y="19", width="12", height="18",
plotFill="F5F5F5", chartFill="FAFAFA",
roundedCorners="true",
**{"chartArea.border": "BFBFBF:0.5",
"plotArea.border": "D9D9D9:0.25"}),
# ==================================================================
# Sheet: 3-Labels & Legend
# ==================================================================
sheet("3-Labels & Legend"),
# Chart 1: Data labels with font styling and position
# Features: dataLabels=true, labelPos=outsideEnd,
# labelFont (size:color:bold)
chart("/3-Labels & Legend",
chartType="radar",
radarStyle="marker",
title="Data Labels",
series1="Performance:88,72,95,67,81",
categories="Speed,Strength,Stamina,Agility,Accuracy",
colors="2E75B6",
marker="circle:6:2E75B6",
x="0", y="0", width="12", height="18",
dataLabels="true", labelPos="outsideEnd",
labelFont="9:333333:true"),
# Chart 2: Legend positioning and styling with overlay
# Features: legend=right, legendfont (size:color:fontFamily),
# legend.overlay
chart("/3-Labels & Legend",
chartType="radar",
radarStyle="standard",
title="Legend Styles",
series1="Alpha:80,60,75,90,70",
series2="Beta:70,80,85,65,75",
series3="Gamma:65,75,70,80,85",
categories="Metric A,Metric B,Metric C,Metric D,Metric E",
colors="4472C4,ED7D31,70AD47",
x="13", y="0", width="12", height="18",
legend="right",
legendfont="10:1F4E79:Calibri",
**{"legend.overlay": "true"}),
# Chart 3: Manual plot area layout
# Features: plotArea.x/y/w/h (fractional manual layout positioning)
chart("/3-Labels & Legend",
chartType="radar",
radarStyle="filled",
title="Custom Layout",
series1="Team:85,70,90,65,80",
categories="Vision,Execution,Culture,Agility,Impact",
colors="4472C4",
transparency="30",
x="0", y="19", width="12", height="18",
**{"plotArea.x": "0.1", "plotArea.y": "0.15",
"plotArea.w": "0.8", "plotArea.h": "0.75"}),
# Chart 4: Multiple series (5+) comparison
# Features: 5 series on one radar, distinguishing many overlapping lines
chart("/3-Labels & Legend",
chartType="radar",
radarStyle="standard",
title="Multi-Team Comparison",
series1="Dev:90,70,80,65,75",
series2="QA:60,85,70,80,90",
series3="Design:75,80,85,70,60",
series4="PM:80,65,75,90,70",
series5="DevOps:70,75,60,85,80",
categories="Speed,Quality,Innovation,Teamwork,Delivery",
colors="4472C4,ED7D31,70AD47,FFC000,7030A0",
x="13", y="19", width="12", height="18",
legend="bottom",
legendfont="9:333333:Calibri"),
# ==================================================================
# Sheet: 4-Advanced
# ==================================================================
sheet("4-Advanced"),
# Chart 1: Title glow and shadow effects
# Features: title.glow (color-radius), title.shadow combined
chart("/4-Advanced",
chartType="radar",
radarStyle="marker",
title="Glow & Shadow Title",
series1="Score:75,85,65,90,80",
categories="Creativity,Logic,Memory,Focus,Speed",
colors="2E75B6",
marker="diamond:7:2E75B6",
x="0", y="0", width="12", height="18",
**{"title.font": "Georgia", "title.size": "16",
"title.bold": "true", "title.color": "1F4E79",
"title.glow": "4472C4-8",
"title.shadow": "000000-3-315-2-30"}),
# Chart 2: Radar with many spokes (8 categories)
# Features: 8 categories (many spokes), benchmark overlay, gridlines
chart("/4-Advanced",
chartType="radar",
radarStyle="filled",
title="8-Spoke Assessment",
series1="Candidate:85,70,90,60,75,80,65,88",
series2="Benchmark:70,70,70,70,70,70,70,70",
categories="Technical,Communication,Leadership,Creativity,Analytical,Teamwork,Adaptability,Initiative",
colors="4472C4,BFBFBF",
transparency="35",
x="13", y="0", width="12", height="18",
legend="bottom",
gridlines="D9D9D9:0.25"),
# Chart 3: Single-series radar with full styling
# Features: single series with marker, full title/chart/plot styling,
# themed color scheme (purple)
chart("/4-Advanced",
chartType="radar",
radarStyle="marker",
title="Personal Profile",
series1="Self:92,78,85,65,88,70",
categories="Python,JavaScript,SQL,DevOps,Testing,Design",
colors="7030A0",
marker="square:7:7030A0",
x="0", y="19", width="12", height="18",
dataLabels="true", labelFont="9:7030A0:true",
plotFill="F8F0FF", chartFill="FFFFFF",
roundedCorners="true",
**{"title.font": "Calibri", "title.size": "14",
"title.color": "7030A0", "title.bold": "true",
"chartArea.border": "7030A0:0.5"}),
# Chart 4: Two-series filled radar with low transparency for overlap
# Features: low transparency (20%) for visible overlap, before/after
# comparison pattern, series.outline for separation
chart("/4-Advanced",
chartType="radar",
radarStyle="filled",
title="Before vs After",
series1="Before:55,40,65,50,45",
series2="After:85,75,80,70,80",
categories="Revenue,Efficiency,Satisfaction,Innovation,Retention",
colors="C00000,70AD47",
transparency="20",
**{"series.outline": "FFFFFF-0.75"},
x="13", y="19", width="12", height="18",
legend="bottom",
dataLabels="true", labelFont="9:333333:false",
chartFill="FAFAFA", plotFill="F5F5F5"),
# Remove blank default Sheet1 (all data is inline)
{"command": "remove", "path": "/Sheet1"},
]
doc.batch(items)
print(f" added {len(items)} sheets/charts")
print(f"Generated: {FILE}")
print(" 4 chart sheets, 16 charts total")
+272
View File
@@ -0,0 +1,272 @@
#!/bin/bash
# Radar Charts Showcase — radar with standard, filled, and marker styles.
# Generates charts-radar.xlsx (16 charts across 4 sheets).
# CLI twin of charts-radar.py (officecli Python SDK).
# Usage: ./charts-radar.sh
# 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")/charts-radar.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet: 1-Radar Fundamentals
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Radar Fundamentals"
# Chart 1: Basic radar (standard style) with 3 series
officecli add "$FILE" "/1-Radar Fundamentals" --type chart \
--prop chartType=radar \
--prop radarStyle=standard \
--prop title="Athlete Comparison" \
--prop series1="Alice:85,70,90,60,75" \
--prop series2="Bob:65,90,70,80,85" \
--prop series3="Carol:75,80,80,70,65" \
--prop categories=Speed,Strength,Stamina,Agility,Accuracy \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=bottom
# Chart 2: Radar with markers (marker style)
officecli add "$FILE" "/1-Radar Fundamentals" --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop title="Product Ratings" \
--prop series1="Product A:9,7,8,6,8" \
--prop series2="Product B:6,9,7,8,5" \
--prop categories=Quality,Price,Design,Support,Delivery \
--prop colors=2E75B6,C00000 \
--prop marker=circle:6:2E75B6 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop dataLabels=true
# Chart 3: Filled radar with transparency
officecli add "$FILE" "/1-Radar Fundamentals" --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop title="Skills Assessment" \
--prop series1="Junior:50,40,60,70,55" \
--prop series2="Senior:85,80,75,90,80" \
--prop categories=Coding,Design,Testing,Communication,Leadership \
--prop colors=4472C4,70AD47 \
--prop transparency=40 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom
# Chart 4: Filled radar with per-series colors and white outline
officecli add "$FILE" "/1-Radar Fundamentals" --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop title="Department Scores" \
--prop series1="Engineering:90,75,60,85,70" \
--prop series2="Marketing:60,85,80,70,90" \
--prop series3="Sales:70,80,75,65,85" \
--prop categories=Innovation,Teamwork,Efficiency,Quality,Growth \
--prop colors=4472C4,ED7D31,70AD47 \
--prop series.outline=FFFFFF-0.5 \
--prop transparency=35 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom
# ==========================================================================
# Sheet: 2-Radar Styling
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Radar Styling"
# Chart 1: Title styling (font, size, color, bold, shadow)
officecli add "$FILE" "/2-Radar Styling" --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop title="Styled Title Demo" \
--prop series1="Team A:80,65,90,70,85" \
--prop categories=Attack,Defense,Speed,Skill,Stamina \
--prop colors=2E75B6 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=18 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30
# Chart 2: Series shadow effects
officecli add "$FILE" "/2-Radar Styling" --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop title="Shadow Effects" \
--prop series1="Region A:75,80,65,90,70" \
--prop series2="Region B:60,70,85,75,80" \
--prop categories=Revenue,Profit,Growth,Retention,Satisfaction \
--prop colors=4472C4,ED7D31 \
--prop series.shadow=000000-4-315-2-30 \
--prop transparency=30 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=bottom
# Chart 3: Axis font and gridlines styling
officecli add "$FILE" "/2-Radar Styling" --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop title="Axis & Gridlines" \
--prop series1="Actual:70,85,60,75,80" \
--prop series2="Target:80,80,80,80,80" \
--prop categories="KPI 1,KPI 2,KPI 3,KPI 4,KPI 5" \
--prop colors=4472C4,C00000 \
--prop axisfont=10:333333:Calibri \
--prop gridlines=D9D9D9:0.5 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom
# Chart 4: Plot fill, chart fill, rounded corners, borders
officecli add "$FILE" "/2-Radar Styling" --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop title="Chart Area Styling" \
--prop series1="Score:85,70,90,60,75" \
--prop categories=Speed,Power,Technique,Endurance,Flexibility \
--prop colors=4472C4 \
--prop transparency=25 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop plotFill=F5F5F5 --prop chartFill=FAFAFA \
--prop roundedCorners=true \
--prop chartArea.border=BFBFBF:0.5 \
--prop plotArea.border=D9D9D9:0.25
# ==========================================================================
# Sheet: 3-Labels & Legend
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Labels & Legend"
# Chart 1: Data labels with font styling and position
officecli add "$FILE" "/3-Labels & Legend" --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop title="Data Labels" \
--prop series1="Performance:88,72,95,67,81" \
--prop categories=Speed,Strength,Stamina,Agility,Accuracy \
--prop colors=2E75B6 \
--prop marker=circle:6:2E75B6 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=outsideEnd \
--prop labelFont=9:333333:true
# Chart 2: Legend positioning and styling with overlay
officecli add "$FILE" "/3-Labels & Legend" --type chart \
--prop chartType=radar \
--prop radarStyle=standard \
--prop title="Legend Styles" \
--prop series1="Alpha:80,60,75,90,70" \
--prop series2="Beta:70,80,85,65,75" \
--prop series3="Gamma:65,75,70,80,85" \
--prop categories="Metric A,Metric B,Metric C,Metric D,Metric E" \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=right \
--prop legendfont=10:1F4E79:Calibri \
--prop legend.overlay=true
# Chart 3: Manual plot area layout
officecli add "$FILE" "/3-Labels & Legend" --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop title="Custom Layout" \
--prop series1="Team:85,70,90,65,80" \
--prop categories=Vision,Execution,Culture,Agility,Impact \
--prop colors=4472C4 \
--prop transparency=30 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop plotArea.x=0.1 --prop plotArea.y=0.15 \
--prop plotArea.w=0.8 --prop plotArea.h=0.75
# Chart 4: Multiple series (5+) comparison
officecli add "$FILE" "/3-Labels & Legend" --type chart \
--prop chartType=radar \
--prop radarStyle=standard \
--prop title="Multi-Team Comparison" \
--prop series1="Dev:90,70,80,65,75" \
--prop series2="QA:60,85,70,80,90" \
--prop series3="Design:75,80,85,70,60" \
--prop series4="PM:80,65,75,90,70" \
--prop series5="DevOps:70,75,60,85,80" \
--prop categories=Speed,Quality,Innovation,Teamwork,Delivery \
--prop colors=4472C4,ED7D31,70AD47,FFC000,7030A0 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop legendfont=9:333333:Calibri
# ==========================================================================
# Sheet: 4-Advanced
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="4-Advanced"
# Chart 1: Title glow and shadow effects
officecli add "$FILE" "/4-Advanced" --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop title="Glow & Shadow Title" \
--prop series1="Score:75,85,65,90,80" \
--prop categories=Creativity,Logic,Memory,Focus,Speed \
--prop colors=2E75B6 \
--prop marker=diamond:7:2E75B6 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.bold=true --prop title.color=1F4E79 \
--prop title.glow=4472C4-8 \
--prop title.shadow=000000-3-315-2-30
# Chart 2: Radar with many spokes (8 categories)
officecli add "$FILE" "/4-Advanced" --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop title="8-Spoke Assessment" \
--prop series1="Candidate:85,70,90,60,75,80,65,88" \
--prop series2="Benchmark:70,70,70,70,70,70,70,70" \
--prop categories=Technical,Communication,Leadership,Creativity,Analytical,Teamwork,Adaptability,Initiative \
--prop colors=4472C4,BFBFBF \
--prop transparency=35 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop gridlines=D9D9D9:0.25
# Chart 3: Single-series radar with full styling
officecli add "$FILE" "/4-Advanced" --type chart \
--prop chartType=radar \
--prop radarStyle=marker \
--prop title="Personal Profile" \
--prop series1="Self:92,78,85,65,88,70" \
--prop categories=Python,JavaScript,SQL,DevOps,Testing,Design \
--prop colors=7030A0 \
--prop marker=square:7:7030A0 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelFont=9:7030A0:true \
--prop title.font=Calibri --prop title.size=14 \
--prop title.color=7030A0 --prop title.bold=true \
--prop plotFill=F8F0FF --prop chartFill=FFFFFF \
--prop roundedCorners=true \
--prop chartArea.border=7030A0:0.5
# Chart 4: Two-series filled radar with low transparency for overlap
officecli add "$FILE" "/4-Advanced" --type chart \
--prop chartType=radar \
--prop radarStyle=filled \
--prop title="Before vs After" \
--prop series1="Before:55,40,65,50,45" \
--prop series2="After:85,75,80,70,80" \
--prop categories=Revenue,Efficiency,Satisfaction,Innovation,Retention \
--prop colors=C00000,70AD47 \
--prop transparency=20 \
--prop series.outline=FFFFFF-0.75 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=bottom \
--prop dataLabels=true --prop labelFont=9:333333:false \
--prop chartFill=FAFAFA --prop plotFill=F5F5F5
# Remove blank default Sheet1 (all data is inline)
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+217
View File
@@ -0,0 +1,217 @@
# Scatter Charts Showcase
This demo consists of three files that work together:
- **charts-scatter.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-scatter.xlsx** — The generated workbook with 7 sheets (1 default + 6 chart sheets, 24 charts total).
- **charts-scatter.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-scatter.py
# → charts-scatter.xlsx
```
## Chart Sheets
### Sheet: 1-Scatter Fundamentals
Four scatter variants covering markers+lines, marker-only, smooth curves, and line-only.
```bash
# Basic scatter with circle markers and connecting lines
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop series1="Male:62,68,72,78,82,88,95" \
--prop categories=160,165,170,175,180,185,190 \
--prop marker=circle --prop markerSize=6 --prop lineWidth=1.5 \
--prop catTitle=Height (cm) --prop axisTitle=Weight (kg)
# Scatter marker-only (no connecting lines)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=marker \
--prop markerSize=8 --prop gridlines=D9D9D9:0.5:dot
# Scatter smooth curve (Bezier interpolation)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=smooth \
--prop smooth=true --prop marker=diamond --prop lineWidth=2
# Scatter line-only (no markers)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=line \
--prop showMarker=false --prop lineWidth=2.5 --prop lineDash=dash
```
**Features:** `scatter`, `scatterStyle=marker|smooth|line`, `smooth=true`, `marker=circle|diamond`, `markerSize`, `lineWidth`, `lineDash=dash`, `showMarker=false`, `catTitle`, `axisTitle`, `gridlines`
### Sheet: 2-Marker Styles
Four charts demonstrating all marker shapes and per-series marker control.
```bash
# Per-series markers: circle, diamond, square
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop series1.marker=circle --prop series2.marker=diamond \
--prop series3.marker=square --prop markerSize=8
# Per-series markers: triangle, star, x
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop series1.marker=triangle --prop series2.marker=star \
--prop series3.marker=x --prop markerSize=9
# Large markers with plus and dash shapes
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=marker \
--prop series1.marker=circle --prop series2.marker=plus \
--prop series3.marker=dash --prop markerSize=10
# showMarker=false with lineDash=dashDot
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=lineMarker \
--prop showMarker=false --prop lineDash=dashDot
```
**Features:** `series{N}.marker=circle|diamond|square|triangle|star|x|plus|dash`, `markerSize`, `scatterStyle=lineMarker|marker`, `showMarker=false`, `lineDash=dashDot`
### Sheet: 3-Trendlines
Four charts covering all trendline types and sub-properties.
```bash
# Linear trendline with equation display
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=marker \
--prop trendline=linear \
--prop series1.trendline.equation=true
# Polynomial (order 3) with R-squared display
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=marker \
--prop trendline=poly:3 \
--prop series1.trendline.rsquared=true
# Exponential with forward/backward extrapolation
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=marker \
--prop trendline=exp:2:1 \
--prop series1.trendline.name=Exponential Fit
# Per-series trendlines: linear vs logarithmic
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=marker \
--prop series1.trendline=linear --prop series2.trendline=log \
--prop series1.trendline.equation=true \
--prop series2.trendline.rsquared=true
```
**Features:** `trendline=linear|poly:N|exp|log|power|movingAvg`, `trendline=exp:forward:backward` (extrapolation), `series{N}.trendline` (per-series), `series{N}.trendline.equation`, `series{N}.trendline.rsquared`, `series{N}.trendline.name`
### Sheet: 4-Error Bars
Four charts covering all error bar types on scatter series.
```bash
# Fixed error bars (+/-5)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop errBars=fixed:5
# Percentage error bars (10%)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop errBars=percent:10
# Standard deviation error bars
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop errBars=stddev
# Standard error with series shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop errBars=stderr \
--prop series.shadow=000000-4-315-2-30
```
**Features:** `errBars=fixed:N|percent:N|stddev|stderr`, `series.shadow`
### Sheet: 5-Styling
Four charts covering title styling, fills, gradients, borders, and axis formatting.
```bash
# Title styling with series shadow and outline
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30 \
--prop series.shadow=000000-4-315-2-30 \
--prop series.outline=333333-1.5
# Gradients, transparency, plotFill, chartFill
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop 'gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90' \
--prop transparency=20 \
--prop plotFill=F5F5F5 --prop chartFill=FAFAFA
# Axis font, gridlines, minor gridlines, axis line
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop axisfont=9:C00000:Arial \
--prop gridlines=BFBFBF:0.75:solid \
--prop minorGridlines=E0E0E0:0.25:dot \
--prop axisLine=333333:1
# Chart/plot borders and rounded corners
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop chartArea.border=333333-1.5 \
--prop plotArea.border=999999-0.75 \
--prop roundedCorners=true
```
**Features:** `title.font/size/color/bold`, `title.shadow`, `series.shadow`, `series.outline`, `gradients`, `transparency`, `plotFill`, `chartFill`, `axisfont`, `gridlines`, `minorGridlines`, `axisLine`, `chartArea.border`, `plotArea.border`, `roundedCorners`
### Sheet: 6-Advanced
Four charts covering secondary axis, reference lines, log scale, and conditional coloring.
```bash
# Secondary Y-axis for dual-unit scatter
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop secondaryAxis=2
# Reference line (horizontal target)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop referenceLine=75:FF0000:Target:dash
# Logarithmic axis with min/max bounds
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter \
--prop logBase=10 \
--prop axisMin=1 --prop axisMax=10000
# Data labels with conditional color rule
officecli add data.xlsx /Sheet --type chart \
--prop chartType=scatter --prop scatterStyle=marker \
--prop dataLabels=true --prop labelPos=top \
--prop colorRule=60:C00000:00AA00
```
**Features:** `secondaryAxis`, `referenceLine=value:color:label:dash`, `logBase`, `axisMin`, `axisMax`, `dataLabels`, `labelPos=top`, `colorRule=threshold:belowColor:aboveColor`
## Inspect the Generated File
```bash
officecli query charts-scatter.xlsx chart
officecli get charts-scatter.xlsx "/1-Scatter Fundamentals/chart[1]"
```
+483
View File
@@ -0,0 +1,483 @@
#!/usr/bin/env python3
"""
Scatter Charts Showcase — scatter with all marker, trendline, error bar, and styling variations.
Generates: charts-scatter.xlsx
Every scatter chart feature officecli supports is demonstrated at least once:
scatter styles, marker types, smooth curves, trendlines (linear, polynomial,
exponential, logarithmic, power, movingAvg), error bars, axis scaling,
gridlines, data labels, legend, fills, shadows, borders, secondary axis,
reference lines, log scale, and color rules.
6 sheets, 24 charts total.
1-Scatter Fundamentals 4 charts — basic scatter, marker-only, smooth curve, line-only
2-Marker Styles 4 charts — per-series markers, shapes, sizes, toggle
3-Trendlines 4 charts — linear, polynomial, exponential, per-series
4-Error Bars 4 charts — fixed, percent, stddev, stderr
5-Styling 4 charts — title/shadow, gradients, axis/grid, borders
6-Advanced 4 charts — secondary axis, reference line, log scale, color rule
SDK twin of charts-scatter.sh (officecli CLI). Both produce an equivalent
charts-scatter.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in a single `doc.batch(...)` round-trip.
Each item is the same `{"command","parent","type","props"}` dict you'd put in
an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-scatter.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-scatter.xlsx")
def sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(parent, **props):
"""One `add chart` item in batch-shape."""
return {"command": "add", "parent": parent, "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
items = [
# ==================================================================
# Sheet: 1-Scatter Fundamentals
# ==================================================================
sheet("1-Scatter Fundamentals"),
# Chart 1: Basic scatter with circle markers and connecting lines
# Features: chartType=scatter, marker=circle, markerSize=6, lineWidth=1.5,
# catTitle, axisTitle, legend
chart("/1-Scatter Fundamentals",
chartType="scatter",
title="Height vs Weight",
categories="160,165,170,175,180,185,190",
series1="Male:62,68,72,78,82,88,95",
series2="Female:50,55,58,62,65,70,74",
colors="2E75B6,ED7D31",
x="0", y="0", width="12", height="18",
marker="circle", markerSize="6",
lineWidth="1.5",
catTitle="Height (cm)", axisTitle="Weight (kg)",
legend="bottom"),
# Chart 2: Scatter marker-only (scatterStyle=marker), various marker sizes
# Features: scatterStyle=marker (no connecting lines), markerSize=8,
# gridlines styling
chart("/1-Scatter Fundamentals",
chartType="scatter",
scatterStyle="marker",
title="Study Hours vs Test Score",
categories="1,2,3,4,5,6,7,8",
series1="Class A:55,60,65,72,78,82,88,92",
series2="Class B:50,58,62,68,74,80,85,90",
colors="4472C4,70AD47",
x="13", y="0", width="12", height="18",
markerSize="8",
catTitle="Study Hours", axisTitle="Score",
gridlines="D9D9D9:0.5:dot"),
# Chart 3: Scatter smooth curve (smooth=true, scatterStyle=smooth)
# Features: scatterStyle=smooth, smooth=true (Bezier interpolation),
# marker=diamond, single series
chart("/1-Scatter Fundamentals",
chartType="scatter",
scatterStyle="smooth",
smooth="true",
title="Temperature vs Ice Cream Sales",
categories="15,18,22,25,28,30,33,35",
series1="Sales ($):120,180,260,340,420,480,530,560",
colors="C00000",
x="0", y="19", width="12", height="18",
marker="diamond", markerSize="7",
lineWidth="2",
catTitle="Temperature (C)", axisTitle="Daily Sales ($)"),
# Chart 4: Scatter line-only (no markers, scatterStyle=line)
# Features: scatterStyle=line (line without markers), showMarker=false,
# lineWidth=2.5, lineDash=dash
chart("/1-Scatter Fundamentals",
chartType="scatter",
scatterStyle="line",
title="Altitude vs Air Pressure",
categories="0,500,1000,2000,3000,5000,8000",
series1="Pressure (hPa):1013,955,899,795,701,540,356",
colors="1F4E79",
x="13", y="19", width="12", height="18",
showMarker="false",
lineWidth="2.5",
lineDash="dash",
catTitle="Altitude (m)", axisTitle="Pressure (hPa)"),
# ==================================================================
# Sheet: 2-Marker Styles
# ==================================================================
sheet("2-Marker Styles"),
# Chart 1: Per-series markers — circle, diamond, square
# Features: series1.marker=circle, series2.marker=diamond,
# series3.marker=square (per-series marker style)
chart("/2-Marker Styles",
chartType="scatter",
title="Per-Series Markers: Circle, Diamond, Square",
categories="10,20,30,40,50,60",
series1="Sensor A:12,28,35,42,55,68",
series2="Sensor B:8,22,30,38,48,58",
series3="Sensor C:15,25,32,45,52,62",
colors="4472C4,ED7D31,70AD47",
x="0", y="0", width="12", height="18",
**{"series1.marker": "circle",
"series2.marker": "diamond",
"series3.marker": "square"},
markerSize="8", lineWidth="1",
legend="bottom"),
# Chart 2: Per-series markers — triangle, star, x
# Features: series1.marker=triangle, series2.marker=star, series3.marker=x
chart("/2-Marker Styles",
chartType="scatter",
title="Per-Series Markers: Triangle, Star, X",
categories="5,10,15,20,25,30",
series1="Lab 1:18,32,28,45,52,60",
series2="Lab 2:22,25,38,40,48,55",
series3="Lab 3:10,20,32,35,42,50",
colors="FFC000,9DC3E6,843C0B",
x="13", y="0", width="12", height="18",
**{"series1.marker": "triangle",
"series2.marker": "star",
"series3.marker": "x"},
markerSize="9", lineWidth="1",
legend="bottom"),
# Chart 3: Large markers with series colors, markerSize=10
# Features: markerSize=10, marker=plus, marker=dash, scatterStyle=marker
chart("/2-Marker Styles",
chartType="scatter",
scatterStyle="marker",
title="Large Markers (size=10)",
categories="100,200,300,400,500",
series1="Revenue:150,280,350,420,510",
series2="Profit:80,140,180,220,280",
series3="Cost:70,140,170,200,230",
colors="2E75B6,548235,BF8F00",
x="0", y="19", width="12", height="18",
**{"series1.marker": "circle",
"series2.marker": "plus",
"series3.marker": "dash"},
markerSize="10",
legend="right"),
# Chart 4: showMarker=false (line only) vs showMarker=true
# Features: scatterStyle=lineMarker, showMarker=false (markers hidden),
# lineDash=dashDot
chart("/2-Marker Styles",
chartType="scatter",
scatterStyle="lineMarker",
title="Marker Toggle (none shown)",
categories="1,2,3,4,5,6,7,8,9,10",
series1="Signal:3,7,5,11,9,14,12,18,15,20",
series2="Noise:2,4,6,5,8,7,10,9,12,11",
colors="4472C4,BFBFBF",
x="13", y="19", width="12", height="18",
showMarker="false",
lineWidth="2",
lineDash="dashDot",
legend="bottom"),
# ==================================================================
# Sheet: 3-Trendlines
# ==================================================================
sheet("3-Trendlines"),
# Chart 1: Linear trendline with equation display
# Features: trendline=linear, series1.trendline.equation=true
chart("/3-Trendlines",
chartType="scatter",
scatterStyle="marker",
title="Linear Trendline + Equation",
categories="1,2,3,4,5,6,7,8,9,10",
series1="Observed:8,15,22,28,33,42,48,55,60,68",
colors="4472C4",
x="0", y="0", width="12", height="18",
markerSize="7",
trendline="linear",
**{"series1.trendline.equation": "true"},
catTitle="X", axisTitle="Y"),
# Chart 2: Polynomial trendline (order 3) with R-squared display
# Features: trendline=poly:3, series1.trendline.rsquared=true
chart("/3-Trendlines",
chartType="scatter",
scatterStyle="marker",
title="Polynomial (order 3) + R-squared",
categories="1,2,3,4,5,6,7,8,9,10",
series1="Measurement:5,12,25,30,28,35,50,62,58,72",
colors="70AD47",
x="13", y="0", width="12", height="18",
markerSize="7", marker="square",
trendline="poly:3",
**{"series1.trendline.rsquared": "true"},
catTitle="Sample", axisTitle="Value"),
# Chart 3: Exponential trendline with forward/backward extrapolation
# Features: trendline=exp:2:1 (forward=2, backward=1),
# series1.trendline.name (custom trendline label)
chart("/3-Trendlines",
chartType="scatter",
scatterStyle="marker",
title="Exponential + Extrapolation",
categories="1,2,3,4,5,6,7,8",
series1="Growth:2,4,7,12,20,35,58,95",
colors="ED7D31",
x="0", y="19", width="12", height="18",
markerSize="7", marker="triangle",
trendline="exp:2:1",
**{"series1.trendline.name": "Exponential Fit"},
catTitle="Period", axisTitle="Amount"),
# Chart 4: Per-series trendlines — linear vs logarithmic
# Features: series1.trendline=linear, series2.trendline=log,
# per-series trendline with sub-properties
chart("/3-Trendlines",
chartType="scatter",
scatterStyle="marker",
title="Per-Series: Linear vs Logarithmic",
categories="1,2,4,8,16,32,64",
series1="Dataset A:10,18,30,45,62,78,95",
series2="Dataset B:5,25,38,45,50,54,56",
colors="4472C4,C00000",
x="13", y="19", width="12", height="18",
markerSize="7",
**{"series1.trendline": "linear",
"series2.trendline": "log",
"series1.trendline.equation": "true",
"series2.trendline.rsquared": "true"},
legend="bottom"),
# ==================================================================
# Sheet: 4-Error Bars
# ==================================================================
sheet("4-Error Bars"),
# Chart 1: Fixed error bars (errBars=fixed:5)
# Features: errBars=fixed:5 (constant +/-5 error)
chart("/4-Error Bars",
chartType="scatter",
title="Fixed Error Bars (+-5)",
categories="10,20,30,40,50,60",
series1="Measurement:25,42,58,72,88,105",
colors="4472C4",
x="0", y="0", width="12", height="18",
marker="circle", markerSize="7",
lineWidth="1",
errBars="fixed:5",
catTitle="Input", axisTitle="Output"),
# Chart 2: Percentage error bars (errBars=percent:10)
# Features: errBars=percent:10 (10% of each value)
chart("/4-Error Bars",
chartType="scatter",
title="Percentage Error Bars (10%)",
categories="5,10,15,20,25,30",
series1="Yield:120,185,240,310,375,450",
colors="70AD47",
x="13", y="0", width="12", height="18",
marker="diamond", markerSize="7",
lineWidth="1",
errBars="percent:10",
catTitle="Dosage", axisTitle="Yield"),
# Chart 3: Standard deviation error bars (errBars=stddev)
# Features: errBars=stddev (standard deviation), multi-series with errBars
chart("/4-Error Bars",
chartType="scatter",
title="Standard Deviation Error Bars",
categories="0,1,2,3,4,5,6,7",
series1="Trial 1:48,52,47,55,50,53,49,51",
series2="Trial 2:30,35,28,40,32,38,34,36",
colors="ED7D31,9DC3E6",
x="0", y="19", width="12", height="18",
marker="square", markerSize="6",
lineWidth="1",
errBars="stddev",
legend="bottom"),
# Chart 4: Standard error with series styling
# Features: errBars=stderr, series.shadow, gridlines styling
chart("/4-Error Bars",
chartType="scatter",
title="Standard Error + Styled Series",
categories="2,4,6,8,10,12,14",
series1="Experiment:18,32,28,45,40,55,52",
colors="843C0B",
x="13", y="19", width="12", height="18",
marker="star", markerSize="8",
lineWidth="1.5",
errBars="stderr",
**{"series.shadow": "000000-4-315-2-30"},
gridlines="D9D9D9:0.5:dot",
catTitle="Time (h)", axisTitle="Response"),
# ==================================================================
# Sheet: 5-Styling
# ==================================================================
sheet("5-Styling"),
# Chart 1: Title styling, series shadow, series outline
# Features: title.font, title.size, title.color, title.bold, title.shadow,
# series.shadow, series.outline
chart("/5-Styling",
chartType="scatter",
title="Styled Title + Series Effects",
categories="10,20,30,40,50",
series1="Alpha:15,35,28,48,55",
series2="Beta:8,22,32,40,50",
colors="4472C4,ED7D31",
x="0", y="0", width="12", height="18",
marker="circle", markerSize="8", lineWidth="2",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true",
"title.shadow": "000000-3-315-2-30",
"series.shadow": "000000-4-315-2-30",
"series.outline": "333333:1.5"},
legend="bottom"),
# Chart 2: Gradients, transparency, plotFill, chartFill
# Features: gradients (per-series gradient), transparency, plotFill, chartFill
chart("/5-Styling",
chartType="scatter",
title="Gradients + Fills",
categories="5,15,25,35,45",
series1="Group 1:12,28,35,42,55",
series2="Group 2:8,18,22,38,48",
x="13", y="0", width="12", height="18",
marker="diamond", markerSize="8", lineWidth="1.5",
gradients="4472C4-BDD7EE:90;ED7D31-FBE5D6:90",
transparency="20",
plotFill="F5F5F5",
chartFill="FAFAFA",
legend="bottom"),
# Chart 3: Axis font, gridlines, minor gridlines, axis line
# Features: axisfont (size:color:font), gridlines, minorGridlines, axisLine
chart("/5-Styling",
chartType="scatter",
title="Axis & Grid Styling",
categories="0,10,20,30,40,50",
series1="Readings:5,22,38,52,68,82",
colors="2E75B6",
x="0", y="19", width="12", height="18",
marker="circle", markerSize="7", lineWidth="1.5",
axisfont="9:C00000:Arial",
gridlines="BFBFBF:0.75:solid",
minorGridlines="E0E0E0:0.25:dot",
axisLine="333333:1",
catTitle="X Axis", axisTitle="Y Axis"),
# Chart 4: Chart area border, plot area border, rounded corners
# Features: chartArea.border, plotArea.border, roundedCorners
chart("/5-Styling",
chartType="scatter",
title="Borders + Rounded Corners",
categories="1,3,5,7,9",
series1="Data:10,25,18,35,28",
colors="548235",
x="13", y="19", width="12", height="18",
marker="square", markerSize="8", lineWidth="1.5",
**{"chartArea.border": "333333:1.5",
"plotArea.border": "999999:0.75"},
roundedCorners="true",
chartFill="FFFFFF",
plotFill="F0F0F0"),
# ==================================================================
# Sheet: 6-Advanced
# ==================================================================
sheet("6-Advanced"),
# Chart 1: Secondary axis
# Features: secondaryAxis=2 (series 2 on right Y-axis)
chart("/6-Advanced",
chartType="scatter",
title="Secondary Y-Axis",
categories="10,20,30,40,50,60",
series1="Temperature (C):15,20,28,32,38,42",
series2="Humidity (%):85,78,65,58,45,38",
colors="C00000,4472C4",
x="0", y="0", width="12", height="18",
marker="circle", markerSize="7", lineWidth="1.5",
secondaryAxis="2",
legend="bottom",
catTitle="Location"),
# Chart 2: Reference line (horizontal target)
# Features: referenceLine=value:color:label:dash (horizontal target line)
chart("/6-Advanced",
chartType="scatter",
title="Reference Line (Target=75)",
categories="1,2,3,4,5,6,7,8",
series1="Score:60,68,72,78,80,74,82,88",
colors="70AD47",
x="13", y="0", width="12", height="18",
marker="diamond", markerSize="7", lineWidth="1.5",
referenceLine="75:FF0000:Target:dash",
catTitle="Week", axisTitle="Performance"),
# Chart 3: Axis min/max and log scale
# Features: logBase=10 (logarithmic value axis), axisMin, axisMax
chart("/6-Advanced",
chartType="scatter",
title="Log Scale (base 10)",
categories="1,10,100,1000,10000",
series1="Response:2,15,120,950,8500",
colors="1F4E79",
x="0", y="19", width="12", height="18",
marker="triangle", markerSize="8", lineWidth="1.5",
logBase="10",
axisMin="1", axisMax="10000",
catTitle="Concentration", axisTitle="Response"),
# Chart 4: Data labels and color rule
# Features: dataLabels=true, labelPos=top, colorRule=threshold:below:above
# (points below 60 = red, above = green)
chart("/6-Advanced",
chartType="scatter",
scatterStyle="marker",
title="Data Labels + Color Rule",
categories="1,2,3,4,5,6,7,8",
series1="KPI:45,62,38,78,55,82,48,90",
colors="4472C4",
x="13", y="19", width="12", height="18",
markerSize="9",
dataLabels="true", labelPos="top",
colorRule="60:C00000:00AA00",
catTitle="Quarter", axisTitle="KPI Score"),
# Remove blank default Sheet1 (all data is inline)
{"command": "remove", "path": "/Sheet1"},
]
doc.batch(items)
print(f" added {len(items)} sheets/charts")
print(f"Generated: {FILE}")
print(" 6 chart sheets, 24 charts total")
+410
View File
@@ -0,0 +1,410 @@
#!/bin/bash
# Scatter Charts Showcase — scatter with all marker, trendline, error bar, and
# styling variations. CLI twin of charts-scatter.py (officecli Python SDK).
# Both produce an equivalent charts-scatter.xlsx.
#
# 6 sheets, 24 charts total:
# 1-Scatter Fundamentals basic scatter, marker-only, smooth curve, line-only
# 2-Marker Styles per-series markers, shapes, sizes, toggle
# 3-Trendlines linear, polynomial, exponential, per-series
# 4-Error Bars fixed, percent, stddev, stderr
# 5-Styling title/shadow, gradients, axis/grid, borders
# 6-Advanced secondary axis, reference line, log scale, color rule
#
# Usage: ./charts-scatter.sh [officecli path]
# 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.
CLI="${1:-officecli}"
FILE="$(dirname "$0")/charts-scatter.xlsx"
rm -f "$FILE"
$CLI create "$FILE"
$CLI open "$FILE"
# ==========================================================================
# Sheet: 1-Scatter Fundamentals
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name="1-Scatter Fundamentals"
# Chart 1: Basic scatter with circle markers and connecting lines
$CLI add "$FILE" "/1-Scatter Fundamentals" --type chart \
--prop chartType=scatter \
--prop title="Height vs Weight" \
--prop categories=160,165,170,175,180,185,190 \
--prop series1="Male:62,68,72,78,82,88,95" \
--prop series2="Female:50,55,58,62,65,70,74" \
--prop colors=2E75B6,ED7D31 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=circle --prop markerSize=6 \
--prop lineWidth=1.5 \
--prop catTitle="Height (cm)" --prop axisTitle="Weight (kg)" \
--prop legend=bottom
# Chart 2: Scatter marker-only (scatterStyle=marker), various marker sizes
$CLI add "$FILE" "/1-Scatter Fundamentals" --type chart \
--prop chartType=scatter \
--prop scatterStyle=marker \
--prop title="Study Hours vs Test Score" \
--prop categories=1,2,3,4,5,6,7,8 \
--prop series1="Class A:55,60,65,72,78,82,88,92" \
--prop series2="Class B:50,58,62,68,74,80,85,90" \
--prop colors=4472C4,70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop markerSize=8 \
--prop catTitle="Study Hours" --prop axisTitle=Score \
--prop gridlines=D9D9D9:0.5:dot
# Chart 3: Scatter smooth curve (smooth=true, scatterStyle=smooth)
$CLI add "$FILE" "/1-Scatter Fundamentals" --type chart \
--prop chartType=scatter \
--prop scatterStyle=smooth \
--prop smooth=true \
--prop title="Temperature vs Ice Cream Sales" \
--prop categories=15,18,22,25,28,30,33,35 \
--prop series1="Sales (\$):120,180,260,340,420,480,530,560" \
--prop colors=C00000 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=diamond --prop markerSize=7 \
--prop lineWidth=2 \
--prop catTitle="Temperature (C)" --prop axisTitle="Daily Sales (\$)"
# Chart 4: Scatter line-only (no markers, scatterStyle=line)
$CLI add "$FILE" "/1-Scatter Fundamentals" --type chart \
--prop chartType=scatter \
--prop scatterStyle=line \
--prop title="Altitude vs Air Pressure" \
--prop categories=0,500,1000,2000,3000,5000,8000 \
--prop series1="Pressure (hPa):1013,955,899,795,701,540,356" \
--prop colors=1F4E79 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop showMarker=false \
--prop lineWidth=2.5 \
--prop lineDash=dash \
--prop catTitle="Altitude (m)" --prop axisTitle="Pressure (hPa)"
# ==========================================================================
# Sheet: 2-Marker Styles
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name="2-Marker Styles"
# Chart 1: Per-series markers — circle, diamond, square
$CLI add "$FILE" "/2-Marker Styles" --type chart \
--prop chartType=scatter \
--prop title="Per-Series Markers: Circle, Diamond, Square" \
--prop categories=10,20,30,40,50,60 \
--prop series1="Sensor A:12,28,35,42,55,68" \
--prop series2="Sensor B:8,22,30,38,48,58" \
--prop series3="Sensor C:15,25,32,45,52,62" \
--prop colors=4472C4,ED7D31,70AD47 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop series1.marker=circle \
--prop series2.marker=diamond \
--prop series3.marker=square \
--prop markerSize=8 --prop lineWidth=1 \
--prop legend=bottom
# Chart 2: Per-series markers — triangle, star, x
$CLI add "$FILE" "/2-Marker Styles" --type chart \
--prop chartType=scatter \
--prop title="Per-Series Markers: Triangle, Star, X" \
--prop categories=5,10,15,20,25,30 \
--prop series1="Lab 1:18,32,28,45,52,60" \
--prop series2="Lab 2:22,25,38,40,48,55" \
--prop series3="Lab 3:10,20,32,35,42,50" \
--prop colors=FFC000,9DC3E6,843C0B \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop series1.marker=triangle \
--prop series2.marker=star \
--prop series3.marker=x \
--prop markerSize=9 --prop lineWidth=1 \
--prop legend=bottom
# Chart 3: Large markers with series colors, markerSize=10
$CLI add "$FILE" "/2-Marker Styles" --type chart \
--prop chartType=scatter \
--prop scatterStyle=marker \
--prop title="Large Markers (size=10)" \
--prop categories=100,200,300,400,500 \
--prop series1="Revenue:150,280,350,420,510" \
--prop series2="Profit:80,140,180,220,280" \
--prop series3="Cost:70,140,170,200,230" \
--prop colors=2E75B6,548235,BF8F00 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop series1.marker=circle \
--prop series2.marker=plus \
--prop series3.marker=dash \
--prop markerSize=10 \
--prop legend=right
# Chart 4: showMarker=false (line only) vs showMarker=true
$CLI add "$FILE" "/2-Marker Styles" --type chart \
--prop chartType=scatter \
--prop scatterStyle=lineMarker \
--prop title="Marker Toggle (none shown)" \
--prop categories=1,2,3,4,5,6,7,8,9,10 \
--prop series1="Signal:3,7,5,11,9,14,12,18,15,20" \
--prop series2="Noise:2,4,6,5,8,7,10,9,12,11" \
--prop colors=4472C4,BFBFBF \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop showMarker=false \
--prop lineWidth=2 \
--prop lineDash=dashDot \
--prop legend=bottom
# ==========================================================================
# Sheet: 3-Trendlines
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name="3-Trendlines"
# Chart 1: Linear trendline with equation display
$CLI add "$FILE" "/3-Trendlines" --type chart \
--prop chartType=scatter \
--prop scatterStyle=marker \
--prop title="Linear Trendline + Equation" \
--prop categories=1,2,3,4,5,6,7,8,9,10 \
--prop series1="Observed:8,15,22,28,33,42,48,55,60,68" \
--prop colors=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop markerSize=7 \
--prop trendline=linear \
--prop series1.trendline.equation=true \
--prop catTitle=X --prop axisTitle=Y
# Chart 2: Polynomial trendline (order 3) with R-squared display
$CLI add "$FILE" "/3-Trendlines" --type chart \
--prop chartType=scatter \
--prop scatterStyle=marker \
--prop title="Polynomial (order 3) + R-squared" \
--prop categories=1,2,3,4,5,6,7,8,9,10 \
--prop series1="Measurement:5,12,25,30,28,35,50,62,58,72" \
--prop colors=70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop markerSize=7 --prop marker=square \
--prop trendline=poly:3 \
--prop series1.trendline.rsquared=true \
--prop catTitle=Sample --prop axisTitle=Value
# Chart 3: Exponential trendline with forward/backward extrapolation
$CLI add "$FILE" "/3-Trendlines" --type chart \
--prop chartType=scatter \
--prop scatterStyle=marker \
--prop title="Exponential + Extrapolation" \
--prop categories=1,2,3,4,5,6,7,8 \
--prop series1="Growth:2,4,7,12,20,35,58,95" \
--prop colors=ED7D31 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop markerSize=7 --prop marker=triangle \
--prop trendline=exp:2:1 \
--prop series1.trendline.name="Exponential Fit" \
--prop catTitle=Period --prop axisTitle=Amount
# Chart 4: Per-series trendlines — linear vs logarithmic
$CLI add "$FILE" "/3-Trendlines" --type chart \
--prop chartType=scatter \
--prop scatterStyle=marker \
--prop title="Per-Series: Linear vs Logarithmic" \
--prop categories=1,2,4,8,16,32,64 \
--prop series1="Dataset A:10,18,30,45,62,78,95" \
--prop series2="Dataset B:5,25,38,45,50,54,56" \
--prop colors=4472C4,C00000 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop markerSize=7 \
--prop series1.trendline=linear \
--prop series2.trendline=log \
--prop series1.trendline.equation=true \
--prop series2.trendline.rsquared=true \
--prop legend=bottom
# ==========================================================================
# Sheet: 4-Error Bars
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name="4-Error Bars"
# Chart 1: Fixed error bars (errBars=fixed:5)
$CLI add "$FILE" "/4-Error Bars" --type chart \
--prop chartType=scatter \
--prop title="Fixed Error Bars (+-5)" \
--prop categories=10,20,30,40,50,60 \
--prop series1="Measurement:25,42,58,72,88,105" \
--prop colors=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=circle --prop markerSize=7 \
--prop lineWidth=1 \
--prop errBars=fixed:5 \
--prop catTitle=Input --prop axisTitle=Output
# Chart 2: Percentage error bars (errBars=percent:10)
$CLI add "$FILE" "/4-Error Bars" --type chart \
--prop chartType=scatter \
--prop title="Percentage Error Bars (10%)" \
--prop categories=5,10,15,20,25,30 \
--prop series1="Yield:120,185,240,310,375,450" \
--prop colors=70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=diamond --prop markerSize=7 \
--prop lineWidth=1 \
--prop errBars=percent:10 \
--prop catTitle=Dosage --prop axisTitle=Yield
# Chart 3: Standard deviation error bars (errBars=stddev)
$CLI add "$FILE" "/4-Error Bars" --type chart \
--prop chartType=scatter \
--prop title="Standard Deviation Error Bars" \
--prop categories=0,1,2,3,4,5,6,7 \
--prop series1="Trial 1:48,52,47,55,50,53,49,51" \
--prop series2="Trial 2:30,35,28,40,32,38,34,36" \
--prop colors=ED7D31,9DC3E6 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=square --prop markerSize=6 \
--prop lineWidth=1 \
--prop errBars=stddev \
--prop legend=bottom
# Chart 4: Standard error with series styling
$CLI add "$FILE" "/4-Error Bars" --type chart \
--prop chartType=scatter \
--prop title="Standard Error + Styled Series" \
--prop categories=2,4,6,8,10,12,14 \
--prop series1="Experiment:18,32,28,45,40,55,52" \
--prop colors=843C0B \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=star --prop markerSize=8 \
--prop lineWidth=1.5 \
--prop errBars=stderr \
--prop series.shadow=000000-4-315-2-30 \
--prop gridlines=D9D9D9:0.5:dot \
--prop catTitle="Time (h)" --prop axisTitle=Response
# ==========================================================================
# Sheet: 5-Styling
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name="5-Styling"
# Chart 1: Title styling, series shadow, series outline
$CLI add "$FILE" "/5-Styling" --type chart \
--prop chartType=scatter \
--prop title="Styled Title + Series Effects" \
--prop categories=10,20,30,40,50 \
--prop series1="Alpha:15,35,28,48,55" \
--prop series2="Beta:8,22,32,40,50" \
--prop colors=4472C4,ED7D31 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=circle --prop markerSize=8 --prop lineWidth=2 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30 \
--prop series.shadow=000000-4-315-2-30 \
--prop series.outline=333333:1.5 \
--prop legend=bottom
# Chart 2: Gradients, transparency, plotFill, chartFill
$CLI add "$FILE" "/5-Styling" --type chart \
--prop chartType=scatter \
--prop title="Gradients + Fills" \
--prop categories=5,15,25,35,45 \
--prop series1="Group 1:12,28,35,42,55" \
--prop series2="Group 2:8,18,22,38,48" \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=diamond --prop markerSize=8 --prop lineWidth=1.5 \
--prop "gradients=4472C4-BDD7EE:90;ED7D31-FBE5D6:90" \
--prop transparency=20 \
--prop plotFill=F5F5F5 \
--prop chartFill=FAFAFA \
--prop legend=bottom
# Chart 3: Axis font, gridlines, minor gridlines, axis line
$CLI add "$FILE" "/5-Styling" --type chart \
--prop chartType=scatter \
--prop title="Axis & Grid Styling" \
--prop categories=0,10,20,30,40,50 \
--prop series1="Readings:5,22,38,52,68,82" \
--prop colors=2E75B6 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=circle --prop markerSize=7 --prop lineWidth=1.5 \
--prop axisfont=9:C00000:Arial \
--prop gridlines=BFBFBF:0.75:solid \
--prop minorGridlines=E0E0E0:0.25:dot \
--prop axisLine=333333:1 \
--prop catTitle="X Axis" --prop axisTitle="Y Axis"
# Chart 4: Chart area border, plot area border, rounded corners
$CLI add "$FILE" "/5-Styling" --type chart \
--prop chartType=scatter \
--prop title="Borders + Rounded Corners" \
--prop categories=1,3,5,7,9 \
--prop series1="Data:10,25,18,35,28" \
--prop colors=548235 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=square --prop markerSize=8 --prop lineWidth=1.5 \
--prop chartArea.border=333333:1.5 \
--prop plotArea.border=999999:0.75 \
--prop roundedCorners=true \
--prop chartFill=FFFFFF \
--prop plotFill=F0F0F0
# ==========================================================================
# Sheet: 6-Advanced
# ==========================================================================
$CLI add "$FILE" / --type sheet --prop name="6-Advanced"
# Chart 1: Secondary axis
$CLI add "$FILE" "/6-Advanced" --type chart \
--prop chartType=scatter \
--prop title="Secondary Y-Axis" \
--prop categories=10,20,30,40,50,60 \
--prop series1="Temperature (C):15,20,28,32,38,42" \
--prop series2="Humidity (%):85,78,65,58,45,38" \
--prop colors=C00000,4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=circle --prop markerSize=7 --prop lineWidth=1.5 \
--prop secondaryAxis=2 \
--prop legend=bottom \
--prop catTitle=Location
# Chart 2: Reference line (horizontal target)
$CLI add "$FILE" "/6-Advanced" --type chart \
--prop chartType=scatter \
--prop title="Reference Line (Target=75)" \
--prop categories=1,2,3,4,5,6,7,8 \
--prop series1="Score:60,68,72,78,80,74,82,88" \
--prop colors=70AD47 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop marker=diamond --prop markerSize=7 --prop lineWidth=1.5 \
--prop referenceLine=75:FF0000:Target:dash \
--prop catTitle=Week --prop axisTitle=Performance
# Chart 3: Axis min/max and log scale
$CLI add "$FILE" "/6-Advanced" --type chart \
--prop chartType=scatter \
--prop title="Log Scale (base 10)" \
--prop categories=1,10,100,1000,10000 \
--prop series1="Response:2,15,120,950,8500" \
--prop colors=1F4E79 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop marker=triangle --prop markerSize=8 --prop lineWidth=1.5 \
--prop logBase=10 \
--prop axisMin=1 --prop axisMax=10000 \
--prop catTitle=Concentration --prop axisTitle=Response
# Chart 4: Data labels and color rule
$CLI add "$FILE" "/6-Advanced" --type chart \
--prop chartType=scatter \
--prop scatterStyle=marker \
--prop title="Data Labels + Color Rule" \
--prop categories=1,2,3,4,5,6,7,8 \
--prop series1="KPI:45,62,38,78,55,82,48,90" \
--prop colors=4472C4 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop markerSize=9 \
--prop dataLabels=true --prop labelPos=top \
--prop colorRule=60:C00000:00AA00 \
--prop catTitle=Quarter --prop axisTitle="KPI Score"
# Remove blank default Sheet1 (all data is inline)
$CLI remove "$FILE" /Sheet1
$CLI close "$FILE"
$CLI validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+117
View File
@@ -0,0 +1,117 @@
# Stock Charts Showcase
This demo consists of three files that work together:
- **charts-stock.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-stock.xlsx** — The generated workbook with 4 sheets (1 default + 3 chart sheets, 12 charts total).
- **charts-stock.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-stock.py
# -> charts-stock.xlsx
```
## Chart Sheets
### Sheet: 1-Stock Fundamentals
Four OHLC stock charts covering basic rendering, gridlines, hi-low lines, and up-down bars.
```bash
# Basic OHLC stock chart with axis titles
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop series1="Open:142,145,148,150,147,152" \
--prop series2="High:148,151,155,156,153,158" \
--prop series3="Low:139,142,145,147,144,149" \
--prop series4="Close:145,148,150,147,152,155" \
--prop catTitle=Week --prop axisTitle=Price ($)
# Stock with gridlines and axis font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop gridlines=D9D9D9:0.5 --prop axisfont=9:666666
# Hi-low lines connecting high to low per category
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop hiLowLines=true
# Up-down bars showing open-to-close direction
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop updownbars=100:70AD47:C00000
```
**Features:** `stock`, 4-series OHLC, `catTitle`, `axisTitle`, `gridlines`, `axisfont`, `hiLowLines`, `updownbars=gapWidth:upColor:downColor`
### Sheet: 2-Stock Styling
Four styled stock charts with title fonts, axis lines, custom ranges, and chart fills.
```bash
# Title and legend styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop legend=right --prop legendfont=10:333333:Calibri
# Axis line styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop axisLine=333333-1.5 --prop catAxisLine=333333-1.5
# Custom axis range with major unit
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop axisMin=110 --prop axisMax=150 --prop majorUnit=10
# Chart area fills and rounded corners
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop plotFill=F0F4F8 --prop chartFill=FAFAFA \
--prop roundedCorners=true
```
**Features:** `title.font/size/color/bold`, `legend=right`, `legendfont`, `axisLine`, `catAxisLine`, `axisMin/Max`, `majorUnit`, `plotFill`, `chartFill`, `roundedCorners`
### Sheet: 3-Stock Advanced
Four advanced stock charts with data labels, reference lines, borders, and number formatting.
```bash
# Data labels on stock chart
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=8:666666:false
# Reference line as support/resistance level
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop referenceLine=115:Resistance:C00000
# Chart and plot area borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop chartArea.border=333333-1.5 \
--prop plotArea.border=999999-0.75
# Axis number format (dollar)
officecli add data.xlsx /Sheet --type chart \
--prop chartType=stock \
--prop axisNumFmt=$#,##0
```
**Features:** `dataLabels`, `labelPos`, `labelFont`, `referenceLine`, `chartArea.border`, `plotArea.border`, `axisNumFmt`
## Inspect the Generated File
```bash
officecli query charts-stock.xlsx chart
officecli get charts-stock.xlsx "/1-Stock Fundamentals/chart[1]"
```
+288
View File
@@ -0,0 +1,288 @@
#!/usr/bin/env python3
"""
Stock Charts Showcase — generates charts-stock.xlsx exercising the xlsx
`chart` element with chartType=stock: OHLC series (Open/High/Low/Close),
hi-low lines, up-down bars, axis/title/legend styling, data labels,
reference lines, borders, and number formats.
SDK twin of charts-stock.sh (officecli CLI). Both produce an equivalent
charts-stock.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every sheet and
chart is shipped over the named pipe in `doc.batch(...)` round-trips. Each
item is the same `{"command","parent","type","props"}` dict you'd put in an
`officecli batch` list.
3 chart sheets, 12 stock charts total:
1-Stock Fundamentals — basic OHLC, gridlines+axisfont, hiLowLines, updownbars
2-Stock Styling — title styling, axis lines, axis range, plot/chart fill
3-Stock Advanced — data labels, reference line, borders, number format
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-stock.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-stock.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(sheet, **props):
"""One `add chart` item in batch-shape, parented to a sheet."""
return {"command": "add", "parent": f"/{sheet}", "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet: 1-Stock Fundamentals
# ======================================================================
print("\n--- 1-Stock Fundamentals ---")
items = [add_sheet("1-Stock Fundamentals")]
# ------------------------------------------------------------------
# Chart 1: Basic OHLC stock chart
# Features: chartType=stock, 4 series (Open/High/Low/Close), catTitle, axisTitle
# ------------------------------------------------------------------
items.append(chart("1-Stock Fundamentals",
chartType="stock",
title="ACME Corp Weekly OHLC",
series1="Open:142,145,148,150,147,152",
series2="High:148,151,155,156,153,158",
series3="Low:139,142,145,147,144,149",
series4="Close:145,148,150,147,152,155",
categories="Week 1,Week 2,Week 3,Week 4,Week 5,Week 6",
x="0", y="0", width="12", height="18",
catTitle="Week", axisTitle="Price ($)",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 2: Stock with gridlines and axisfont
# Features: gridlines, axisfont on stock chart
# ------------------------------------------------------------------
items.append(chart("1-Stock Fundamentals",
chartType="stock",
title="Tech Sector Daily",
series1="Open:210,215,212,218,220",
series2="High:218,222,219,225,228",
series3="Low:207,211,208,214,216",
series4="Close:215,212,218,220,225",
categories="Mon,Tue,Wed,Thu,Fri",
x="13", y="0", width="12", height="18",
gridlines="D9D9D9:0.5",
axisfont="9:666666",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 3: Stock with hiLowLines
# Features: hiLowLines=true (vertical lines connecting high to low)
# ------------------------------------------------------------------
items.append(chart("1-Stock Fundamentals",
chartType="stock",
title="Energy Sector with Hi-Low Lines",
series1="Open:78,80,82,79,83,85",
series2="High:84,86,88,85,89,91",
series3="Low:75,77,79,76,80,82",
series4="Close:80,82,79,83,85,88",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="0", y="19", width="12", height="18",
hiLowLines="true",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 4: Stock with updownbars
# Features: updownbars=gapWidth:upColor:downColor
# ------------------------------------------------------------------
items.append(chart("1-Stock Fundamentals",
chartType="stock",
title="Pharma Index with Up-Down Bars",
series1="Open:55,58,56,60,62,59",
series2="High:61,63,62,66,68,65",
series3="Low:52,55,53,57,59,56",
series4="Close:58,56,60,62,59,63",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="19", width="12", height="18",
updownbars="100:70AD47:C00000",
legend="bottom"))
doc.batch(items)
# ======================================================================
# Sheet: 2-Stock Styling
# ======================================================================
print("--- 2-Stock Styling ---")
items = [add_sheet("2-Stock Styling")]
# ------------------------------------------------------------------
# Chart 1: Title styling, legend positioning
# Features: title.font/size/color/bold, legend=right, legendfont
# ------------------------------------------------------------------
items.append(chart("2-Stock Styling",
chartType="stock",
title="Styled Stock Chart",
series1="Open:165,170,168,172,175",
series2="High:175,178,176,180,183",
series3="Low:160,165,163,168,170",
series4="Close:170,168,172,175,180",
categories="Mon,Tue,Wed,Thu,Fri",
x="0", y="0", width="12", height="18",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"},
legend="right", legendfont="10:333333:Calibri"))
# ------------------------------------------------------------------
# Chart 2: Series effects, axisLine, catAxisLine
# Features: axisLine, catAxisLine on stock chart
# ------------------------------------------------------------------
items.append(chart("2-Stock Styling",
chartType="stock",
title="Axis Line Styling",
series1="Open:92,95,93,97,99",
series2="High:99,102,100,104,106",
series3="Low:88,91,89,93,95",
series4="Close:95,93,97,99,103",
categories="W1,W2,W3,W4,W5",
x="13", y="0", width="12", height="18",
hiLowLines="true",
axisLine="333333:1.5", catAxisLine="333333:1.5",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 3: axisMin/Max, majorUnit
# Features: axisMin/Max, majorUnit
# ------------------------------------------------------------------
items.append(chart("2-Stock Styling",
chartType="stock",
title="Custom Axis Range",
series1="Open:120,125,122,128,130",
series2="High:132,138,135,140,142",
series3="Low:115,120,118,124,126",
series4="Close:125,122,128,130,135",
categories="Day 1,Day 2,Day 3,Day 4,Day 5",
x="0", y="19", width="12", height="18",
axisMin="110", axisMax="150",
majorUnit="10",
updownbars="100:70AD47:C00000",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 4: plotFill, chartFill, roundedCorners
# Features: plotFill, chartFill, roundedCorners
# ------------------------------------------------------------------
items.append(chart("2-Stock Styling",
chartType="stock",
title="Styled Chart Area",
series1="Open:48,50,52,49,53",
series2="High:55,57,59,56,60",
series3="Low:44,46,48,45,49",
series4="Close:50,52,49,53,56",
categories="Mon,Tue,Wed,Thu,Fri",
x="13", y="19", width="12", height="18",
plotFill="F0F4F8", chartFill="FAFAFA",
roundedCorners="true",
hiLowLines="true",
legend="bottom"))
doc.batch(items)
# ======================================================================
# Sheet: 3-Stock Advanced
# ======================================================================
print("--- 3-Stock Advanced ---")
items = [add_sheet("3-Stock Advanced")]
# ------------------------------------------------------------------
# Chart 1: dataLabels, labelFont
# Features: dataLabels, labelPos, labelFont on stock
# ------------------------------------------------------------------
items.append(chart("3-Stock Advanced",
chartType="stock",
title="Stock with Data Labels",
series1="Open:185,190,188,192,195",
series2="High:195,198,196,200,203",
series3="Low:180,185,183,188,190",
series4="Close:190,188,192,195,200",
categories="W1,W2,W3,W4,W5",
x="0", y="0", width="12", height="18",
dataLabels="true", labelPos="top",
labelFont="8:666666:false",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 2: referenceLine (support/resistance)
# Features: referenceLine as support/resistance level
# ------------------------------------------------------------------
items.append(chart("3-Stock Advanced",
chartType="stock",
title="Support & Resistance",
series1="Open:105,108,106,110,112,109",
series2="High:112,115,113,117,119,116",
series3="Low:101,104,102,106,108,105",
series4="Close:108,106,110,112,109,113",
categories="Jan,Feb,Mar,Apr,May,Jun",
x="13", y="0", width="12", height="18",
referenceLine="115:C00000:Resistance",
hiLowLines="true",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 3: chartArea.border, plotArea.border
# Features: chartArea.border, plotArea.border
# ------------------------------------------------------------------
items.append(chart("3-Stock Advanced",
chartType="stock",
title="Bordered Stock Chart",
series1="Open:72,75,73,77,79",
series2="High:79,82,80,84,86",
series3="Low:68,71,69,73,75",
series4="Close:75,73,77,79,83",
categories="Mon,Tue,Wed,Thu,Fri",
x="0", y="19", width="12", height="18",
**{"chartArea.border": "333333:1.5",
"plotArea.border": "999999:0.75"},
updownbars="100:70AD47:C00000",
legend="bottom"))
# ------------------------------------------------------------------
# Chart 4: dispUnits, axisNumFmt
# Features: axisNumFmt (dollar format)
# ------------------------------------------------------------------
items.append(chart("3-Stock Advanced",
chartType="stock",
title="Large Cap Stock",
series1="Open:2850,2900,2880,2920,2950",
series2="High:2950,2980,2960,3000,3020",
series3="Low:2800,2850,2830,2870,2900",
series4="Close:2900,2880,2920,2950,2990",
categories="Q1,Q2,Q3,Q4,Q5",
x="13", y="19", width="12", height="18",
axisNumFmt="$#,##0",
hiLowLines="true",
legend="bottom"))
doc.batch(items)
# Remove blank default Sheet1 (all data is inline)
doc.send({"command": "remove", "path": "/Sheet1"})
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nDone! Generated: {FILE}")
print(" 3 chart sheets, 12 stock charts total")
+219
View File
@@ -0,0 +1,219 @@
#!/bin/bash
# Stock Charts Showcase — generates charts-stock.xlsx exercising the xlsx
# `chart` element with chartType=stock: OHLC series, hi-low lines, up-down
# bars, axis/title/legend styling, data labels, reference lines, borders.
#
# CLI twin of charts-stock.py (officecli Python SDK). Both produce an
# equivalent charts-stock.xlsx.
#
# Usage: ./charts-stock.sh
# 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")/charts-stock.xlsx"
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet: 1-Stock Fundamentals
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="1-Stock Fundamentals"
# Chart 1: Basic OHLC stock chart
# Features: chartType=stock, 4 series (Open/High/Low/Close), catTitle, axisTitle
officecli add "$FILE" "/1-Stock Fundamentals" --type chart \
--prop chartType=stock \
--prop title="ACME Corp Weekly OHLC" \
--prop series1=Open:142,145,148,150,147,152 \
--prop series2=High:148,151,155,156,153,158 \
--prop series3=Low:139,142,145,147,144,149 \
--prop series4=Close:145,148,150,147,152,155 \
--prop "categories=Week 1,Week 2,Week 3,Week 4,Week 5,Week 6" \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop catTitle=Week --prop "axisTitle=Price (\$)" \
--prop legend=bottom
# Chart 2: Stock with gridlines and axisfont
# Features: gridlines, axisfont on stock chart
officecli add "$FILE" "/1-Stock Fundamentals" --type chart \
--prop chartType=stock \
--prop title="Tech Sector Daily" \
--prop series1=Open:210,215,212,218,220 \
--prop series2=High:218,222,219,225,228 \
--prop series3=Low:207,211,208,214,216 \
--prop series4=Close:215,212,218,220,225 \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop gridlines=D9D9D9:0.5 \
--prop axisfont=9:666666 \
--prop legend=bottom
# Chart 3: Stock with hiLowLines
# Features: hiLowLines=true (vertical lines connecting high to low)
officecli add "$FILE" "/1-Stock Fundamentals" --type chart \
--prop chartType=stock \
--prop title="Energy Sector with Hi-Low Lines" \
--prop series1=Open:78,80,82,79,83,85 \
--prop series2=High:84,86,88,85,89,91 \
--prop series3=Low:75,77,79,76,80,82 \
--prop series4=Close:80,82,79,83,85,88 \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop hiLowLines=true \
--prop legend=bottom
# Chart 4: Stock with updownbars
# Features: updownbars=gapWidth:upColor:downColor
officecli add "$FILE" "/1-Stock Fundamentals" --type chart \
--prop chartType=stock \
--prop title="Pharma Index with Up-Down Bars" \
--prop series1=Open:55,58,56,60,62,59 \
--prop series2=High:61,63,62,66,68,65 \
--prop series3=Low:52,55,53,57,59,56 \
--prop series4=Close:58,56,60,62,59,63 \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop updownbars=100:70AD47:C00000 \
--prop legend=bottom
# ==========================================================================
# Sheet: 2-Stock Styling
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="2-Stock Styling"
# Chart 1: Title styling, legend positioning
# Features: title.font/size/color/bold, legend=right, legendfont
officecli add "$FILE" "/2-Stock Styling" --type chart \
--prop chartType=stock \
--prop title="Styled Stock Chart" \
--prop series1=Open:165,170,168,172,175 \
--prop series2=High:175,178,176,180,183 \
--prop series3=Low:160,165,163,168,170 \
--prop series4=Close:170,168,172,175,180 \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true \
--prop legend=right --prop legendfont=10:333333:Calibri
# Chart 2: Series effects, axisLine, catAxisLine
# Features: axisLine, catAxisLine on stock chart
officecli add "$FILE" "/2-Stock Styling" --type chart \
--prop chartType=stock \
--prop title="Axis Line Styling" \
--prop series1=Open:92,95,93,97,99 \
--prop series2=High:99,102,100,104,106 \
--prop series3=Low:88,91,89,93,95 \
--prop series4=Close:95,93,97,99,103 \
--prop categories=W1,W2,W3,W4,W5 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop hiLowLines=true \
--prop axisLine=333333:1.5 --prop catAxisLine=333333:1.5 \
--prop legend=bottom
# Chart 3: axisMin/Max, majorUnit
# Features: axisMin/Max, majorUnit
officecli add "$FILE" "/2-Stock Styling" --type chart \
--prop chartType=stock \
--prop title="Custom Axis Range" \
--prop series1=Open:120,125,122,128,130 \
--prop series2=High:132,138,135,140,142 \
--prop series3=Low:115,120,118,124,126 \
--prop series4=Close:125,122,128,130,135 \
--prop "categories=Day 1,Day 2,Day 3,Day 4,Day 5" \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop axisMin=110 --prop axisMax=150 \
--prop majorUnit=10 \
--prop updownbars=100:70AD47:C00000 \
--prop legend=bottom
# Chart 4: plotFill, chartFill, roundedCorners
# Features: plotFill, chartFill, roundedCorners
officecli add "$FILE" "/2-Stock Styling" --type chart \
--prop chartType=stock \
--prop title="Styled Chart Area" \
--prop series1=Open:48,50,52,49,53 \
--prop series2=High:55,57,59,56,60 \
--prop series3=Low:44,46,48,45,49 \
--prop series4=Close:50,52,49,53,56 \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop plotFill=F0F4F8 --prop chartFill=FAFAFA \
--prop roundedCorners=true \
--prop hiLowLines=true \
--prop legend=bottom
# ==========================================================================
# Sheet: 3-Stock Advanced
# ==========================================================================
officecli add "$FILE" / --type sheet --prop name="3-Stock Advanced"
# Chart 1: dataLabels, labelFont
# Features: dataLabels, labelPos, labelFont on stock
officecli add "$FILE" "/3-Stock Advanced" --type chart \
--prop chartType=stock \
--prop title="Stock with Data Labels" \
--prop series1=Open:185,190,188,192,195 \
--prop series2=High:195,198,196,200,203 \
--prop series3=Low:180,185,183,188,190 \
--prop series4=Close:190,188,192,195,200 \
--prop categories=W1,W2,W3,W4,W5 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true --prop labelPos=top \
--prop labelFont=8:666666:false \
--prop legend=bottom
# Chart 2: referenceLine (support/resistance)
# Features: referenceLine as support/resistance level
officecli add "$FILE" "/3-Stock Advanced" --type chart \
--prop chartType=stock \
--prop title="Support & Resistance" \
--prop series1=Open:105,108,106,110,112,109 \
--prop series2=High:112,115,113,117,119,116 \
--prop series3=Low:101,104,102,106,108,105 \
--prop series4=Close:108,106,110,112,109,113 \
--prop categories=Jan,Feb,Mar,Apr,May,Jun \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop referenceLine=115:C00000:Resistance \
--prop hiLowLines=true \
--prop legend=bottom
# Chart 3: chartArea.border, plotArea.border
# Features: chartArea.border, plotArea.border
officecli add "$FILE" "/3-Stock Advanced" --type chart \
--prop chartType=stock \
--prop title="Bordered Stock Chart" \
--prop series1=Open:72,75,73,77,79 \
--prop series2=High:79,82,80,84,86 \
--prop series3=Low:68,71,69,73,75 \
--prop series4=Close:75,73,77,79,83 \
--prop categories=Mon,Tue,Wed,Thu,Fri \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop chartArea.border=333333:1.5 \
--prop plotArea.border=999999:0.75 \
--prop updownbars=100:70AD47:C00000 \
--prop legend=bottom
# Chart 4: dispUnits, axisNumFmt
# Features: axisNumFmt (dollar format)
officecli add "$FILE" "/3-Stock Advanced" --type chart \
--prop chartType=stock \
--prop title="Large Cap Stock" \
--prop series1=Open:2850,2900,2880,2920,2950 \
--prop series2=High:2950,2980,2960,3000,3020 \
--prop series3=Low:2800,2850,2830,2870,2900 \
--prop series4=Close:2900,2880,2920,2950,2990 \
--prop categories=Q1,Q2,Q3,Q4,Q5 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop "axisNumFmt=\$#,##0" \
--prop hiLowLines=true \
--prop legend=bottom
# Remove blank default Sheet1 (all data is inline)
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+189
View File
@@ -0,0 +1,189 @@
# Waterfall Charts Showcase
This demo consists of three files that work together:
- **charts-waterfall.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
- **charts-waterfall.xlsx** — The generated workbook with 5 sheets (1 default + 4 chart sheets, 16 charts total).
- **charts-waterfall.md** — This file. Maps each sheet to the features it demonstrates.
## Regenerate
```bash
cd examples/excel
python3 charts-waterfall.py
# → charts-waterfall.xlsx
```
## Chart Sheets
### Sheet: 1-Waterfall Fundamentals
Four waterfall chart variants covering basic P&L, budget analysis, quarterly flow, and title styling.
```bash
# Basic P&L waterfall with increase/decrease/total colors
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop data="Start:1000,Revenue:500,Costs:-300,Tax:-100,Net:1100" \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop dataLabels=true
# Budget waterfall with blue/red/amber theme
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop data="Budget:5000,Sales:2000,Marketing:-800,Ops:-600,Net:5600" \
--prop increaseColor=2E75B6 \
--prop decreaseColor=C00000 \
--prop totalColor=FFC000 \
--prop legend=bottom
# Quarterly cash flow with 10 data points
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop data="Opening:3000,Q1 Sales:1200,Q1 Costs:-500,...,Closing:6000" \
--prop increaseColor=70AD47 --prop decreaseColor=ED7D31 --prop totalColor=4472C4
# Waterfall with styled title
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true
```
**Features:** `chartType=waterfall`, `data=` name:value pairs (positive=increase, negative=decrease), `increaseColor`, `decreaseColor`, `totalColor`, `dataLabels`, `legend=bottom`, `title.font/size/color/bold`
### Sheet: 2-Waterfall Styling
Four waterfall charts demonstrating visual styling options.
```bash
# Title with font, size, color, bold, and shadow
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop title.font=Trebuchet MS --prop title.size=18 \
--prop title.color=833C0B --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30
# Series shadow, plot/chart fills, rounded corners
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop series.shadow=000000-4-315-2-30 \
--prop plotFill=F0F0F0 --prop chartFill=FAFAFA \
--prop roundedCorners=true
# Gridline color and axis font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop gridlineColor=CCCCCC \
--prop axisfont=10:333333:Calibri
# Chart area and plot area borders
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop chartArea.border=4472C4-2 \
--prop plotArea.border=A5A5A5-1
```
**Features:** `title.shadow`, `series.shadow`, `plotFill`, `chartFill`, `roundedCorners`, `gridlineColor`, `axisfont`, `chartArea.border`, `plotArea.border`
### Sheet: 3-Waterfall Labels & Axis
Four waterfall charts demonstrating data labels, axis configuration, and layout control.
```bash
# Data labels with font and number format
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop dataLabels=true \
--prop labelFont=10:333333:true \
--prop dataLabels.numFmt=#,##0
# Custom axis range and tick interval
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop axisMin=0 --prop axisMax=3500 --prop majorUnit=500
# Legend position and font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop legend=right \
--prop legendfont=10:1F4E79:Helvetica
# Manual plot area layout
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop plotArea.x=0.15 --prop plotArea.y=0.15 \
--prop plotArea.w=0.75 --prop plotArea.h=0.70
```
**Features:** `dataLabels`, `labelFont`, `dataLabels.numFmt`, `axisMin`, `axisMax`, `majorUnit`, `legend=right`, `legendfont`, `plotArea.x/y/w/h`
### Sheet: 4-Waterfall Advanced
Four waterfall charts demonstrating advanced features and large datasets.
```bash
# Reference line overlay
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop referenceLine=2000:Target-FF0000-dash-2
# Value axis and category axis line styling
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop axisLine=333333-2 \
--prop catAxisLine=333333-2
# Title glow and shadow effects
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop title.glow=4472C4-8 \
--prop title.shadow=000000-3-315-2-30
# Large dataset (12 categories) with small axis font
officecli add data.xlsx /Sheet --type chart \
--prop chartType=waterfall \
--prop data="Revenue:8500,COGS:-3400,...,Net Income:1050" \
--prop dataLabels=true \
--prop axisfont=8:333333:Calibri
```
**Features:** `referenceLine`, `axisLine`, `catAxisLine`, `title.glow`, `title.shadow`, large dataset (12 categories)
## Property Coverage
| Property | Sheet |
|---|---|
| `chartType=waterfall` | 1, 2, 3, 4 |
| `data=` (name:value pairs) | 1, 2, 3, 4 |
| `increaseColor` | 1, 2, 3, 4 |
| `decreaseColor` | 1, 2, 3, 4 |
| `totalColor` | 1, 2, 3, 4 |
| `dataLabels` | 1, 3, 4 |
| `legend` | 1, 3 |
| `title.font/size/color/bold` | 1, 2 |
| `title.shadow` | 2, 4 |
| `title.glow` | 4 |
| `series.shadow` | 2 |
| `plotFill`, `chartFill` | 2 |
| `roundedCorners` | 2 |
| `gridlineColor` | 2 |
| `axisfont` | 2, 4 |
| `chartArea.border` | 2 |
| `plotArea.border` | 2 |
| `labelFont` | 3 |
| `dataLabels.numFmt` | 3 |
| `axisMin/Max`, `majorUnit` | 3 |
| `legendfont` | 3 |
| `plotArea.x/y/w/h` | 3 |
| `referenceLine` | 4 |
| `axisLine`, `catAxisLine` | 4 |
## Inspect the Generated File
```bash
officecli query charts-waterfall.xlsx chart
officecli get charts-waterfall.xlsx "/1-Waterfall Fundamentals/chart[1]"
```
+305
View File
@@ -0,0 +1,305 @@
#!/usr/bin/env python3
"""
Waterfall Charts Showcase — waterfall chart type with all variations.
Generates: charts-waterfall.xlsx
4 sheets, 16 charts total:
1-Waterfall Fundamentals 4 charts — basic P&L, budget bridge, quarterly cash flow, title styling
2-Waterfall Styling 4 charts — title shadow, series shadow/fill, gridlines/axis font, borders
3-Waterfall Labels & Axis 4 charts — label numFmt, axis range, legend styling, manual plot layout
4-Waterfall Advanced 4 charts — reference line, axis line styling, glow/shadow, large dataset
SDK twin of charts-waterfall.sh (officecli CLI). Both produce an equivalent
charts-waterfall.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every chart is
shipped over the named pipe via `doc.batch(...)`. Each item is the same
`{"command","parent","type","props"}` dict you'd put in an `officecli batch`
list. The batch defaults to stop_on_error=False, so a not-yet-consumed
("unsupported_property") prop warns but still creates the element — matching
the CLI twin's forward-compat tolerance.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 charts-waterfall.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "charts-waterfall.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def chart(sheet, **props):
"""One `add chart` item in batch-shape (props become --prop k=v)."""
return {"command": "add", "parent": f"/{sheet}", "type": "chart", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet: 1-Waterfall Fundamentals
# ======================================================================
print("--- 1-Waterfall Fundamentals ---")
items = [add_sheet("1-Waterfall Fundamentals")]
# Chart 1: Basic P&L waterfall with increase/decrease/total colors
# Features: chartType=waterfall, data= name:value pairs, increaseColor,
# decreaseColor, totalColor, dataLabels
items.append(chart("1-Waterfall Fundamentals",
chartType="waterfall",
title="P&L Summary",
data="Start:1000,Revenue:500,Costs:-300,Tax:-100,Net:1100",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
x="0", y="0", width="12", height="18",
dataLabels="true"))
# Chart 2: Budget waterfall with blue/red/amber theme and legend
# Features: waterfall legend=bottom, alternative color palette (blue/red/amber)
items.append(chart("1-Waterfall Fundamentals",
chartType="waterfall",
title="Budget vs Actual",
data="Budget:5000,Sales:2000,Marketing:-800,Ops:-600,Net:5600",
increaseColor="2E75B6",
decreaseColor="C00000",
totalColor="FFC000",
x="13", y="0", width="12", height="18",
legend="bottom"))
# Chart 3: Quarterly cash flow bridge with more data points
# Features: waterfall with 10 categories (extended data points),
# quarterly granularity
items.append(chart("1-Waterfall Fundamentals",
chartType="waterfall",
title="Quarterly Cash Flow",
data="Opening:3000,Q1 Sales:1200,Q1 Costs:-500,Q2 Sales:1500,Q2 Costs:-700,Q3 Sales:800,Q3 Costs:-400,Q4 Sales:2000,Q4 Costs:-900,Closing:6000",
increaseColor="70AD47",
decreaseColor="ED7D31",
totalColor="4472C4",
x="0", y="19", width="12", height="18",
dataLabels="true"))
# Chart 4: Waterfall with custom title styling
# Features: title.font, title.size, title.color, title.bold
items.append(chart("1-Waterfall Fundamentals",
chartType="waterfall",
title="Revenue Bridge",
data="Base:2500,New Clients:800,Upsell:400,Churn:-600,Total:3100",
increaseColor="548235",
decreaseColor="BF0000",
totalColor="2F5496",
x="13", y="19", width="12", height="18",
**{"title.font": "Georgia", "title.size": "16",
"title.color": "1F4E79", "title.bold": "true"}))
doc.batch(items)
# ======================================================================
# Sheet: 2-Waterfall Styling
# ======================================================================
print("--- 2-Waterfall Styling ---")
items = [add_sheet("2-Waterfall Styling")]
# Chart 1: Title styling with font, size, color, bold, and shadow
# Features: title.font, title.size, title.color, title.bold, title.shadow
items.append(chart("2-Waterfall Styling",
chartType="waterfall",
title="Styled Title Demo",
data="Start:800,Income:300,Expenses:-200,Net:900",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
x="0", y="0", width="12", height="18",
**{"title.font": "Trebuchet MS", "title.size": "18",
"title.color": "833C0B", "title.bold": "true",
"title.shadow": "000000-3-315-2-30"}))
# Chart 2: Series shadow, plotFill, chartFill, roundedCorners
# Features: series.shadow, plotFill, chartFill, roundedCorners
items.append(chart("2-Waterfall Styling",
chartType="waterfall",
title="Shadow & Fill Effects",
data="Baseline:1500,Growth:600,Decline:-400,Result:1700",
increaseColor="2E75B6",
decreaseColor="C00000",
totalColor="FFC000",
x="13", y="0", width="12", height="18",
plotFill="F0F0F0",
chartFill="FAFAFA",
roundedCorners="true",
**{"series.shadow": "000000-4-315-2-30"}))
# Chart 3: Gridlines styling and axis font
# Features: gridlineColor, axisfont (size:color:font)
items.append(chart("2-Waterfall Styling",
chartType="waterfall",
title="Gridlines & Axis Font",
data="Open:2000,Add:750,Remove:-350,Close:2400",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
x="0", y="19", width="12", height="18",
gridlineColor="CCCCCC",
axisfont="10:333333:Calibri"))
# Chart 4: Chart area border and plot area border
# Features: chartArea.border (color-width), plotArea.border
items.append(chart("2-Waterfall Styling",
chartType="waterfall",
title="Border Styling",
data="Initial:1200,Gain:500,Loss:-300,Final:1400",
increaseColor="548235",
decreaseColor="BF0000",
totalColor="2F5496",
x="13", y="19", width="12", height="18",
**{"chartArea.border": "4472C4:2",
"plotArea.border": "A5A5A5:1"}))
doc.batch(items)
# ======================================================================
# Sheet: 3-Waterfall Labels & Axis
# ======================================================================
print("--- 3-Waterfall Labels & Axis ---")
items = [add_sheet("3-Waterfall Labels & Axis")]
# Chart 1: Data labels with labelFont and numFmt
# Features: dataLabels, labelFont (size:color:bold), dataLabels.numFmt
items.append(chart("3-Waterfall Labels & Axis",
chartType="waterfall",
title="Labels with NumFmt",
data="Start:4500,Revenue:1800,COGS:-1200,SGA:-600,Net:4500",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
x="0", y="0", width="12", height="18",
dataLabels="true",
labelFont="10:333333:true",
**{"dataLabels.numFmt": "#,##0"}))
# Chart 2: Axis min/max and majorUnit
# Features: axisMin, axisMax, majorUnit
items.append(chart("3-Waterfall Labels & Axis",
chartType="waterfall",
title="Custom Axis Range",
data="Base:2000,Up:800,Down:-500,Total:2300",
increaseColor="2E75B6",
decreaseColor="C00000",
totalColor="FFC000",
x="13", y="0", width="12", height="18",
axisMin="0", axisMax="3500", majorUnit="500"))
# Chart 3: Legend positioning and legendfont
# Features: legend=right, legendfont (size:color:font)
items.append(chart("3-Waterfall Labels & Axis",
chartType="waterfall",
title="Legend Styling",
data="Begin:3000,Earned:1100,Spent:-700,End:3400",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
x="0", y="19", width="12", height="18",
legend="right",
legendfont="10:1F4E79:Helvetica"))
# Chart 4: Manual layout with plotArea.x/y/w/h
# Features: plotArea.x/y/w/h (manual layout, fractional coordinates)
items.append(chart("3-Waterfall Labels & Axis",
chartType="waterfall",
title="Manual Plot Layout",
data="Start:1800,Add:600,Sub:-400,End:2000",
increaseColor="548235",
decreaseColor="BF0000",
totalColor="2F5496",
x="13", y="19", width="12", height="18",
**{"plotArea.x": "0.15", "plotArea.y": "0.15",
"plotArea.w": "0.75", "plotArea.h": "0.70"}))
doc.batch(items)
# ======================================================================
# Sheet: 4-Waterfall Advanced
# ======================================================================
print("--- 4-Waterfall Advanced ---")
items = [add_sheet("4-Waterfall Advanced")]
# Chart 1: Waterfall with referenceLine
# Features: referenceLine (value:label-color-dash-width)
items.append(chart("4-Waterfall Advanced",
chartType="waterfall",
title="Reference Line",
data="Start:2000,Revenue:900,Refunds:-300,Fees:-200,Net:2400",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
x="0", y="0", width="12", height="18",
referenceLine="2000:FF0000:Target:dash"))
# Chart 2: Axis line and category axis line styling
# Features: axisLine (color-width), catAxisLine
items.append(chart("4-Waterfall Advanced",
chartType="waterfall",
title="Axis Line Styling",
data="Open:1500,Deposit:700,Withdraw:-400,Close:1800",
increaseColor="2E75B6",
decreaseColor="C00000",
totalColor="FFC000",
x="13", y="0", width="12", height="18",
axisLine="333333:2",
catAxisLine="333333:2"))
# Chart 3: Title glow and shadow effects
# Features: title.glow (color-radius), title.shadow
items.append(chart("4-Waterfall Advanced",
chartType="waterfall",
title="Glow & Shadow Effects",
data="Base:3000,Inflow:1200,Outflow:-800,Balance:3400",
increaseColor="70AD47",
decreaseColor="FF0000",
totalColor="4472C4",
x="0", y="19", width="12", height="18",
**{"title.glow": "4472C4-8",
"title.shadow": "000000-3-315-2-30",
"title.size": "16", "title.bold": "true"}))
# Chart 4: Large dataset waterfall (8+ categories)
# Features: large dataset (12 categories), axisfont with smaller size
# for readability
items.append(chart("4-Waterfall Advanced",
chartType="waterfall",
title="Annual P&L Detail",
data="Revenue:8500,COGS:-3400,Gross Profit:5100,R&D:-1200,Sales:-900,Marketing:-600,G&A:-500,EBITDA:1900,Depreciation:-300,Interest:-200,Tax:-350,Net Income:1050",
increaseColor="548235",
decreaseColor="C00000",
totalColor="2F5496",
x="13", y="19", width="12", height="18",
dataLabels="true",
axisfont="8:333333:Calibri"))
doc.batch(items)
# Remove blank default Sheet1 (all data is inline)
doc.send({"command": "remove", "path": "/Sheet1"})
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"Generated: {FILE}")
print(" 4 sheets (16 charts total)")
print(" Sheet 1: Waterfall Fundamentals (4 charts)")
print(" Sheet 2: Waterfall Styling (4 charts)")
print(" Sheet 3: Waterfall Labels & Axis (4 charts)")
print(" Sheet 4: Waterfall Advanced (4 charts)")
+268
View File
@@ -0,0 +1,268 @@
#!/bin/bash
# Waterfall Charts Showcase — generates charts-waterfall.xlsx exercising the
# xlsx waterfall chart type with all variations.
#
# CLI twin of charts-waterfall.py (officecli Python SDK). Both produce an
# equivalent charts-waterfall.xlsx.
#
# 4 sheets, 16 charts total.
#
# Usage: ./charts-waterfall.sh
# 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")/charts-waterfall.xlsx"
rm -f "$FILE"
# Forward-compat: a few props aren't consumed by the chart handler yet —
# officecli warns and exits 2 (unsupported_property) but still creates the
# element. Tolerate that exit code so the showcase runs end-to-end, matching the
# SDK twin (whose batch doesn't abort on it). Any OTHER non-zero exit is a real
# error and aborts.
officecli() {
command officecli "$@" || { rc=$?; [ "$rc" -eq 2 ] && return 0; return $rc; }
}
officecli create "$FILE"
officecli open "$FILE"
# ==========================================================================
# Sheet: 1-Waterfall Fundamentals
# ==========================================================================
echo "--- 1-Waterfall Fundamentals ---"
officecli add "$FILE" / --type sheet --prop name="1-Waterfall Fundamentals"
# Chart 1: Basic P&L waterfall with increase/decrease/total colors
# Features: chartType=waterfall, data= name:value pairs, increaseColor,
# decreaseColor, totalColor, dataLabels
officecli add "$FILE" "/1-Waterfall Fundamentals" --type chart \
--prop chartType=waterfall \
--prop title="P&L Summary" \
--prop data=Start:1000,Revenue:500,Costs:-300,Tax:-100,Net:1100 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true
# Chart 2: Budget waterfall with blue/red/amber theme and legend
# Features: waterfall legend=bottom, alternative color palette (blue/red/amber)
officecli add "$FILE" "/1-Waterfall Fundamentals" --type chart \
--prop chartType=waterfall \
--prop title="Budget vs Actual" \
--prop data=Budget:5000,Sales:2000,Marketing:-800,Ops:-600,Net:5600 \
--prop increaseColor=2E75B6 \
--prop decreaseColor=C00000 \
--prop totalColor=FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop legend=bottom
# Chart 3: Quarterly cash flow bridge with more data points
# Features: waterfall with 10 categories (extended data points),
# quarterly granularity
officecli add "$FILE" "/1-Waterfall Fundamentals" --type chart \
--prop chartType=waterfall \
--prop title="Quarterly Cash Flow" \
--prop "data=Opening:3000,Q1 Sales:1200,Q1 Costs:-500,Q2 Sales:1500,Q2 Costs:-700,Q3 Sales:800,Q3 Costs:-400,Q4 Sales:2000,Q4 Costs:-900,Closing:6000" \
--prop increaseColor=70AD47 \
--prop decreaseColor=ED7D31 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop dataLabels=true
# Chart 4: Waterfall with custom title styling
# Features: title.font, title.size, title.color, title.bold
officecli add "$FILE" "/1-Waterfall Fundamentals" --type chart \
--prop chartType=waterfall \
--prop title="Revenue Bridge" \
--prop "data=Base:2500,New Clients:800,Upsell:400,Churn:-600,Total:3100" \
--prop increaseColor=548235 \
--prop decreaseColor=BF0000 \
--prop totalColor=2F5496 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop title.font=Georgia --prop title.size=16 \
--prop title.color=1F4E79 --prop title.bold=true
# ==========================================================================
# Sheet: 2-Waterfall Styling
# ==========================================================================
echo "--- 2-Waterfall Styling ---"
officecli add "$FILE" / --type sheet --prop name="2-Waterfall Styling"
# Chart 1: Title styling with font, size, color, bold, and shadow
# Features: title.font, title.size, title.color, title.bold, title.shadow
officecli add "$FILE" "/2-Waterfall Styling" --type chart \
--prop chartType=waterfall \
--prop title="Styled Title Demo" \
--prop data=Start:800,Income:300,Expenses:-200,Net:900 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop "title.font=Trebuchet MS" --prop title.size=18 \
--prop title.color=833C0B --prop title.bold=true \
--prop title.shadow=000000-3-315-2-30
# Chart 2: Series shadow, plotFill, chartFill, roundedCorners
# Features: series.shadow, plotFill, chartFill, roundedCorners
officecli add "$FILE" "/2-Waterfall Styling" --type chart \
--prop chartType=waterfall \
--prop title="Shadow & Fill Effects" \
--prop data=Baseline:1500,Growth:600,Decline:-400,Result:1700 \
--prop increaseColor=2E75B6 \
--prop decreaseColor=C00000 \
--prop totalColor=FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop series.shadow=000000-4-315-2-30 \
--prop plotFill=F0F0F0 \
--prop chartFill=FAFAFA \
--prop roundedCorners=true
# Chart 3: Gridlines styling and axis font
# Features: gridlineColor, axisfont (size:color:font)
officecli add "$FILE" "/2-Waterfall Styling" --type chart \
--prop chartType=waterfall \
--prop title="Gridlines & Axis Font" \
--prop data=Open:2000,Add:750,Remove:-350,Close:2400 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop gridlineColor=CCCCCC \
--prop axisfont=10:333333:Calibri
# Chart 4: Chart area border and plot area border
# Features: chartArea.border (color-width), plotArea.border
officecli add "$FILE" "/2-Waterfall Styling" --type chart \
--prop chartType=waterfall \
--prop title="Border Styling" \
--prop data=Initial:1200,Gain:500,Loss:-300,Final:1400 \
--prop increaseColor=548235 \
--prop decreaseColor=BF0000 \
--prop totalColor=2F5496 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop chartArea.border=4472C4:2 \
--prop plotArea.border=A5A5A5:1
# ==========================================================================
# Sheet: 3-Waterfall Labels & Axis
# ==========================================================================
echo "--- 3-Waterfall Labels & Axis ---"
officecli add "$FILE" / --type sheet --prop name="3-Waterfall Labels & Axis"
# Chart 1: Data labels with labelFont and numFmt
# Features: dataLabels, labelFont (size:color:bold), dataLabels.numFmt
officecli add "$FILE" "/3-Waterfall Labels & Axis" --type chart \
--prop chartType=waterfall \
--prop title="Labels with NumFmt" \
--prop data=Start:4500,Revenue:1800,COGS:-1200,SGA:-600,Net:4500 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop dataLabels=true \
--prop labelFont=10:333333:true \
--prop dataLabels.numFmt=#,##0
# Chart 2: Axis min/max and majorUnit
# Features: axisMin, axisMax, majorUnit
officecli add "$FILE" "/3-Waterfall Labels & Axis" --type chart \
--prop chartType=waterfall \
--prop title="Custom Axis Range" \
--prop data=Base:2000,Up:800,Down:-500,Total:2300 \
--prop increaseColor=2E75B6 \
--prop decreaseColor=C00000 \
--prop totalColor=FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop axisMin=0 --prop axisMax=3500 --prop majorUnit=500
# Chart 3: Legend positioning and legendfont
# Features: legend=right, legendfont (size:color:font)
officecli add "$FILE" "/3-Waterfall Labels & Axis" --type chart \
--prop chartType=waterfall \
--prop title="Legend Styling" \
--prop data=Begin:3000,Earned:1100,Spent:-700,End:3400 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop legend=right \
--prop legendfont=10:1F4E79:Helvetica
# Chart 4: Manual layout with plotArea.x/y/w/h
# Features: plotArea.x/y/w/h (manual layout, fractional coordinates)
officecli add "$FILE" "/3-Waterfall Labels & Axis" --type chart \
--prop chartType=waterfall \
--prop title="Manual Plot Layout" \
--prop data=Start:1800,Add:600,Sub:-400,End:2000 \
--prop increaseColor=548235 \
--prop decreaseColor=BF0000 \
--prop totalColor=2F5496 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop plotArea.x=0.15 --prop plotArea.y=0.15 \
--prop plotArea.w=0.75 --prop plotArea.h=0.70
# ==========================================================================
# Sheet: 4-Waterfall Advanced
# ==========================================================================
echo "--- 4-Waterfall Advanced ---"
officecli add "$FILE" / --type sheet --prop name="4-Waterfall Advanced"
# Chart 1: Waterfall with referenceLine
# Features: referenceLine (value:label-color-dash-width)
officecli add "$FILE" "/4-Waterfall Advanced" --type chart \
--prop chartType=waterfall \
--prop title="Reference Line" \
--prop data=Start:2000,Revenue:900,Refunds:-300,Fees:-200,Net:2400 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=0 --prop width=12 --prop height=18 \
--prop referenceLine=2000:FF0000:Target:dash
# Chart 2: Axis line and category axis line styling
# Features: axisLine (color-width), catAxisLine
officecli add "$FILE" "/4-Waterfall Advanced" --type chart \
--prop chartType=waterfall \
--prop title="Axis Line Styling" \
--prop data=Open:1500,Deposit:700,Withdraw:-400,Close:1800 \
--prop increaseColor=2E75B6 \
--prop decreaseColor=C00000 \
--prop totalColor=FFC000 \
--prop x=13 --prop y=0 --prop width=12 --prop height=18 \
--prop axisLine=333333:2 \
--prop catAxisLine=333333:2
# Chart 3: Title glow and shadow effects
# Features: title.glow (color-radius), title.shadow
officecli add "$FILE" "/4-Waterfall Advanced" --type chart \
--prop chartType=waterfall \
--prop title="Glow & Shadow Effects" \
--prop data=Base:3000,Inflow:1200,Outflow:-800,Balance:3400 \
--prop increaseColor=70AD47 \
--prop decreaseColor=FF0000 \
--prop totalColor=4472C4 \
--prop x=0 --prop y=19 --prop width=12 --prop height=18 \
--prop title.glow=4472C4-8 \
--prop title.shadow=000000-3-315-2-30 \
--prop title.size=16 --prop title.bold=true
# Chart 4: Large dataset waterfall (8+ categories)
# Features: large dataset (12 categories), axisfont with smaller size for readability
officecli add "$FILE" "/4-Waterfall Advanced" --type chart \
--prop chartType=waterfall \
--prop title="Annual P&L Detail" \
--prop "data=Revenue:8500,COGS:-3400,Gross Profit:5100,R&D:-1200,Sales:-900,Marketing:-600,G&A:-500,EBITDA:1900,Depreciation:-300,Interest:-200,Tax:-350,Net Income:1050" \
--prop increaseColor=548235 \
--prop decreaseColor=C00000 \
--prop totalColor=2F5496 \
--prop x=13 --prop y=19 --prop width=12 --prop height=18 \
--prop dataLabels=true \
--prop axisfont=8:333333:Calibri
# Remove blank default Sheet1 (all data is inline)
officecli remove "$FILE" /Sheet1
officecli close "$FILE"
officecli validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+179
View File
@@ -0,0 +1,179 @@
# Conditional Formatting Showcase
Exercises the full xlsx `conditionalformatting` rule family — the one major
spreadsheet feature the other excel examples don't cover. Three files work
together:
- **conditional-formatting.py** — builds the workbook via the **officecli Python SDK**.
- **conditional-formatting.xlsx** — the generated 7-sheet workbook.
- **conditional-formatting.md** — this file.
## Built on the SDK (not subprocess)
Unlike the sibling `*.py` examples (which `subprocess.run("officecli …")` once
per command), this script drives the [`officecli-sdk`](../../sdk/python) Python
client. One resident process is started; every rule is shipped over the named
pipe; all the rules for a sheet go in a single `doc.batch(...)` round-trip:
```python
import officecli # pip install officecli-sdk
with officecli.create(FILE, "--force") as doc:
doc.batch([
{"command": "set", "path": "/Sheet1/A2", "props": {"value": "58"}},
{"command": "add", "parent": "/Sheet1", "type": "conditionalformatting",
"props": {"type": "cellIs", "ref": "A2:A11",
"operator": "greaterThan", "value": "80", "fill": "C6EFCE"}},
])
```
The dict shape is identical to an `officecli batch` list item — `command`,
`path`/`parent`/`type`, and `props`. The script falls back to the in-repo SDK
copy if `officecli-sdk` isn't pip-installed, so it runs straight from a checkout.
## Regenerate
```bash
cd examples/excel
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 conditional-formatting.py
# → conditional-formatting.xlsx
```
## A conditional-formatting rule
Every rule is one `add` against the sheet, with `type=` selecting the rule kind
and `ref=` the target range. The match format (fill colour, bar, scale, icons)
is carried by the remaining props:
```bash
officecli add file.xlsx /Sheet1 --type conditionalformatting \
--prop type=cellIs --prop ref=A2:A11 --prop operator=greaterThan \
--prop value=80 --prop fill=C6EFCE
```
The rule lands at `/Sheet1/cf[N]`; `get`/`set`/`remove` address it there. A
rule's differential fill is stored once in the workbook-level `<dxfs>` table
(styles.xml) and referenced by index.
## Sheets
### Sheet1 — CellIs (value comparison)
`operator``greaterThan`, `lessThan`, `greaterThanOrEqual`,
`lessThanOrEqual`, `equal`, `notEqual`, `between`, `notBetween`. `between`/
`notBetween` use both `value` and `value2`.
```bash
officecli add file.xlsx /Sheet1 --type conditionalformatting --prop type=cellIs --prop ref=A2:A11 --prop operator=greaterThan --prop value=80 --prop fill=C6EFCE
officecli add file.xlsx /Sheet1 --type conditionalformatting --prop type=cellIs --prop ref=A2:A11 --prop operator=between --prop value=50 --prop value2=70 --prop fill=FFEB9C
```
### Sheet2 — Text rules
`containsText`, `notContainsText`, `beginsWith`, `endsWith`. The needle is
`text=`; the match fill is `fill=`.
```bash
officecli add file.xlsx /Text --type conditionalformatting --prop type=containsText --prop ref=A2:A9 --prop text=error --prop fill=FFC7CE
officecli add file.xlsx /Text --type conditionalformatting --prop type=beginsWith --prop ref=A2:A9 --prop text=Begins --prop fill=BDD7EE
```
### Sheet3 — Top / Bottom / Average
`top10`/`topN` (count via `rank=`), `topPercent` (`rank=` + `percent=true`),
`bottom`, `aboveAverage`/`belowAverage` (`aboveAverage=true|false`, optional
`stdDev=` for an N-sigma band).
```bash
officecli add file.xlsx /TopBottom --type conditionalformatting --prop type=top10 --prop ref=A2:A13 --prop rank=3 --prop fill=C6EFCE
officecli add file.xlsx /TopBottom --type conditionalformatting --prop type=topPercent --prop ref=A2:A13 --prop rank=25 --prop percent=true --prop fill=63BE7B
officecli add file.xlsx /TopBottom --type conditionalformatting --prop type=aboveAverage --prop ref=A2:A13 --prop aboveAverage=true --prop stdDev=1 --prop fill=FFEB9C
```
### Sheet4 — Data bars
`color` is the bar fill; `min`/`max` set the scale (`auto` = automatic bounds —
the default). The 2010+ extension adds `negativeColor`, `axisColor`, and
`axisPosition` (`automatic`/`middle`/`none`), so negative values render leftward
in their own colour about a mid axis.
```bash
officecli add file.xlsx /DataBars --type conditionalformatting --prop type=dataBar --prop ref=A2:A11 \
--prop color=638EC6 --prop min=auto --prop max=auto \
--prop negativeColor=FF0000 --prop axisColor=000000 --prop axisPosition=middle --prop showValue=true
```
> `min=auto`/`max=auto` is the **automatic-bound sentinel** — it serializes to
> `<cfvo type="min"/>`/`<cfvo type="max"/>` (and x14 `autoMin`/`autoMax`), the
> same as omitting the bound. (Passing a real number, e.g. `min=0 max=100`,
> pins the scale instead.)
### Sheet5 — Color scales
2-colour (`minColor`/`maxColor`) or 3-colour (`+ midColor`, midpoint via
`midPoint=`).
```bash
officecli add file.xlsx /ColorScales --type conditionalformatting --prop type=colorScale --prop ref=A2:A11 --prop minColor=FFFFFF --prop maxColor=63BE7B
officecli add file.xlsx /ColorScales --type conditionalformatting --prop type=colorScale --prop ref=B2:B11 --prop minColor=F8696B --prop midColor=FFEB84 --prop maxColor=63BE7B --prop midPoint=50
```
### Sheet6 — Icon sets
`iconset=` names the set (`3TrafficLights1`, `3Arrows`, `4Rating`, `5Rating`, …).
`reverse=true` flips the order; `showValue=false` hides the cell value behind the
icon.
```bash
officecli add file.xlsx /IconSets --type conditionalformatting --prop type=iconSet --prop ref=A2:A11 --prop iconset=3TrafficLights1
officecli add file.xlsx /IconSets --type conditionalformatting --prop type=iconSet --prop ref=D2:D11 --prop iconset=3TrafficLights1 --prop reverse=true
```
### Sheet7 — Formula, date, duplicate / unique
`formula` (a boolean expression, no leading `=`), `dateOccurring` (`period=`
token), `duplicateValues`, `uniqueValues`.
```bash
officecli add file.xlsx /FormulaEtc --type conditionalformatting --prop type=formula --prop ref=A2:A11 --prop formula="ISODD(A2)" --prop fill=BDD7EE
officecli add file.xlsx /FormulaEtc --type conditionalformatting --prop type=duplicateValues --prop ref=A2:A11 --prop fill=FFC7CE
officecli add file.xlsx /FormulaEtc --type conditionalformatting --prop type=dateOccurring --prop ref=B2:B11 --prop period=thisMonth --prop fill=FFEB9C
```
## Complete feature coverage
| Rule family | `type=` | Key props | Sheet |
|---|---|---|---|
| Comparison | `cellIs` | `operator`, `value`, `value2`, `fill` | Sheet1 |
| Text | `containsText` / `notContainsText` / `beginsWith` / `endsWith` | `text`, `fill` | Sheet2 |
| Top/Bottom | `top10` / `topN` / `topPercent` / `bottom` | `rank`, `percent`, `bottom`, `fill` | Sheet3 |
| Average | `aboveAverage` / `belowAverage` | `aboveAverage`, `stdDev`, `equalAverage`, `fill` | Sheet3 |
| Data bar | `dataBar` | `color`, `min`, `max`, `negativeColor`, `axisColor`, `axisPosition`, `showValue` | Sheet4 |
| Colour scale | `colorScale` | `minColor`, `midColor`, `maxColor`, `midPoint` | Sheet5 |
| Icon set | `iconSet` | `iconset`, `reverse`, `showValue` | Sheet6 |
| Formula | `formula` | `formula`, `fill` | Sheet7 |
| Date | `dateOccurring` | `period`, `fill` | Sheet7 |
| Dup/Unique | `duplicateValues` / `uniqueValues` | `fill` | Sheet7 |
Full property list: `officecli help xlsx conditionalformatting` (or
`schemas/help/xlsx/conditionalformatting.json`).
## Read a rule back
```bash
officecli query conditional-formatting.xlsx conditionalformatting
officecli get conditional-formatting.xlsx "/Sheet1/cf[1]" --json
```
`get` normalizes on read: colours gain a `#` prefix (`#C6EFCE`), and the rule
`type` comes back as the canonical camelCase token.
## Validating CF documents
A data-bar / colour-scale fill lives in the workbook `<dxfs>` table, so always
validate the **saved** file from a fresh process:
```bash
officecli validate conditional-formatting.xlsx
```
+224
View File
@@ -0,0 +1,224 @@
#!/usr/bin/env python3
"""
Conditional Formatting Showcase — generates conditional-formatting.xlsx
exercising the full xlsx `conditionalformatting` rule family
(schemas/help/xlsx/conditionalformatting.json).
Unlike the other excel/*.py (which shell out per command), this one drives the
**officecli Python SDK** (`pip install officecli-sdk`): one resident is started,
every write goes over the named pipe, and all the rules for a sheet are applied
in a single `doc.batch(...)` round-trip. Same `{"command","parent","type",
"props"}` dict shape you'd put in an `officecli batch` list.
7 sheets, one rule family each:
CellIs — greaterThan/lessThan/between/equal/notEqual + fill
Text — containsText/notContainsText/beginsWith/endsWith + needle + fill
TopBottom — top10/topN/topPercent/bottom + aboveAverage/belowAverage (+stdDev)
DataBars — bar color, min/max, negativeColor, axisColor/Position, showValue
ColorScales — 2-colour (min/max) and 3-colour (min/mid/max + midPoint) scales
IconSets — 3TrafficLights/3Arrows/4Rating/5Rating, reverse, showValue
FormulaEtc — formula, dateOccurring, duplicateValues, uniqueValues
Closes with a Get round-trip proving the canonical keys read back.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 conditional-formatting.py
"""
import os
import sys
import subprocess
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "conditional-formatting.xlsx")
def col_data(sheet, start_row, values, **header):
"""Build batch `set` items writing `values` down a column from A{start_row}.
Returns the item list (caller batches them)."""
items = []
if header:
items.append({"command": "set", "path": f"/{sheet}/A{start_row - 1}",
"props": {"value": header.pop("title"), "font.bold": "true",
"fill": "1F4E79", "font.color": "FFFFFF", **header}})
for i, v in enumerate(values, start=start_row):
items.append({"command": "set", "path": f"/{sheet}/A{i}", "props": {"value": str(v)}})
return items
def cf(sheet, **props):
"""One `add conditionalformatting` item in batch-shape."""
return {"command": "add", "parent": f"/{sheet}", "type": "conditionalformatting", "props": props}
def add_sheet(name):
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def label(sheet, ref, text):
return {"command": "set", "path": f"/{sheet}/{ref}",
"props": {"value": text, "font.italic": "true", "font.color": "595959"}}
print("\n==========================================")
print(f"Generating conditional-formatting showcase: {FILE}")
print("==========================================")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet1: CellIs — value-comparison rules
# ======================================================================
print("\n--- Sheet1: CellIs (comparison) ---")
scores = [42, 58, 91, 73, 30, 88, 65, 100, 12, 77]
items = col_data("Sheet1", 2, scores, title="Scores")
items += [
# one column per operator so each rule's effect is visible side-by-side
cf("Sheet1", type="cellIs", ref="A2:A11", operator="greaterThan", value="80", fill="C6EFCE"),
cf("Sheet1", type="cellIs", ref="A2:A11", operator="lessThan", value="40", fill="FFC7CE"),
cf("Sheet1", type="cellIs", ref="A2:A11", operator="between", value="50", value2="70", fill="FFEB9C"),
cf("Sheet1", type="cellIs", ref="A2:A11", operator="equal", value="100", fill="63BE7B"),
label("Sheet1", "C2", ">80 green · <40 red · 50-70 amber · =100 deep-green"),
]
doc.batch(items)
# ======================================================================
# Sheet2: Text rules — needle matching
# ======================================================================
print("--- Sheet2: Text rules ---")
words = ["ERROR: timeout", "ok", "WARNING low", "error code 5", "passed",
"Begins here", "ends with END", "neutral"]
items = [add_sheet("Text")] + col_data("Text", 2, words, title="Log line")
items += [
cf("Text", type="containsText", ref="A2:A9", text="error", fill="FFC7CE"),
cf("Text", type="notContainsText", ref="A2:A9", text="error", fill="C6EFCE"),
cf("Text", type="beginsWith", ref="A2:A9", text="Begins", fill="BDD7EE"),
cf("Text", type="endsWith", ref="A2:A9", text="END", fill="FFE699"),
label("Text", "C2", "contains 'error' red · begins 'Begins' blue · ends 'END' gold"),
]
doc.batch(items)
# ======================================================================
# Sheet3: Top / Bottom / Average
# ======================================================================
print("--- Sheet3: Top/Bottom/Average ---")
revenue = [120, 340, 90, 510, 275, 60, 430, 180, 295, 75, 360, 145]
items = [add_sheet("TopBottom")] + col_data("TopBottom", 2, revenue, title="Revenue")
items += [
cf("TopBottom", type="top10", ref="A2:A13", rank="3", fill="C6EFCE"), # top 3 values
cf("TopBottom", type="bottom", ref="A2:A13", rank="3", fill="FFC7CE"), # bottom 3 values
cf("TopBottom", type="topPercent", ref="A2:A13", rank="25", percent="true", fill="63BE7B"),
cf("TopBottom", type="aboveAverage", ref="A2:A13", aboveAverage="true", fill="BDD7EE"),
cf("TopBottom", type="belowAverage", ref="A2:A13", aboveAverage="false", fill="F8CBAD"),
cf("TopBottom", type="aboveAverage", ref="A2:A13", aboveAverage="true", stdDev="1", fill="FFEB9C"), # >1 sigma
label("TopBottom", "C2", "top3 / bottom3 / top25% / above & below avg / >1 sigma"),
]
doc.batch(items)
# ======================================================================
# Sheet4: Data bars
# ======================================================================
print("--- Sheet4: Data bars ---")
netflow = [120, -45, 300, -80, 210, 60, -150, 90, 175, -30]
items = [add_sheet("DataBars")] + col_data("DataBars", 2, netflow, title="Net flow")
items += [
# gradient-style bar with explicit scale, negative bars + axis styling
cf("DataBars", type="dataBar", ref="A2:A11", color="638EC6", min="auto", max="auto",
negativeColor="FF0000", axisColor="000000", axisPosition="middle", showValue="true"),
label("DataBars", "C2", "blue bars, red negatives, mid axis, values shown"),
]
doc.batch(items)
# ======================================================================
# Sheet5: Color scales
# ======================================================================
print("--- Sheet5: Color scales ---")
heat = [10, 25, 40, 55, 70, 85, 100, 30, 60, 90]
items = [add_sheet("ColorScales")]
items += col_data("ColorScales", 2, heat, title="2-colour")
# second column for the 3-colour scale
items += [{"command": "set", "path": f"/ColorScales/B{i}", "props": {"value": str(v)}}
for i, v in enumerate(heat, start=2)]
items += [{"command": "set", "path": "/ColorScales/B1",
"props": {"value": "3-colour", "font.bold": "true", "fill": "1F4E79", "font.color": "FFFFFF"}}]
items += [
cf("ColorScales", type="colorScale", ref="A2:A11", minColor="FFFFFF", maxColor="63BE7B"),
cf("ColorScales", type="colorScale", ref="B2:B11", minColor="F8696B", midColor="FFEB84",
maxColor="63BE7B", midPoint="50"),
label("ColorScales", "D2", "A: white-to-green 2-stop · B: red-amber-green 3-stop @ 50%"),
]
doc.batch(items)
# ======================================================================
# Sheet6: Icon sets
# ======================================================================
print("--- Sheet6: Icon sets ---")
ratings = [1, 2, 3, 4, 5, 2, 4, 5, 1, 3]
items = [add_sheet("IconSets")]
cols = [("A", "3TrafficLights1", "false"), ("B", "3Arrows", "false"),
("C", "5Rating", "false"), ("D", "3TrafficLights1", "true")]
for c, _, _ in cols:
items += [{"command": "set", "path": f"/IconSets/{c}{i}", "props": {"value": str(v)}}
for i, v in enumerate(ratings, start=2)]
for c, name, rev in cols:
items.append({"command": "set", "path": f"/IconSets/{c}1",
"props": {"value": f"{name}{' rev' if rev == 'true' else ''}",
"font.bold": "true", "fill": "1F4E79", "font.color": "FFFFFF"}})
items.append(cf("IconSets", type="iconSet", ref=f"{c}2:{c}11", iconset=name,
reverse=rev, showValue="true"))
items.append(label("IconSets", "F2", "lights / arrows / 5-rating / reversed lights"))
doc.batch(items)
# ======================================================================
# Sheet7: Formula, date-occurring, duplicate / unique
# ======================================================================
print("--- Sheet7: Formula / date / dup / unique ---")
nums = [4, 7, 4, 9, 2, 7, 5, 1, 9, 3]
items = [add_sheet("FormulaEtc")] + col_data("FormulaEtc", 2, nums, title="Value")
# a date column for dateOccurring
items += [{"command": "set", "path": f"/FormulaEtc/B{i}",
"props": {"value": d, "numberformat": "yyyy-mm-dd"}}
for i, d in enumerate(["45800", "45810", "45820", "45830", "45840",
"45850", "45860", "45870", "45880", "45890"], start=2)]
items += [{"command": "set", "path": "/FormulaEtc/B1",
"props": {"value": "Date", "font.bold": "true", "fill": "1F4E79", "font.color": "FFFFFF"}}]
items += [
cf("FormulaEtc", type="formula", ref="A2:A11", formula="ISODD(A2)", fill="BDD7EE"), # odd values
cf("FormulaEtc", type="duplicateValues", ref="A2:A11", fill="FFC7CE"),
cf("FormulaEtc", type="uniqueValues", ref="A2:A11", fill="C6EFCE"),
cf("FormulaEtc", type="dateOccurring", ref="B2:B11", period="thisMonth", fill="FFEB9C"),
label("FormulaEtc", "D2", "A: odd=blue, dup=red, unique=green · B: this-month=amber"),
]
doc.batch(items)
# ======================================================================
# Get round-trip: confirm canonical keys read back (in-session, over pipe)
# ======================================================================
print("\n--- Round-trip readback (Get the rules) ---")
for path in ["/Sheet1/cf[1]", "/DataBars/cf[1]", "/ColorScales/cf[2]", "/IconSets/cf[1]"]:
node = doc.send({"command": "get", "path": path})
fmt = node.get("data", {}).get("results", [{}])[0].get("format", {})
keys = ("type", "ref", "operator", "value", "fill", "color", "minColor", "maxColor",
"midColor", "iconset", "reverse")
shown = {k: fmt.get(k) for k in keys if k in fmt}
print(f" {path}: {shown}")
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
# Validate the SAVED file with a fresh one-shot process (NOT in-session): a
# conditional-formatting rule's fill lives in the workbook-level <dxfs> table in
# styles.xml, so validate from disk to confirm those dxf references resolved.
print("\n--- Validate (fresh process, from disk) ---")
r = subprocess.run(["officecli", "validate", FILE], capture_output=True, text=True)
print(" ", (r.stdout or r.stderr).strip().split("\n")[0])
print(f"\nCreated: {FILE}")
+94
View File
@@ -0,0 +1,94 @@
#!/bin/bash
# conditional-formatting.sh — exercise the full xlsx `conditionalformatting` rule
# family (schemas/help/xlsx/conditionalformatting.json) using the officecli CLI.
#
# 7 sheets, one rule family each: cellIs, text, top/bottom/average, data bars,
# colour scales, icon sets, formula/date/dup-unique. CLI twin of
# conditional-formatting.py (officecli SDK); both produce an equivalent
# conditional-formatting.xlsx.
#
# Each rule is one `add` against the sheet, with type= selecting the rule kind
# and ref= the target range. The fill lands in the workbook <dxfs> table.
# 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")/conditional-formatting.xlsx"
echo "Building $FILE ..."
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# helper: write a header in A1 then a column of values from A2 down
col() { # col <sheet> <title> <v1> <v2> ...
local sheet="$1" title="$2"; shift 2
officecli set "$FILE" "/$sheet/A1" --prop value="$title" --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF
local i=2
for v in "$@"; do officecli set "$FILE" "/$sheet/A$i" --prop value="$v"; i=$((i+1)); done
}
cf() { officecli add "$FILE" "/$1" --type conditionalformatting "${@:2}"; } # cf <sheet> --prop ...
# ===== Sheet1: CellIs (comparison) =====
col Sheet1 Scores 42 58 91 73 30 88 65 100 12 77
cf Sheet1 --prop type=cellIs --prop ref=A2:A11 --prop operator=greaterThan --prop value=80 --prop fill=C6EFCE
cf Sheet1 --prop type=cellIs --prop ref=A2:A11 --prop operator=lessThan --prop value=40 --prop fill=FFC7CE
cf Sheet1 --prop type=cellIs --prop ref=A2:A11 --prop operator=between --prop value=50 --prop value2=70 --prop fill=FFEB9C
cf Sheet1 --prop type=cellIs --prop ref=A2:A11 --prop operator=equal --prop value=100 --prop fill=63BE7B
# ===== Sheet2: Text rules =====
officecli add "$FILE" / --type sheet --prop name=Text
col Text "Log line" "ERROR: timeout" ok "WARNING low" "error code 5" passed "Begins here" "ends with END" neutral
cf Text --prop type=containsText --prop ref=A2:A9 --prop text=error --prop fill=FFC7CE
cf Text --prop type=notContains --prop ref=A2:A9 --prop text=error --prop fill=C6EFCE # handler token: notContains
cf Text --prop type=beginsWith --prop ref=A2:A9 --prop text=Begins --prop fill=BDD7EE
cf Text --prop type=endsWith --prop ref=A2:A9 --prop text=END --prop fill=FFE699
# ===== Sheet3: Top / Bottom / Average =====
officecli add "$FILE" / --type sheet --prop name=TopBottom
col TopBottom Revenue 120 340 90 510 275 60 430 180 295 75 360 145
cf TopBottom --prop type=top10 --prop ref=A2:A13 --prop rank=3 --prop fill=C6EFCE
cf TopBottom --prop type=bottom --prop ref=A2:A13 --prop rank=3 --prop fill=FFC7CE
cf TopBottom --prop type=topPercent --prop ref=A2:A13 --prop rank=25 --prop percent=true --prop fill=63BE7B
cf TopBottom --prop type=aboveAverage --prop ref=A2:A13 --prop aboveAverage=true --prop fill=BDD7EE
cf TopBottom --prop type=belowAverage --prop ref=A2:A13 --prop fill=F8CBAD # type implies direction
cf TopBottom --prop type=aboveAverage --prop ref=A2:A13 --prop aboveAverage=true --prop stdDev=1 --prop equalAverage=true --prop fill=FFEB9C
# ===== Sheet4: Data bars =====
officecli add "$FILE" / --type sheet --prop name=DataBars
col DataBars "Net flow" 120 -45 300 -80 210 60 -150 90 175 -30
cf DataBars --prop type=dataBar --prop ref=A2:A11 --prop color=638EC6 --prop min=auto --prop max=auto \
--prop negativeColor=FF0000 --prop axisColor=000000 --prop axisPosition=middle --prop showValue=true
# ===== Sheet5: Colour scales =====
officecli add "$FILE" / --type sheet --prop name=ColorScales
col ColorScales "2-colour" 10 25 40 55 70 85 100 30 60 90
officecli set "$FILE" /ColorScales/B1 --prop value="3-colour" --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF
b=2; for v in 10 25 40 55 70 85 100 30 60 90; do officecli set "$FILE" "/ColorScales/B$b" --prop value="$v"; b=$((b+1)); done
cf ColorScales --prop type=colorScale --prop ref=A2:A11 --prop minColor=FFFFFF --prop maxColor=63BE7B
cf ColorScales --prop type=colorScale --prop ref=B2:B11 --prop minColor=F8696B --prop midColor=FFEB84 --prop maxColor=63BE7B --prop midPoint=50
# ===== Sheet6: Icon sets =====
officecli add "$FILE" / --type sheet --prop name=IconSets
for c in A B C D; do r=2; for v in 1 2 3 4 5 2 4 5 1 3; do officecli set "$FILE" "/IconSets/$c$r" --prop value="$v"; r=$((r+1)); done; done
officecli set "$FILE" /IconSets/A1 --prop value=3TrafficLights1 --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF
officecli set "$FILE" /IconSets/B1 --prop value=3Arrows --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF
officecli set "$FILE" /IconSets/C1 --prop value=5Rating --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF
officecli set "$FILE" /IconSets/D1 --prop value="3TrafficLights1 rev" --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF
cf IconSets --prop type=iconSet --prop ref=A2:A11 --prop iconset=3TrafficLights1 --prop showValue=true
cf IconSets --prop type=iconSet --prop ref=B2:B11 --prop iconset=3Arrows --prop showValue=true
cf IconSets --prop type=iconSet --prop ref=C2:C11 --prop iconset=5Rating --prop showValue=true
cf IconSets --prop type=iconSet --prop ref=D2:D11 --prop iconset=3TrafficLights1 --prop reverse=true --prop showValue=true
# ===== Sheet7: Formula / date / duplicate / unique =====
officecli add "$FILE" / --type sheet --prop name=FormulaEtc
col FormulaEtc Value 4 7 4 9 2 7 5 1 9 3
officecli set "$FILE" /FormulaEtc/B1 --prop value=Date --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF
b=2; for d in 45800 45810 45820 45830 45840 45850 45860 45870 45880 45890; do
officecli set "$FILE" "/FormulaEtc/B$b" --prop value="$d" --prop numberformat=yyyy-mm-dd; b=$((b+1)); done
cf FormulaEtc --prop type=formula --prop ref=A2:A11 --prop formula="ISODD(A2)" --prop fill=BDD7EE
cf FormulaEtc --prop type=duplicateValues --prop ref=A2:A11 --prop fill=FFC7CE
cf FormulaEtc --prop type=uniqueValues --prop ref=A2:A11 --prop fill=C6EFCE
cf FormulaEtc --prop type=dateOccurring --prop ref=B2:B11 --prop period=thisMonth --prop fill=FFEB9C
officecli close "$FILE"
officecli validate "$FILE"
echo "Created: $FILE"
Binary file not shown.
+186
View File
@@ -0,0 +1,186 @@
# Data Validation Showcase
Exercises the full xlsx `validation` (dataValidation) feature surface — the
input-restriction rules Excel enforces on cell entry. Three files work together:
- **data-validation.py** — builds the workbook via the **officecli Python SDK**.
- **data-validation.sh** — the CLI twin (`officecli add … --type validation`).
- **data-validation.xlsx** — the generated 6-sheet workbook.
- **data-validation.md** — this file.
## Built on the SDK (not subprocess)
Unlike the sibling `*.py` examples (which `subprocess.run("officecli …")` once
per command), the Python twin drives the [`officecli-sdk`](../../sdk/python)
client. One resident process is started; every validation is shipped over the
named pipe; all the validations for a sheet go in a single `doc.batch(...)`
round-trip:
```python
import officecli # pip install officecli-sdk
with officecli.create(FILE, "--force") as doc:
doc.batch([
{"command": "set", "path": "/Sheet1/A2", "props": {"value": "Draft"}},
{"command": "add", "parent": "/Sheet1", "type": "validation",
"props": {"type": "list", "ref": "A2:A20",
"formula1": "Draft,Review,Approved,Rejected"}},
])
```
The dict shape is identical to an `officecli batch` list item — `command`,
`path`/`parent`/`type`, and `props`. The script falls back to the in-repo SDK
copy if `officecli-sdk` isn't pip-installed, so it runs straight from a checkout.
## Regenerate
```bash
cd examples/excel
bash data-validation.sh # CLI twin → data-validation.xlsx
# or:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 data-validation.py # SDK twin → equivalent data-validation.xlsx
```
## A data-validation rule
Every rule is one `add --type validation` against the sheet, with `type=`
selecting the rule kind and `ref=` (alias `sqref`) the target range:
```bash
officecli add file.xlsx /Sheet1 --type validation \
--prop type=whole --prop ref=A2:A50 --prop operator=between \
--prop formula1=1 --prop formula2=100
```
The rule lands at `/SheetName/dataValidation[N]`; `get`/`set`/`remove` address
it there (the alias `/SheetName/validation[N]` is also accepted). `type`
determines which of `formula1`/`formula2` are used — comparison rules use
`operator` plus one bound (`formula1`) or two (`between`/`notBetween` use both).
## Sheets
### Sheet1 — List (inline + range)
`type=list`. The allowed values are `formula1`: either an **inline CSV**
(`Draft,Review,Approved,Rejected`) or a **range reference** (`=$H$2:$H$5`)
pointing at a helper column. `inCellDropdown=true` (default) shows the dropdown
arrow; `inCellDropdown=false` hides it (the list still validates on typed input).
```bash
officecli add file.xlsx /Sheet1 --type validation --prop type=list --prop ref=A2:A20 --prop formula1="Draft,Review,Approved,Rejected"
officecli add file.xlsx /Sheet1 --type validation --prop type=list --prop sqref=B2:B20 --prop formula1==$H$2:$H$5 --prop inCellDropdown=false
```
### Sheet2 — Number (whole / decimal)
`type=whole` (integers) or `type=decimal` (any number), with `operator`
`between`, `notBetween`, `equal`, `notEqual`, `greaterThan`,
`greaterThanOrEqual`, `lessThan`, `lessThanOrEqual`. `between`/`notBetween` use
both `formula1` (low) and `formula2` (high); the others use `formula1` only.
```bash
officecli add file.xlsx /Number --type validation --prop type=whole --prop ref=A2:A50 --prop operator=between --prop formula1=1 --prop formula2=100
officecli add file.xlsx /Number --type validation --prop type=decimal --prop ref=B2:B50 --prop operator=lessThanOrEqual --prop formula1=0.5
officecli add file.xlsx /Number --type validation --prop type=whole --prop ref=E2:E50 --prop operator=notEqual --prop formula1=13
```
### Sheet3 — Date & Time
`type=date` / `type=time`, same operator set. Dates accept ISO input
(`2024-01-01`) and are stored as Excel **serial numbers** on readback
(`2024-01-01``45292`); times are stored as **day fractions**
(`09:00:00``0.375`).
```bash
officecli add file.xlsx /DateTime --type validation --prop type=date --prop ref=A2:A50 --prop operator=between --prop formula1=2024-01-01 --prop formula2=2024-12-31
officecli add file.xlsx /DateTime --type validation --prop type=time --prop ref=B2:B50 --prop operator=between --prop formula1=09:00:00 --prop formula2=17:00:00
officecli add file.xlsx /DateTime --type validation --prop type=date --prop ref=C2:C50 --prop operator=equal --prop formula1=2024-12-31
```
### Sheet4 — Text length
`type=textLength`, same operator set — `formula1`/`formula2` are character
counts. Handy for bounded (`between 316`), exact (`equal 2`), capped
(`lessThanOrEqual 280`), or excluded-band (`notBetween 57`) lengths.
```bash
officecli add file.xlsx /TextLength --type validation --prop type=textLength --prop ref=A2:A50 --prop operator=between --prop formula1=3 --prop formula2=16
officecli add file.xlsx /TextLength --type validation --prop type=textLength --prop ref=C2:C50 --prop operator=lessThanOrEqual --prop formula1=280
```
### Sheet5 — Custom formula
`type=custom`. `formula1` is any boolean expression (relative to the top-left
cell of `ref`); the entry is valid when it evaluates `TRUE`. No `operator`.
```bash
officecli add file.xlsx /Custom --type validation --prop type=custom --prop ref=A2:A50 --prop formula1="ISNUMBER(A2)"
officecli add file.xlsx /Custom --type validation --prop type=custom --prop ref=B2:B50 --prop formula1="MOD(B2,2)=0"
```
### Sheet6 — Messages (prompt / error / errorStyle)
Any validation can carry an **input prompt** (`promptTitle` + `prompt`, gated by
`showInput`) shown when the cell is selected, and an **error alert**
(`errorTitle` + `error`, gated by `showError`) shown on invalid input. The alert
severity is `errorStyle`:
- `stop` (default) — hard block; the entry is rejected.
- `warning` — soft block; the user may override.
- `information` — advisory only; never blocks.
`allowBlank=false` makes empty cells themselves invalid (default `true`).
```bash
officecli add file.xlsx /Messages --type validation --prop type=whole --prop ref=A2:A50 --prop operator=between --prop formula1=18 --prop formula2=120 \
--prop promptTitle="Enter age" --prop prompt="Age must be 18-120" \
--prop errorTitle="Invalid age" --prop error="Please enter a whole number 18-120" --prop errorStyle=stop
officecli add file.xlsx /Messages --type validation --prop type=decimal --prop ref=B2:B50 --prop operator=lessThanOrEqual --prop formula1=10000 --prop errorStyle=warning ...
officecli add file.xlsx /Messages --type validation --prop type=whole --prop ref=D2:D50 --prop operator=greaterThan --prop formula1=0 --prop allowBlank=false --prop showInput=false
```
## Complete feature coverage
| Family | `type=` | Key props | Sheet |
|---|---|---|---|
| List (inline) | `list` | `formula1` (CSV), `inCellDropdown` | Sheet1 |
| List (range) | `list` | `formula1` (`=$H$2:$H$5`), `sqref`, `inCellDropdown=false` | Sheet1 |
| Whole number | `whole` | `operator`, `formula1`, `formula2` | Number |
| Decimal | `decimal` | `operator`, `formula1`, `formula2` | Number |
| Date | `date` | `operator`, `formula1`, `formula2` (ISO → serial) | DateTime |
| Time | `time` | `operator`, `formula1`, `formula2` (→ day fraction) | DateTime |
| Text length | `textLength` | `operator`, `formula1`, `formula2` | TextLength |
| Custom | `custom` | `formula1` (boolean expr) | Custom |
| Input prompt | any | `promptTitle`, `prompt`, `showInput` | Messages |
| Error alert | any | `errorTitle`, `error`, `showError`, `errorStyle` | Messages |
| Blank policy | any | `allowBlank` | Messages |
Operators covered: `between`, `notBetween`, `equal`, `notEqual`,
`greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual`.
`errorStyle` covered: `stop`, `warning`, `information`.
Full property list: `officecli help xlsx validation` (or
`schemas/help/xlsx/validation.json`).
## Read a validation back
```bash
officecli query data-validation.xlsx validation
officecli get data-validation.xlsx "/Sheet1/dataValidation[1]" --json
```
`get` normalizes on read: `type`/`operator` come back as canonical tokens,
dates/times as serials/fractions, and default flags (`showInput=true`,
`showError=true`, `allowBlank=true`) are implied — only non-default flags
(e.g. `inCellDropdown=false`, `allowBlank=false`, `errorStyle=warning`) surface
explicitly.
## Validating
Validations live in each sheet's `<dataValidations>` block, so validate the
saved file:
```bash
officecli validate data-validation.xlsx
```
+220
View File
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""
Data Validation Showcase — generates data-validation.xlsx exercising the full
xlsx `validation` (dataValidation) feature surface
(schemas/help/xlsx/validation.json).
Unlike the other excel/*.py (which shell out per command), this one drives the
**officecli Python SDK** (`pip install officecli-sdk`): one resident is started,
every write goes over the named pipe, and all the validations for a sheet are
applied in a single `doc.batch(...)` round-trip. Same `{"command","parent",
"type","props"}` dict shape you'd put in an `officecli batch` list.
6 sheets, one validation family each:
Sheet1 — list: inline CSV list AND range-based list (=$H$2:$H$5), inCellDropdown
Number — whole/decimal with between/notEqual/greaterThan/lessThanOrEqual/…
DateTime — date range, time window, exact-equal date, on/after date
TextLength — bounded / exact / capped / notBetween length rules
Custom — arbitrary boolean formula1 expressions
Messages — prompt + promptTitle + showInput, error + errorTitle + showError,
all three errorStyles (stop / warning / information), allowBlank
Closes with a Get round-trip proving the canonical keys read back.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 data-validation.py
"""
import os
import sys
import subprocess
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data-validation.xlsx")
def hdr(sheet, ref, text):
"""Bold blue-on-white header cell."""
return {"command": "set", "path": f"/{sheet}/{ref}",
"props": {"value": text, "font.bold": "true", "fill": "1F4E79", "font.color": "FFFFFF"}}
def col(sheet, letter, start_row, values):
"""Write `values` down a column from {letter}{start_row}."""
return [{"command": "set", "path": f"/{sheet}/{letter}{i}", "props": {"value": str(v)}}
for i, v in enumerate(values, start=start_row)]
def dv(sheet, **props):
"""One `add validation` item in batch-shape."""
return {"command": "add", "parent": f"/{sheet}", "type": "validation", "props": props}
def add_sheet(name):
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
print("\n==========================================")
print(f"Generating data-validation showcase: {FILE}")
print("==========================================")
with officecli.create(FILE, "--force") as doc:
# ======================================================================
# Sheet1: List — inline CSV list AND range-based list (helper column)
# ======================================================================
print("\n--- Sheet1: List (inline + range) ---")
items = [
hdr("Sheet1", "A1", "Status (inline)"),
hdr("Sheet1", "B1", "Priority (range)"),
hdr("Sheet1", "H1", "Priorities"),
]
items += col("Sheet1", "H", 2, ["Low", "Medium", "High", "Critical"]) # the range B points at
items += [
# inline CSV list; dropdown arrow shown (inCellDropdown default true)
dv("Sheet1", type="list", ref="A2:A20", formula1="Draft,Review,Approved,Rejected"),
# range-based list via sqref alias; hide the dropdown arrow
dv("Sheet1", type="list", sqref="B2:B20", formula1="=$H$2:$H$5", inCellDropdown="false"),
]
doc.batch(items)
# ======================================================================
# Sheet2: Number — whole & decimal, every comparison operator
# ======================================================================
print("--- Sheet2: Number (whole/decimal) ---")
items = [add_sheet("Number"),
hdr("Number", "A1", "Qty (whole)"), hdr("Number", "B1", "Discount (decimal)"),
hdr("Number", "C1", "Rating (1-5)"), hdr("Number", "D1", "Price (>0)"),
hdr("Number", "E1", "Not 13")]
items += col("Number", "A", 2, [1, 5, 10, 20])
items += col("Number", "B", 2, [0.05, 0.10, 0.25])
items += col("Number", "C", 2, [1, 3, 5])
items += col("Number", "D", 2, [9.99, 19.5])
items += col("Number", "E", 2, [12, 14])
items += [
dv("Number", type="whole", ref="A2:A50", operator="between", formula1="1", formula2="100"),
dv("Number", type="decimal", ref="B2:B50", operator="lessThanOrEqual", formula1="0.5"),
dv("Number", type="whole", ref="C2:C50", operator="greaterThanOrEqual", formula1="1"),
dv("Number", type="decimal", ref="D2:D50", operator="greaterThan", formula1="0"),
dv("Number", type="whole", ref="E2:E50", operator="notEqual", formula1="13"),
]
doc.batch(items)
# ======================================================================
# Sheet3: Date & Time
# ======================================================================
print("--- Sheet3: Date & Time ---")
items = [add_sheet("DateTime"),
hdr("DateTime", "A1", "Event date (2024)"), hdr("DateTime", "B1", "Shift start (9-17)"),
hdr("DateTime", "C1", "Deadline (=EOY)"), hdr("DateTime", "D1", "Ship after")]
items += col("DateTime", "A", 2, ["2024-03-15", "2024-07-01"])
items += col("DateTime", "B", 2, ["10:30:00", "14:00:00"])
items += col("DateTime", "D", 2, ["2024-06-01"])
items += [
dv("DateTime", type="date", ref="A2:A50", operator="between",
formula1="2024-01-01", formula2="2024-12-31"), # bounds stored as serials
dv("DateTime", type="time", ref="B2:B50", operator="between",
formula1="09:00:00", formula2="17:00:00"), # bounds stored as day fractions
dv("DateTime", type="date", ref="C2:C50", operator="equal", formula1="2024-12-31"),
dv("DateTime", type="date", ref="D2:D50", operator="greaterThan", formula1="2024-01-01"),
]
doc.batch(items)
# ======================================================================
# Sheet4: Text length
# ======================================================================
print("--- Sheet4: Text length ---")
items = [add_sheet("TextLength"),
hdr("TextLength", "A1", "Username (3-16)"), hdr("TextLength", "B1", "Country code (=2)"),
hdr("TextLength", "C1", "Tweet (<=280)"), hdr("TextLength", "D1", "PIN (not 5-7)")]
items += col("TextLength", "A", 2, ["alice", "bob_smith"])
items += col("TextLength", "B", 2, ["US", "GB"])
items += col("TextLength", "C", 2, ["hello world"])
items += col("TextLength", "D", 2, ["1234", "12345678"])
items += [
dv("TextLength", type="textLength", ref="A2:A50", operator="between", formula1="3", formula2="16"),
dv("TextLength", type="textLength", ref="B2:B50", operator="equal", formula1="2"),
dv("TextLength", type="textLength", ref="C2:C50", operator="lessThanOrEqual", formula1="280"),
dv("TextLength", type="textLength", ref="D2:D50", operator="notBetween", formula1="5", formula2="7"),
]
doc.batch(items)
# ======================================================================
# Sheet5: Custom formula
# ======================================================================
print("--- Sheet5: Custom formula ---")
items = [add_sheet("Custom"),
hdr("Custom", "A1", "Must be number"), hdr("Custom", "B1", "Even only"),
hdr("Custom", "C1", "No spaces")]
items += col("Custom", "A", 2, [42, 3.14])
items += col("Custom", "B", 2, [2, 4])
items += col("Custom", "C", 2, ["nospace", "ok"])
items += [
dv("Custom", type="custom", ref="A2:A50", formula1="ISNUMBER(A2)"),
dv("Custom", type="custom", ref="B2:B50", formula1="MOD(B2,2)=0"),
dv("Custom", type="custom", ref="C2:C50", formula1='ISERROR(FIND(" ",C2))'),
]
doc.batch(items)
# ======================================================================
# Sheet6: Messages — input prompt, error message, all three errorStyles
# ======================================================================
print("--- Sheet6: Messages (prompt / error / errorStyle) ---")
items = [add_sheet("Messages"),
hdr("Messages", "A1", "Age (stop)"), hdr("Messages", "B1", "Budget (warning)"),
hdr("Messages", "C1", "Note (information)"), hdr("Messages", "D1", "Allow blank=false")]
items += [
# errorStyle=stop — hard block; full input prompt + error message
dv("Messages", type="whole", ref="A2:A50", operator="between", formula1="18", formula2="120",
promptTitle="Enter age", prompt="Age must be 18-120", showInput="true",
errorTitle="Invalid age", error="Please enter a whole number 18-120", showError="true",
errorStyle="stop"),
# errorStyle=warning — soft block, user may override
dv("Messages", type="decimal", ref="B2:B50", operator="lessThanOrEqual", formula1="10000",
promptTitle="Budget cap", prompt="Suggested max is 10,000",
errorTitle="Over budget", error="That exceeds the cap — continue anyway?",
errorStyle="warning"),
# errorStyle=information — advisory only, never blocks
dv("Messages", type="textLength", ref="C2:C50", operator="lessThanOrEqual", formula1="200",
promptTitle="Note", prompt="Keep notes under 200 chars",
errorTitle="Long note", error="This note is quite long.",
errorStyle="information"),
# allowBlank=false (empty cells invalid) + showInput=false (no prompt bubble)
dv("Messages", type="whole", ref="D2:D50", operator="greaterThan", formula1="0",
allowBlank="false", showInput="false", showError="true",
errorTitle="Required", error="This field is required and must be > 0"),
]
doc.batch(items)
# ======================================================================
# Get round-trip: confirm canonical keys read back (in-session, over pipe)
# ======================================================================
print("\n--- Round-trip readback (Get the validations) ---")
for path in ["/Sheet1/dataValidation[2]", "/DateTime/dataValidation[1]",
"/Custom/dataValidation[2]", "/Messages/dataValidation[2]"]:
node = doc.send({"command": "get", "path": path})
fmt = node.get("data", {}).get("results", [{}])[0].get("format", {})
keys = ("type", "ref", "operator", "formula1", "formula2", "errorStyle",
"prompt", "error", "inCellDropdown", "allowBlank")
shown = {k: fmt.get(k) for k in keys if k in fmt}
print(f" {path}: {shown}")
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
# Validate the SAVED file with a fresh one-shot process (NOT in-session):
# validations live in each sheet's <dataValidations> block, so validate from
# disk to confirm they serialized cleanly.
print("\n--- Validate (fresh process, from disk) ---")
r = subprocess.run(["officecli", "validate", FILE], capture_output=True, text=True)
print(" ", (r.stdout or r.stderr).strip().split("\n")[0])
print(f"\nCreated: {FILE}")
+165
View File
@@ -0,0 +1,165 @@
#!/bin/bash
# data-validation.sh — exercise the full xlsx `validation` (dataValidation)
# feature surface (schemas/help/xlsx/validation.json) using the officecli CLI.
#
# 6 sheets, one validation family each: list (inline + range), number
# (whole/decimal), date & time, text length, custom formula, and the
# input-prompt / error-message / errorStyle surface. CLI twin of
# data-validation.py (officecli SDK); both produce an equivalent
# data-validation.xlsx.
#
# Each rule is one `add --type validation` against the sheet, with type=
# selecting the rule kind and ref= (alias sqref) the target range. The rule
# lands at /SheetName/dataValidation[N].
# 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")/data-validation.xlsx"
echo "Building $FILE ..."
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE"
# helper: write a bold-blue header in <cell> then a column of values below it
hdr() { officecli set "$FILE" "/$1/$2" --prop value="$3" --prop font.bold=true --prop fill=1F4E79 --prop font.color=FFFFFF; }
col() { # col <sheet> <col-letter> <start-row> <v1> <v2> ...
local sheet="$1" c="$2" r="$3"; shift 3
for v in "$@"; do officecli set "$FILE" "/$sheet/$c$r" --prop value="$v"; r=$((r+1)); done
}
dv() { officecli add "$FILE" "/$1" --type validation "${@:2}"; } # dv <sheet> --prop ...
sheet() { officecli add "$FILE" / --type sheet --prop name="$1"; }
# ==========================================================================
# Sheet1: List — inline CSV list AND range-based list (helper column)
# ==========================================================================
# Sheet1 exists by default; rename the entry column headers + a helper list.
hdr Sheet1 A1 "Status (inline)"
hdr Sheet1 B1 "Priority (range)"
hdr Sheet1 H1 "Priorities"
col Sheet1 H 2 Low Medium High Critical # the range the B-column list points at
# Features: type=list, inline formula1 CSV, inCellDropdown default (dropdown arrow shown)
dv Sheet1 --prop type=list --prop ref=A2:A20 --prop formula1="Draft,Review,Approved,Rejected"
# Features: type=list, range-based formula1 (=$H$2:$H$5), sqref alias for ref, inCellDropdown=false (hide arrow)
dv Sheet1 --prop type=list --prop sqref=B2:B20 --prop 'formula1==$H$2:$H$5' --prop inCellDropdown=false
# ==========================================================================
# Sheet2: Number — whole & decimal with every comparison operator
# ==========================================================================
sheet Number
hdr Number A1 "Qty (whole)"
hdr Number B1 "Discount (decimal)"
hdr Number C1 "Rating (1-5)"
hdr Number D1 "Price (>0)"
hdr Number E1 "Not 13"
col Number A 2 1 5 10 20 # sample entries
col Number B 2 0.05 0.10 0.25
col Number C 2 1 3 5
col Number D 2 9.99 19.5
col Number E 2 12 14
# Features: type=whole, operator=between, formula1+formula2 (both bounds)
dv Number --prop type=whole --prop ref=A2:A50 --prop operator=between --prop formula1=1 --prop formula2=100
# Features: type=decimal, operator=lessThanOrEqual (single bound formula1)
dv Number --prop type=decimal --prop ref=B2:B50 --prop operator=lessThanOrEqual --prop formula1=0.5
# Features: type=whole, operator=greaterThanOrEqual
dv Number --prop type=whole --prop ref=C2:C50 --prop operator=greaterThanOrEqual --prop formula1=1
# Features: type=decimal, operator=greaterThan
dv Number --prop type=decimal --prop ref=D2:D50 --prop operator=greaterThan --prop formula1=0
# Features: type=whole, operator=notEqual (exclude a single value)
dv Number --prop type=whole --prop ref=E2:E50 --prop operator=notEqual --prop formula1=13
# ==========================================================================
# Sheet3: Date & Time — date range, time window, exact-equal date
# ==========================================================================
sheet DateTime
hdr DateTime A1 "Event date (2024)"
hdr DateTime B1 "Shift start (9-17)"
hdr DateTime C1 "Deadline (=EOY)"
hdr DateTime D1 "Ship after"
col DateTime A 2 2024-03-15 2024-07-01
col DateTime B 2 10:30:00 14:00:00
col DateTime D 2 2024-06-01
# Features: type=date, operator=between, formula1+formula2 date bounds (stored as serials)
dv DateTime --prop type=date --prop ref=A2:A50 --prop operator=between --prop formula1=2024-01-01 --prop formula2=2024-12-31
# Features: type=time, operator=between, formula1+formula2 time window (stored as day fractions)
dv DateTime --prop type=time --prop ref=B2:B50 --prop operator=between --prop formula1=09:00:00 --prop formula2=17:00:00
# Features: type=date, operator=equal (exact date)
dv DateTime --prop type=date --prop ref=C2:C50 --prop operator=equal --prop formula1=2024-12-31
# Features: type=date, operator=greaterThan (on/after a date)
dv DateTime --prop type=date --prop ref=D2:D50 --prop operator=greaterThan --prop formula1=2024-01-01
# ==========================================================================
# Sheet4: Text length — bounded, capped, exact, and a notBetween band
# ==========================================================================
sheet TextLength
hdr TextLength A1 "Username (3-16)"
hdr TextLength B1 "Country code (=2)"
hdr TextLength C1 "Tweet (<=280)"
hdr TextLength D1 "PIN (not 5-7)"
col TextLength A 2 alice bob_smith
col TextLength B 2 US GB
col TextLength C 2 "hello world"
col TextLength D 2 1234 12345678
# Features: type=textLength, operator=between, formula1+formula2 length bounds
dv TextLength --prop type=textLength --prop ref=A2:A50 --prop operator=between --prop formula1=3 --prop formula2=16
# Features: type=textLength, operator=equal (exact length)
dv TextLength --prop type=textLength --prop ref=B2:B50 --prop operator=equal --prop formula1=2
# Features: type=textLength, operator=lessThanOrEqual (cap)
dv TextLength --prop type=textLength --prop ref=C2:C50 --prop operator=lessThanOrEqual --prop formula1=280
# Features: type=textLength, operator=notBetween, formula1+formula2 excluded band
dv TextLength --prop type=textLength --prop ref=D2:D50 --prop operator=notBetween --prop formula1=5 --prop formula2=7
# ==========================================================================
# Sheet5: Custom formula — arbitrary boolean expressions
# ==========================================================================
sheet Custom
hdr Custom A1 "Must be number"
hdr Custom B1 "Even only"
hdr Custom C1 "No spaces"
col Custom A 2 42 3.14
col Custom B 2 2 4
col Custom C 2 nospace ok
# Features: type=custom, formula1 = boolean expression (allow only numeric entries)
dv Custom --prop type=custom --prop ref=A2:A50 --prop formula1="ISNUMBER(A2)"
# Features: type=custom, formula1 = MOD expression (allow only even numbers)
dv Custom --prop type=custom --prop ref=B2:B50 --prop formula1="MOD(B2,2)=0"
# Features: type=custom, formula1 = reject any value containing a space
dv Custom --prop type=custom --prop ref=C2:C50 --prop formula1="ISERROR(FIND(\" \",C2))"
# ==========================================================================
# Sheet6: Messages — input prompt, error message, and all three errorStyles
# ==========================================================================
sheet Messages
hdr Messages A1 "Age (stop)"
hdr Messages B1 "Budget (warning)"
hdr Messages C1 "Note (information)"
hdr Messages D1 "Allow blank=false"
# Features: prompt + promptTitle + showInput (input message shown when cell selected)
# + error + errorTitle + showError + errorStyle=stop (hard block)
dv Messages --prop type=whole --prop ref=A2:A50 --prop operator=between --prop formula1=18 --prop formula2=120 \
--prop promptTitle="Enter age" --prop prompt="Age must be 18-120" --prop showInput=true \
--prop errorTitle="Invalid age" --prop error="Please enter a whole number 18-120" --prop showError=true \
--prop errorStyle=stop
# Features: errorStyle=warning (soft block — user may override)
dv Messages --prop type=decimal --prop ref=B2:B50 --prop operator=lessThanOrEqual --prop formula1=10000 \
--prop promptTitle="Budget cap" --prop prompt="Suggested max is 10,000" \
--prop errorTitle="Over budget" --prop error="That exceeds the cap — continue anyway?" \
--prop errorStyle=warning
# Features: errorStyle=information (advisory only — never blocks)
dv Messages --prop type=textLength --prop ref=C2:C50 --prop operator=lessThanOrEqual --prop formula1=200 \
--prop promptTitle="Note" --prop prompt="Keep notes under 200 chars" \
--prop errorTitle="Long note" --prop error="This note is quite long." \
--prop errorStyle=information
# Features: allowBlank=false (empty cells are themselves invalid) + showInput=false (no prompt bubble)
dv Messages --prop type=whole --prop ref=D2:D50 --prop operator=greaterThan --prop formula1=0 \
--prop allowBlank=false --prop showInput=false --prop showError=true \
--prop errorTitle="Required" --prop error="This field is required and must be > 0"
officecli close "$FILE"
officecli validate "$FILE"
echo "Created: $FILE"
Binary file not shown.
+366
View File
@@ -0,0 +1,366 @@
# Pivot Table Showcase
This demo consists of three files that work together:
- **pivot-tables.py** — Python script that calls `officecli` commands to generate the workbook. Each pivot table command is shown as a copyable shell command in the comments, then executed by the script. Read this to learn the exact `officecli add --type pivottable --prop ...` syntax.
- **pivot-tables.xlsx** — The generated workbook with 19 sheets (Sheet1 + CNData + 17 pivot tables). Open in Excel to see the rendered pivot tables. Use `officecli get` or `officecli query` to inspect programmatically.
- **pivot-tables.md** — This file. Maps each sheet in the xlsx to the feature it demonstrates and the command that created it.
## Regenerate
```bash
cd examples/excel
python3 pivot-tables.py
# → pivot-tables.xlsx
```
## Source Data
| Sheet | Rows | Columns | Purpose |
|-------|------|---------|---------|
| Sheet1 | 50 | Region, Category, Product, Quarter, Sales, Quantity, Cost, Channel, Priority, Date | English sales data spanning 2024-2025 |
| CNData | 12 | 地区, 品类, 销售额 | Chinese sales data for locale sort demo |
## Pivot Tables
### Sheet: 1-Sales Overview
The most feature-rich pivot. Tabular layout with 2-level row hierarchy crossed against quarterly columns. Three value fields where Cost is shown as percentage of row total. Dual page filters let users slice by Channel and Priority. Outer row labels repeat on every row.
```bash
officecli add pivot-tables.xlsx "/1-Sales Overview" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop 'values=Sales:sum,Quantity:sum,Cost:sum:percent_of_row' \
--prop 'filters=Channel,Priority' \
--prop layout=tabular \
--prop repeatlabels=true \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=desc \
--prop style=PivotStyleDark2
```
**Features:** `layout=tabular`, `repeatlabels=true`, dual `filters`, `values` with `percent_of_row`, `sort=desc`
### Sheet: 2-Market Share
Each region's share within each category, shown as column percentages. Outline layout provides expand/collapse grouping.
```bash
officecli add pivot-tables.xlsx "/2-Market Share" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region \
--prop cols=Category \
--prop 'values=Sales:sum:percent_of_col' \
--prop filters=Channel \
--prop layout=outline \
--prop grandtotals=both \
--prop style=PivotStyleMedium4
```
**Features:** `layout=outline`, `values` with `percent_of_col`
### Sheet: 3-Product Deep Dive
Five value fields with three different aggregation functions on the same source column (Sales:sum, Sales:average, Sales:max). No column axis — values become column headers automatically.
```bash
officecli add pivot-tables.xlsx "/3-Product Deep Dive" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Category,Product \
--prop 'values=Sales:sum,Sales:average,Sales:max,Quantity:sum,Cost:sum' \
--prop filters=Region \
--prop layout=tabular \
--prop grandtotals=rows \
--prop subtotals=on \
--prop sort=desc \
--prop style=PivotStyleMedium9
```
**Features:** 5 `values` fields, no `cols` (synthetic Values axis), `grandtotals=rows`
### Sheet: 4-Channel Analysis
Sales shown as percentage of the grand total — reveals each channel's global share across quarters. No page filters.
```bash
officecli add pivot-tables.xlsx "/4-Channel Analysis" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Channel \
--prop cols=Quarter \
--prop 'values=Sales:sum:percent_of_total,Quantity:sum' \
--prop layout=outline \
--prop grandtotals=both \
--prop style=PivotStyleLight21
```
**Features:** `values` with `percent_of_total`, no `filters`
### Sheet: 5-Priority Matrix
Blank rows inserted after each outer group (Priority) for visual separation. Ascending sort puts High first.
```bash
officecli add pivot-tables.xlsx "/5-Priority Matrix" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Priority,Region \
--prop cols=Category \
--prop 'values=Sales:sum,Cost:sum:percent_of_row' \
--prop filters=Channel \
--prop layout=tabular \
--prop blankrows=true \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=asc \
--prop style=PivotStyleDark6
```
**Features:** `blankrows=true`, `sort=asc`
### Sheet: 6-Compact 3-Level
Three-level row hierarchy (Region > Category > Product) in compact layout — all labels share one column with progressive indentation.
```bash
officecli add pivot-tables.xlsx "/6-Compact 3-Level" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category,Product \
--prop 'values=Sales:sum,Quantity:sum' \
--prop filters=Priority \
--prop layout=compact \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=desc \
--prop style=PivotStyleMedium14
```
**Features:** `layout=compact`, 3-level `rows`
### Sheet: 7-No Subtotals
Flat tabular view with subtotals disabled. Only the bottom grand total row remains. Outer labels are repeated on every row since there are no subtotal rows to carry them.
```bash
officecli add pivot-tables.xlsx "/7-No Subtotals" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop values=Sales:sum \
--prop layout=tabular \
--prop repeatlabels=true \
--prop grandtotals=cols \
--prop subtotals=off \
--prop sort=asc \
--prop style=PivotStyleLight1
```
**Features:** `subtotals=off`, `grandtotals=cols`, `repeatlabels=true`
### Sheet: 8-Date Grouping
Automatic date grouping from a date column. `Date:year` creates year buckets ("2024", "2025"), `Date:quarter` creates quarter sub-buckets ("2024-Q1", ...). Uses native Excel fieldGroup XML.
```bash
officecli add pivot-tables.xlsx "/8-Date Grouping" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop 'rows=Date:year,Date:quarter' \
--prop 'values=Sales:sum,Cost:sum' \
--prop filters=Region \
--prop layout=outline \
--prop grandtotals=both \
--prop subtotals=on \
--prop style=PivotStyleMedium7
```
**Features:** `rows` with `Date:year,Date:quarter` date grouping syntax
### Sheet: 9-Top 5 Products
Only the top 5 products by sales are shown. Grand totals are hidden entirely.
```bash
officecli add pivot-tables.xlsx "/9-Top 5 Products" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Product \
--prop 'values=Sales:sum,Quantity:sum,Cost:sum' \
--prop layout=tabular \
--prop grandtotals=none \
--prop topN=5 \
--prop sort=desc \
--prop style=PivotStyleDark1
```
**Features:** `topN=5`, `grandtotals=none`
### Sheet: 10-Ultimate
Every feature combined in one pivot table — the kitchen sink.
```bash
officecli add pivot-tables.xlsx "/10-Ultimate" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop 'values=Sales:sum,Quantity:average,Cost:sum:percent_of_row' \
--prop 'filters=Channel,Priority' \
--prop layout=tabular \
--prop repeatlabels=true \
--prop blankrows=true \
--prop grandtotals=rows \
--prop subtotals=on \
--prop sort=desc \
--prop style=PivotStyleDark11
```
**Features:** `repeatlabels=true` + `blankrows=true` + dual `filters` + mixed aggregations + `grandtotals=rows`
### Sheet: 11-Chinese Locale
Chinese data with pinyin-order sorting and a custom grand total label. Demonstrates that field names, filter values, and captions all work with non-ASCII text.
```bash
officecli add pivot-tables.xlsx "/11-Chinese Locale" --type pivottable \
--prop source=CNData!A1:C13 \
--prop rows=地区,品类 \
--prop values=销售额:sum \
--prop layout=tabular \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=locale \
--prop grandTotalCaption=合计 \
--prop style=PivotStyleMedium2
```
**Features:** `sort=locale` (pinyin: 华北 < 华东 < 华南 < 西南), `grandTotalCaption`
### Sheet: 12-Position + Aggregates
Anchors the pivot at cell D2 instead of the auto-placed default. Demonstrates the less-common value aggregations (count, min, product, countNums) and the `aggregate=avg` default used when a value tuple omits its aggregation.
```bash
officecli add pivot-tables.xlsx "/12-Position + Aggregates" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop position=D2 \
--prop rows=Category \
--prop 'values=Sales:count,Quantity:min,Quantity:product,Sales:countNums' \
--prop aggregate=avg \
--prop layout=tabular \
--prop grandtotals=both \
--prop style=PivotStyleLight16
```
**Features:** `position=D2`, `aggregate=avg` (default agg), value aggs `count` / `min` / `product` / `countNums`
### Sheet: 13-Calculated Field
User-defined formula fields (`Margin = Sales - Cost`, `Tax = Sales * 0.1`) are auto-added as data fields — no need to list them in `values=`. A pre-cache `labelFilter` keeps only rows where Region begins with "N".
```bash
officecli add pivot-tables.xlsx "/13-Calculated Field" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop 'calculatedField1=Margin:=Sales-Cost' \
--prop 'calculatedField2=Tax:=Sales*0.1' \
--prop rows=Region \
--prop values=Sales:sum \
--prop 'labelFilter=Region:beginsWith:N' \
--prop layout=tabular \
--prop grandtotals=both \
--prop style=PivotStyleMedium3
```
**Features:** `calculatedField1` / `calculatedField2`, `labelFilter`
### Sheet: 14-Statistical
Completes the aggregate set with sample/population variance (`var` / `varP`). `showDataAs=running_total` is set as a standalone prop (vs the per-value `Field:agg:mode` tuple) and applies to all value fields as the default display.
```bash
officecli add pivot-tables.xlsx "/14-Statistical" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region \
--prop cols=Quarter \
--prop 'values=Sales:var,Sales:varP,Sales:sum' \
--prop showDataAs=running_total \
--prop layout=tabular \
--prop grandtotals=both \
--prop style=PivotStyleLight10
```
**Features:** `Sales:var`, `Sales:varP`, `showDataAs=running_total` (standalone)
### Sheet: 15-Independent Totals
Row and column grand totals toggled independently (vs the combined `grandtotals=both/rows/cols/none`). `defaultSubtotal=true` sets the default-subtotal flag on every pivotField. `sort=locale-desc` reverses pinyin order.
```bash
officecli add pivot-tables.xlsx "/15-Independent Totals" --type pivottable \
--prop source=CNData!A1:C13 \
--prop rows=地区 \
--prop cols=品类 \
--prop values=销售额:sum \
--prop rowGrandTotals=true \
--prop colGrandTotals=false \
--prop defaultSubtotal=true \
--prop layout=outline \
--prop subtotals=on \
--prop sort=locale-desc \
--prop style=PivotStyleMedium11
```
**Features:** `rowGrandTotals` / `colGrandTotals` (independent), `defaultSubtotal`, `sort=locale-desc`
### Sheet: 16-Style Flags
All five `pivotTableStyleInfo` flags wired up — row/col banding, row/col header emphasis, last-column highlight. These map directly to the checkboxes in Excel's PivotTable Styles ribbon.
```bash
officecli add pivot-tables.xlsx "/16-Style Flags" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop values=Sales:sum \
--prop showRowStripes=true \
--prop showColStripes=true \
--prop showRowHeaders=true \
--prop showColHeaders=true \
--prop showLastColumn=true \
--prop layout=tabular \
--prop grandtotals=both \
--prop style=PivotStyleMedium17
```
**Features:** `showRowStripes`, `showColStripes`, `showRowHeaders`, `showColHeaders`, `showLastColumn`
### Sheet: 17-Display Toggles
`showDrill=false` hides the +/- expand-collapse buttons on every field. `mergeLabels=true` merges and centers repeated outer-axis item cells (`<pivotTableDefinition mergeItem="1">`).
```bash
officecli add pivot-tables.xlsx "/17-Display Toggles" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop values=Sales:sum \
--prop showDrill=false \
--prop mergeLabels=true \
--prop layout=outline \
--prop grandtotals=both \
--prop subtotals=on \
--prop style=PivotStyleLight19
```
**Features:** `showDrill=false`, `mergeLabels=true`
## Inspect the Generated File
```bash
# List all pivot tables
officecli query pivot-tables.xlsx pivottable
# Get details of a specific pivot
officecli get pivot-tables.xlsx "/1-Sales Overview/pivottable[1]"
# View rendered data as text
officecli view pivot-tables.xlsx text --sheet "1-Sales Overview"
```
+702
View File
@@ -0,0 +1,702 @@
#!/usr/bin/env python3
"""
Pivot Table Showcase — generates pivot-tables.xlsx with 17 pivot tables.
Each pivot table demonstrates different officecli features.
See pivot-tables.md for a guide to each sheet in the generated file.
SDK twin of pivot-tables.sh (officecli CLI). Both produce an equivalent
pivot-tables.xlsx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started, the 500+ source-data
cell writes ship in a single `doc.batch(...)` round-trip, and each pivot table
is one `add pivottable` item shipped over the named pipe. Each item is the same
`{"command","parent","type","props"}` dict you'd put in an `officecli batch` list.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 pivot-tables.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "pivot-tables.xlsx")
def add_sheet(name):
"""One `add sheet` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "sheet", "props": {"name": name}}
def pivot(sheet, **props):
"""One `add pivottable` item in batch-shape, anchored on the given sheet."""
return {"command": "add", "parent": f"/{sheet}", "type": "pivottable", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ==========================================================================
# Source data — batch is used here only for speed (500+ cell writes).
# ==========================================================================
print("\n--- Populating source data ---")
data_items = []
for j, h in enumerate(["Region", "Category", "Product", "Quarter", "Sales",
"Quantity", "Cost", "Channel", "Priority", "Date"]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCDEFGHIJ'[j]}1",
"props": {"text": h}})
rows = [
("North", "Electronics", "Laptop", "Q1", 12500, 45, 7500, "Online", "High", "2025-01-15"),
("North", "Electronics", "Phone", "Q1", 8900, 120, 5340, "Retail", "High", "2025-02-10"),
("North", "Electronics", "Tablet", "Q2", 6200, 38, 3720, "Online", "Medium", "2025-04-20"),
("North", "Electronics", "Laptop", "Q2", 15800, 55, 9480, "Retail", "High", "2025-05-08"),
("North", "Electronics", "Phone", "Q3", 11200, 150, 6720, "Online", "High", "2025-07-12"),
("North", "Electronics", "Tablet", "Q4", 9500, 62, 5700, "Retail", "Medium", "2025-10-05"),
("North", "Clothing", "Jacket", "Q1", 4200, 85, 2100, "Retail", "Low", "2025-01-22"),
("North", "Clothing", "Shoes", "Q2", 5600, 70, 2800, "Online", "Medium", "2025-04-15"),
("North", "Clothing", "Hat", "Q3", 1800, 110, 900, "Retail", "Low", "2025-08-03"),
("North", "Clothing", "Jacket", "Q4", 7800, 95, 3900, "Online", "High", "2025-11-18"),
("North", "Food", "Coffee", "Q1", 2400, 200, 1200, "Retail", "Low", "2025-03-01"),
("North", "Food", "Snacks", "Q2", 1500, 180, 750, "Online", "Low", "2025-06-10"),
("North", "Food", "Juice", "Q3", 1900, 160, 950, "Retail", "Medium", "2025-09-20"),
("North", "Food", "Coffee", "Q4", 3200, 220, 1600, "Online", "Medium", "2025-12-01"),
("South", "Electronics", "Phone", "Q1", 18500, 200, 11100, "Online", "High", "2024-01-20"),
("South", "Electronics", "Laptop", "Q2", 22000, 72, 13200, "Retail", "High", "2024-05-15"),
("South", "Electronics", "Tablet", "Q3", 7800, 48, 4680, "Online", "Medium", "2024-08-22"),
("South", "Electronics", "Phone", "Q4", 14200, 165, 8520, "Retail", "High", "2024-11-30"),
("South", "Clothing", "Shoes", "Q1", 9200, 110, 4600, "Retail", "Medium", "2024-02-14"),
("South", "Clothing", "Jacket", "Q2", 6500, 78, 3250, "Online", "Low", "2024-06-01"),
("South", "Clothing", "Hat", "Q3", 3100, 130, 1550, "Retail", "Low", "2024-09-10"),
("South", "Clothing", "Shoes", "Q4", 8800, 98, 4400, "Online", "Medium", "2024-12-20"),
("South", "Food", "Juice", "Q1", 1800, 240, 900, "Retail", "Low", "2024-03-08"),
("South", "Food", "Coffee", "Q2", 3500, 280, 1750, "Online", "Medium", "2024-04-25"),
("South", "Food", "Snacks", "Q3", 2200, 190, 1100, "Retail", "Low", "2024-07-14"),
("South", "Food", "Juice", "Q4", 2800, 210, 1400, "Online", "Medium", "2024-10-18"),
("East", "Electronics", "Tablet", "Q1", 5400, 35, 3240, "Online", "Medium", "2025-02-28"),
("East", "Electronics", "Laptop", "Q2", 19500, 65, 11700, "Retail", "High", "2025-05-20"),
("East", "Electronics", "Phone", "Q3", 13800, 180, 8280, "Online", "High", "2025-08-15"),
("East", "Electronics", "Tablet", "Q4", 8200, 52, 4920, "Retail", "Medium", "2025-11-02"),
("East", "Clothing", "Hat", "Q1", 2800, 140, 1400, "Retail", "Low", "2025-01-05"),
("East", "Clothing", "Jacket", "Q2", 7200, 60, 3600, "Online", "Medium", "2025-06-18"),
("East", "Clothing", "Shoes", "Q3", 5500, 88, 2750, "Retail", "Medium", "2025-09-25"),
("East", "Clothing", "Hat", "Q4", 3600, 105, 1800, "Online", "Low", "2025-12-10"),
("East", "Food", "Snacks", "Q1", 1200, 300, 600, "Retail", "Low", "2025-03-15"),
("East", "Food", "Juice", "Q2", 2100, 170, 1050, "Online", "Medium", "2025-04-30"),
("East", "Food", "Coffee", "Q3", 2800, 230, 1400, "Retail", "Medium", "2025-07-22"),
("East", "Food", "Snacks", "Q4", 1600, 250, 800, "Online", "Low", "2025-10-28"),
("West", "Electronics", "Laptop", "Q1", 20500, 68, 12300, "Online", "High", "2024-01-10"),
("West", "Electronics", "Phone", "Q2", 16800, 190, 10080, "Retail", "High", "2024-04-05"),
("West", "Electronics", "Tablet", "Q3", 8900, 55, 5340, "Online", "Medium", "2024-08-12"),
("West", "Electronics", "Laptop", "Q4", 25000, 82, 15000, "Retail", "High", "2024-11-15"),
("West", "Clothing", "Jacket", "Q1", 11000, 88, 5500, "Retail", "Medium", "2024-02-22"),
("West", "Clothing", "Shoes", "Q2", 7500, 95, 3750, "Online", "Medium", "2024-05-30"),
("West", "Clothing", "Hat", "Q3", 4200, 120, 2100, "Retail", "Low", "2024-09-08"),
("West", "Clothing", "Jacket", "Q4", 13500, 105, 6750, "Online", "High", "2024-12-01"),
("West", "Food", "Coffee", "Q1", 4500, 350, 2250, "Online", "Medium", "2024-03-18"),
("West", "Food", "Snacks", "Q2", 2800, 280, 1400, "Online", "Medium", "2024-06-22"),
("West", "Food", "Juice", "Q3", 3200, 260, 1600, "Retail", "Low", "2024-07-30"),
("West", "Food", "Coffee", "Q4", 5800, 400, 2900, "Online", "High", "2024-10-25"),
]
C = "ABCDEFGHIJ"
for i, row in enumerate(rows):
for j, val in enumerate(row):
data_items.append({"command": "set", "path": f"/Sheet1/{C[j]}{i+2}",
"props": {"text": str(val)}})
data_items.append(add_sheet("CNData"))
for j, h in enumerate(["地区", "品类", "销售额"]):
data_items.append({"command": "set", "path": f"/CNData/{C[j]}1",
"props": {"text": h}})
for i, (r, c, s) in enumerate([
("华东", "电子产品", 18000), ("华东", "服装", 9500), ("华东", "食品", 4200),
("华南", "电子产品", 22000), ("华南", "服装", 12000), ("华南", "食品", 5800),
("华北", "电子产品", 15000), ("华北", "服装", 7800), ("华北", "食品", 3600),
("西南", "电子产品", 11000), ("西南", "服装", 6500), ("西南", "食品", 2900),
]):
for j, val in enumerate([r, c, s]):
data_items.append({"command": "set", "path": f"/CNData/{C[j]}{i+2}",
"props": {"text": str(val)}})
doc.batch(data_items)
# ==========================================================================
# 17 Pivot Tables
#
# Each section below shows the exact officecli command in a comment block,
# then ships it as an `add pivottable` batch item. You can copy any command
# block and run it in a terminal.
# ==========================================================================
# --------------------------------------------------------------------------
# Sheet: 1-Sales Overview
#
# officecli add pivot-tables.xlsx "/1-Sales Overview" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region,Category \
# --prop cols=Quarter \
# --prop 'values=Sales:sum,Quantity:sum,Cost:sum:percent_of_row' \
# --prop 'filters=Channel,Priority' \
# --prop layout=tabular \
# --prop repeatlabels=true \
# --prop grandtotals=both \
# --prop subtotals=on \
# --prop sort=desc \
# --prop name=SalesOverview \
# --prop style=PivotStyleDark2
#
# Features: tabular layout, 2-level rows, column axis, 3 value fields,
# Cost as percent_of_row, dual page filters, repeat item labels, desc sort
# --------------------------------------------------------------------------
print("\n--- 1-Sales Overview ---")
doc.send(add_sheet("1-Sales Overview"))
doc.send(pivot("1-Sales Overview",
source="Sheet1!A1:J51",
rows="Region,Category",
cols="Quarter",
values="Sales:sum,Quantity:sum,Cost:sum:percent_of_row",
filters="Channel,Priority",
layout="tabular",
repeatlabels="true",
grandtotals="both",
subtotals="on",
sort="desc",
name="SalesOverview",
style="PivotStyleDark2"))
# --------------------------------------------------------------------------
# Sheet: 2-Market Share
#
# officecli add pivot-tables.xlsx "/2-Market Share" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region \
# --prop cols=Category \
# --prop 'values=Sales:sum:percent_of_col' \
# --prop filters=Channel \
# --prop layout=outline \
# --prop grandtotals=both \
# --prop name=MarketShare \
# --prop style=PivotStyleMedium4
#
# Features: outline layout, percent_of_col (each region's share per category)
# --------------------------------------------------------------------------
print("\n--- 2-Market Share ---")
doc.send(add_sheet("2-Market Share"))
doc.send(pivot("2-Market Share",
source="Sheet1!A1:J51",
rows="Region",
cols="Category",
values="Sales:sum:percent_of_col",
filters="Channel",
layout="outline",
grandtotals="both",
name="MarketShare",
style="PivotStyleMedium4"))
# --------------------------------------------------------------------------
# Sheet: 3-Product Deep Dive
#
# officecli add pivot-tables.xlsx "/3-Product Deep Dive" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Category,Product \
# --prop 'values=Sales:sum,Sales:average,Sales:max,Quantity:sum,Cost:sum' \
# --prop filters=Region \
# --prop layout=tabular \
# --prop grandtotals=rows \
# --prop subtotals=on \
# --prop sort=desc \
# --prop name=ProductDeepDive \
# --prop style=PivotStyleMedium9
#
# Features: 5 value fields (sum, average, max), no column axis — values
# become column headers via synthetic "Values" axis, row grand totals only
# --------------------------------------------------------------------------
print("\n--- 3-Product Deep Dive ---")
doc.send(add_sheet("3-Product Deep Dive"))
doc.send(pivot("3-Product Deep Dive",
source="Sheet1!A1:J51",
rows="Category,Product",
values="Sales:sum,Sales:average,Sales:max,Quantity:sum,Cost:sum",
filters="Region",
layout="tabular",
grandtotals="rows",
subtotals="on",
sort="desc",
name="ProductDeepDive",
style="PivotStyleMedium9"))
# --------------------------------------------------------------------------
# Sheet: 4-Channel Analysis
#
# officecli add pivot-tables.xlsx "/4-Channel Analysis" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Channel \
# --prop cols=Quarter \
# --prop 'values=Sales:sum:percent_of_total,Quantity:sum' \
# --prop layout=outline \
# --prop grandtotals=both \
# --prop name=ChannelTrend \
# --prop style=PivotStyleLight21
#
# Features: percent_of_total (global share), no filters
# --------------------------------------------------------------------------
print("\n--- 4-Channel Analysis ---")
doc.send(add_sheet("4-Channel Analysis"))
doc.send(pivot("4-Channel Analysis",
source="Sheet1!A1:J51",
rows="Channel",
cols="Quarter",
values="Sales:sum:percent_of_total,Quantity:sum",
layout="outline",
grandtotals="both",
name="ChannelTrend",
style="PivotStyleLight21"))
# --------------------------------------------------------------------------
# Sheet: 5-Priority Matrix
#
# officecli add pivot-tables.xlsx "/5-Priority Matrix" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Priority,Region \
# --prop cols=Category \
# --prop 'values=Sales:sum,Cost:sum:percent_of_row' \
# --prop filters=Channel \
# --prop layout=tabular \
# --prop blankrows=true \
# --prop grandtotals=both \
# --prop subtotals=on \
# --prop sort=asc \
# --prop name=PriorityMatrix \
# --prop style=PivotStyleDark6
#
# Features: blankRows — empty line after each outer group for visual separation
# --------------------------------------------------------------------------
print("\n--- 5-Priority Matrix ---")
doc.send(add_sheet("5-Priority Matrix"))
doc.send(pivot("5-Priority Matrix",
source="Sheet1!A1:J51",
rows="Priority,Region",
cols="Category",
values="Sales:sum,Cost:sum:percent_of_row",
filters="Channel",
layout="tabular",
blankrows="true",
grandtotals="both",
subtotals="on",
sort="asc",
name="PriorityMatrix",
style="PivotStyleDark6"))
# --------------------------------------------------------------------------
# Sheet: 6-Compact 3-Level
#
# officecli add pivot-tables.xlsx "/6-Compact 3-Level" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region,Category,Product \
# --prop 'values=Sales:sum,Quantity:sum' \
# --prop filters=Priority \
# --prop layout=compact \
# --prop grandtotals=both \
# --prop subtotals=on \
# --prop sort=desc \
# --prop name=Compact3Level \
# --prop style=PivotStyleMedium14
#
# Features: compact layout — 3-level hierarchy in one indented column
# --------------------------------------------------------------------------
print("\n--- 6-Compact 3-Level ---")
doc.send(add_sheet("6-Compact 3-Level"))
doc.send(pivot("6-Compact 3-Level",
source="Sheet1!A1:J51",
rows="Region,Category,Product",
values="Sales:sum,Quantity:sum",
filters="Priority",
layout="compact",
grandtotals="both",
subtotals="on",
sort="desc",
name="Compact3Level",
style="PivotStyleMedium14"))
# --------------------------------------------------------------------------
# Sheet: 7-No Subtotals
#
# officecli add pivot-tables.xlsx "/7-No Subtotals" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region,Category \
# --prop cols=Quarter \
# --prop values=Sales:sum \
# --prop layout=tabular \
# --prop repeatlabels=true \
# --prop grandtotals=cols \
# --prop subtotals=off \
# --prop sort=asc \
# --prop name=FlatView \
# --prop style=PivotStyleLight1
#
# Features: subtotals=off (flat view), grandtotals=cols (bottom row only),
# repeatlabels=true (essential when subtotals off — otherwise outer labels vanish)
# --------------------------------------------------------------------------
print("\n--- 7-No Subtotals ---")
doc.send(add_sheet("7-No Subtotals"))
doc.send(pivot("7-No Subtotals",
source="Sheet1!A1:J51",
rows="Region,Category",
cols="Quarter",
values="Sales:sum",
layout="tabular",
repeatlabels="true",
grandtotals="cols",
subtotals="off",
sort="asc",
name="FlatView",
style="PivotStyleLight1"))
# --------------------------------------------------------------------------
# Sheet: 8-Date Grouping
#
# officecli add pivot-tables.xlsx "/8-Date Grouping" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop 'rows=Date:year,Date:quarter' \
# --prop 'values=Sales:sum,Cost:sum' \
# --prop filters=Region \
# --prop layout=outline \
# --prop grandtotals=both \
# --prop subtotals=on \
# --prop name=DateGrouping \
# --prop style=PivotStyleMedium7
#
# Features: automatic date grouping — Date:year creates "2024","2025" buckets,
# Date:quarter creates "2024-Q1",... sub-buckets. Uses native Excel fieldGroup XML.
# --------------------------------------------------------------------------
print("\n--- 8-Date Grouping ---")
doc.send(add_sheet("8-Date Grouping"))
doc.send(pivot("8-Date Grouping",
source="Sheet1!A1:J51",
rows="Date:year,Date:quarter",
values="Sales:sum,Cost:sum",
filters="Region",
layout="outline",
grandtotals="both",
subtotals="on",
name="DateGrouping",
style="PivotStyleMedium7"))
# --------------------------------------------------------------------------
# Sheet: 9-Top 5 Products
#
# officecli add pivot-tables.xlsx "/9-Top 5 Products" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Product \
# --prop 'values=Sales:sum,Quantity:sum,Cost:sum' \
# --prop layout=tabular \
# --prop grandtotals=none \
# --prop topN=5 \
# --prop sort=desc \
# --prop name=Top5Products \
# --prop style=PivotStyleDark1
#
# Features: topN=5 (only top 5 products by first value field), grandtotals=none
# --------------------------------------------------------------------------
print("\n--- 9-Top 5 Products ---")
doc.send(add_sheet("9-Top 5 Products"))
doc.send(pivot("9-Top 5 Products",
source="Sheet1!A1:J51",
rows="Product",
values="Sales:sum,Quantity:sum,Cost:sum",
layout="tabular",
grandtotals="none",
topN="5",
sort="desc",
name="Top5Products",
style="PivotStyleDark1"))
# --------------------------------------------------------------------------
# Sheet: 10-Ultimate
#
# officecli add pivot-tables.xlsx "/10-Ultimate" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region,Category \
# --prop cols=Quarter \
# --prop 'values=Sales:sum,Quantity:average,Cost:sum:percent_of_row' \
# --prop 'filters=Channel,Priority' \
# --prop layout=tabular \
# --prop repeatlabels=true \
# --prop blankrows=true \
# --prop grandtotals=rows \
# --prop subtotals=on \
# --prop sort=desc \
# --prop name=UltimatePivot \
# --prop style=PivotStyleDark11
#
# Features: ALL features combined — tabular + repeatLabels + blankRows +
# dual filters + 3 mixed-aggregation values + row-only grand totals
# --------------------------------------------------------------------------
print("\n--- 10-Ultimate ---")
doc.send(add_sheet("10-Ultimate"))
doc.send(pivot("10-Ultimate",
source="Sheet1!A1:J51",
rows="Region,Category",
cols="Quarter",
values="Sales:sum,Quantity:average,Cost:sum:percent_of_row",
filters="Channel,Priority",
layout="tabular",
repeatlabels="true",
blankrows="true",
grandtotals="rows",
subtotals="on",
sort="desc",
name="UltimatePivot",
style="PivotStyleDark11"))
# --------------------------------------------------------------------------
# Sheet: 11-Chinese Locale
#
# officecli add pivot-tables.xlsx "/11-Chinese Locale" --type pivottable \
# --prop source=CNData!A1:C13 \
# --prop rows=地区,品类 \
# --prop values=销售额:sum \
# --prop layout=tabular \
# --prop grandtotals=both \
# --prop subtotals=on \
# --prop sort=locale \
# --prop grandTotalCaption=合计 \
# --prop name=ChineseLocale \
# --prop style=PivotStyleMedium2
#
# Features: sort=locale (Chinese pinyin: 华北 < 华东 < 华南 < 西南),
# grandTotalCaption=合计 (custom grand total label)
# --------------------------------------------------------------------------
print("\n--- 11-Chinese Locale ---")
doc.send(add_sheet("11-Chinese Locale"))
doc.send(pivot("11-Chinese Locale",
source="CNData!A1:C13",
rows="地区,品类",
values="销售额:sum",
layout="tabular",
grandtotals="both",
subtotals="on",
sort="locale",
grandTotalCaption="合计",
name="ChineseLocale",
style="PivotStyleMedium2"))
# --------------------------------------------------------------------------
# Sheet: 12-Position + Aggregates
#
# officecli add pivot-tables.xlsx "/12-Position + Aggregates" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop position=D2 \
# --prop rows=Category \
# --prop 'values=Sales:count,Quantity:min,Quantity:product,Sales:countNums' \
# --prop aggregate=avg \
# --prop layout=tabular \
# --prop grandtotals=both \
# --prop name=PositionAggs \
# --prop style=PivotStyleLight16
#
# Features: position=D2 (anchor cell override, default is auto-place after source),
# aggregate=avg (default agg when omitted from a value tuple),
# value aggregations: count, min, product, countNums (sum/avg/max shown elsewhere)
# --------------------------------------------------------------------------
print("\n--- 12-Position + Aggregates ---")
doc.send(add_sheet("12-Position + Aggregates"))
doc.send(pivot("12-Position + Aggregates",
source="Sheet1!A1:J51",
position="D2",
rows="Category",
values="Sales:count,Quantity:min,Quantity:product,Sales:countNums",
aggregate="avg",
layout="tabular",
grandtotals="both",
name="PositionAggs",
style="PivotStyleLight16"))
# --------------------------------------------------------------------------
# Sheet: 13-Calculated Field
#
# officecli add pivot-tables.xlsx "/13-Calculated Field" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop 'calculatedField1=Margin:=Sales-Cost' \
# --prop 'calculatedField2=Tax:=Sales*0.1' \
# --prop rows=Region \
# --prop values=Sales:sum \
# --prop 'labelFilter=Region:beginsWith:N' \
# --prop layout=tabular \
# --prop grandtotals=both \
# --prop name=CalcField \
# --prop style=PivotStyleMedium3
#
# Features: calculatedField1/2 — user-defined formula fields auto-added as
# data fields (no need to mention in values=). labelFilter — pre-cache row
# filter ('Region:beginsWith:N' keeps only Region values starting with N).
# --------------------------------------------------------------------------
print("\n--- 13-Calculated Field ---")
doc.send(add_sheet("13-Calculated Field"))
doc.send(pivot("13-Calculated Field",
source="Sheet1!A1:J51",
calculatedField1="Margin:=Sales-Cost",
calculatedField2="Tax:=Sales*0.1",
rows="Region",
values="Sales:sum",
labelFilter="Region:beginsWith:N",
layout="tabular",
grandtotals="both",
name="CalcField",
style="PivotStyleMedium3"))
# --------------------------------------------------------------------------
# Sheet: 14-Statistical
#
# officecli add pivot-tables.xlsx "/14-Statistical" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region \
# --prop cols=Quarter \
# --prop 'values=Sales:var,Sales:varP,Sales:sum' \
# --prop showDataAs=running_total \
# --prop layout=tabular \
# --prop grandtotals=both \
# --prop name=Statistical \
# --prop style=PivotStyleLight10
#
# Features: var / varP (sample + population variance) — completes the aggregate
# set. showDataAs=running_total as a standalone --prop (vs the tuple form
# 'Field:agg:mode'); applies as default display for all value fields.
# --------------------------------------------------------------------------
print("\n--- 14-Statistical ---")
doc.send(add_sheet("14-Statistical"))
doc.send(pivot("14-Statistical",
source="Sheet1!A1:J51",
rows="Region",
cols="Quarter",
values="Sales:var,Sales:varP,Sales:sum",
showDataAs="running_total",
layout="tabular",
grandtotals="both",
name="Statistical",
style="PivotStyleLight10"))
# --------------------------------------------------------------------------
# Sheet: 15-Independent Totals
#
# officecli add pivot-tables.xlsx "/15-Independent Totals" --type pivottable \
# --prop source=CNData!A1:C13 \
# --prop rows=地区 \
# --prop cols=品类 \
# --prop values=销售额:sum \
# --prop rowGrandTotals=true \
# --prop colGrandTotals=false \
# --prop defaultSubtotal=true \
# --prop layout=outline \
# --prop subtotals=on \
# --prop sort=locale-desc \
# --prop name=IndepTotals \
# --prop style=PivotStyleMedium11
#
# Features: rowGrandTotals + colGrandTotals as independent toggles
# (vs the combined grandtotals=both/rows/cols/none), defaultSubtotal=true
# (default-subtotal flag on every pivotField), sort=locale-desc (reverse
# pinyin: 西南 > 华南 > 华东 > 华北).
# --------------------------------------------------------------------------
print("\n--- 15-Independent Totals ---")
doc.send(add_sheet("15-Independent Totals"))
doc.send(pivot("15-Independent Totals",
source="CNData!A1:C13",
rows="地区",
cols="品类",
values="销售额:sum",
rowGrandTotals="true",
colGrandTotals="false",
defaultSubtotal="true",
layout="outline",
subtotals="on",
sort="locale-desc",
name="IndepTotals",
style="PivotStyleMedium11"))
# --------------------------------------------------------------------------
# Sheet: 16-Style Flags
#
# officecli add pivot-tables.xlsx "/16-Style Flags" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region,Category \
# --prop cols=Quarter \
# --prop values=Sales:sum \
# --prop showRowStripes=true \
# --prop showColStripes=true \
# --prop showRowHeaders=true \
# --prop showColHeaders=true \
# --prop showLastColumn=true \
# --prop layout=tabular \
# --prop grandtotals=both \
# --prop name=StyleFlags \
# --prop style=PivotStyleMedium17
#
# Features: every pivotTableStyleInfo flag wired up — row/col banding,
# row/col header emphasis, last-column highlight. These map to the five
# checkboxes in Excel's PivotTable Styles ribbon.
# --------------------------------------------------------------------------
print("\n--- 16-Style Flags ---")
doc.send(add_sheet("16-Style Flags"))
doc.send(pivot("16-Style Flags",
source="Sheet1!A1:J51",
rows="Region,Category",
cols="Quarter",
values="Sales:sum",
showRowStripes="true",
showColStripes="true",
showRowHeaders="true",
showColHeaders="true",
showLastColumn="true",
layout="tabular",
grandtotals="both",
name="StyleFlags",
style="PivotStyleMedium17"))
# --------------------------------------------------------------------------
# Sheet: 17-Display Toggles
#
# officecli add pivot-tables.xlsx "/17-Display Toggles" --type pivottable \
# --prop source=Sheet1!A1:J51 \
# --prop rows=Region,Category \
# --prop values=Sales:sum \
# --prop showDrill=false \
# --prop mergeLabels=true \
# --prop layout=outline \
# --prop grandtotals=both \
# --prop subtotals=on \
# --prop name=DisplayToggles \
# --prop style=PivotStyleLight19
#
# Features: showDrill=false (hide +/- expand-collapse buttons on every field),
# mergeLabels=true (merge & center repeated outer-axis item cells —
# <pivotTableDefinition mergeItem='1'>).
# --------------------------------------------------------------------------
print("\n--- 17-Display Toggles ---")
doc.send(add_sheet("17-Display Toggles"))
doc.send(pivot("17-Display Toggles",
source="Sheet1!A1:J51",
rows="Region,Category",
values="Sales:sum",
showDrill="false",
mergeLabels="true",
layout="outline",
grandtotals="both",
subtotals="on",
name="DisplayToggles",
style="PivotStyleLight19"))
doc.send({"command": "save"})
print(f"\nDone! Generated: {FILE}")
print(" 19 sheets (Sheet1 + CNData + 17 pivot tables)")
+401
View File
@@ -0,0 +1,401 @@
#!/bin/bash
# Pivot Table Showcase — generates pivot-tables.xlsx with 17 pivot tables.
# CLI twin of pivot-tables.py (officecli Python SDK). Both produce an
# equivalent pivot-tables.xlsx. See pivot-tables.md for a per-sheet guide.
#
# Usage: ./pivot-tables.sh [officecli path]
# 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.
CLI="${1:-officecli}"
FILE="$(dirname "$0")/pivot-tables.xlsx"
rm -f "$FILE"
$CLI create "$FILE"
$CLI open "$FILE"
# ==========================================================================
# Source data
# ==========================================================================
$CLI set "$FILE" /Sheet1/A1 --prop text=Region
$CLI set "$FILE" /Sheet1/B1 --prop text=Category
$CLI set "$FILE" /Sheet1/C1 --prop text=Product
$CLI set "$FILE" /Sheet1/D1 --prop text=Quarter
$CLI set "$FILE" /Sheet1/E1 --prop text=Sales
$CLI set "$FILE" /Sheet1/F1 --prop text=Quantity
$CLI set "$FILE" /Sheet1/G1 --prop text=Cost
$CLI set "$FILE" /Sheet1/H1 --prop text=Channel
$CLI set "$FILE" /Sheet1/I1 --prop text=Priority
$CLI set "$FILE" /Sheet1/J1 --prop text=Date
# rows: Region Category Product Quarter Sales Quantity Cost Channel Priority Date
write_row() {
local r="$1"; shift
local cols=(A B C D E F G H I J)
local i=0
for v in "$@"; do
$CLI set "$FILE" "/Sheet1/${cols[$i]}${r}" --prop text="$v"
i=$((i + 1))
done
}
write_row 2 North Electronics Laptop Q1 12500 45 7500 Online High 2025-01-15
write_row 3 North Electronics Phone Q1 8900 120 5340 Retail High 2025-02-10
write_row 4 North Electronics Tablet Q2 6200 38 3720 Online Medium 2025-04-20
write_row 5 North Electronics Laptop Q2 15800 55 9480 Retail High 2025-05-08
write_row 6 North Electronics Phone Q3 11200 150 6720 Online High 2025-07-12
write_row 7 North Electronics Tablet Q4 9500 62 5700 Retail Medium 2025-10-05
write_row 8 North Clothing Jacket Q1 4200 85 2100 Retail Low 2025-01-22
write_row 9 North Clothing Shoes Q2 5600 70 2800 Online Medium 2025-04-15
write_row 10 North Clothing Hat Q3 1800 110 900 Retail Low 2025-08-03
write_row 11 North Clothing Jacket Q4 7800 95 3900 Online High 2025-11-18
write_row 12 North Food Coffee Q1 2400 200 1200 Retail Low 2025-03-01
write_row 13 North Food Snacks Q2 1500 180 750 Online Low 2025-06-10
write_row 14 North Food Juice Q3 1900 160 950 Retail Medium 2025-09-20
write_row 15 North Food Coffee Q4 3200 220 1600 Online Medium 2025-12-01
write_row 16 South Electronics Phone Q1 18500 200 11100 Online High 2024-01-20
write_row 17 South Electronics Laptop Q2 22000 72 13200 Retail High 2024-05-15
write_row 18 South Electronics Tablet Q3 7800 48 4680 Online Medium 2024-08-22
write_row 19 South Electronics Phone Q4 14200 165 8520 Retail High 2024-11-30
write_row 20 South Clothing Shoes Q1 9200 110 4600 Retail Medium 2024-02-14
write_row 21 South Clothing Jacket Q2 6500 78 3250 Online Low 2024-06-01
write_row 22 South Clothing Hat Q3 3100 130 1550 Retail Low 2024-09-10
write_row 23 South Clothing Shoes Q4 8800 98 4400 Online Medium 2024-12-20
write_row 24 South Food Juice Q1 1800 240 900 Retail Low 2024-03-08
write_row 25 South Food Coffee Q2 3500 280 1750 Online Medium 2024-04-25
write_row 26 South Food Snacks Q3 2200 190 1100 Retail Low 2024-07-14
write_row 27 South Food Juice Q4 2800 210 1400 Online Medium 2024-10-18
write_row 28 East Electronics Tablet Q1 5400 35 3240 Online Medium 2025-02-28
write_row 29 East Electronics Laptop Q2 19500 65 11700 Retail High 2025-05-20
write_row 30 East Electronics Phone Q3 13800 180 8280 Online High 2025-08-15
write_row 31 East Electronics Tablet Q4 8200 52 4920 Retail Medium 2025-11-02
write_row 32 East Clothing Hat Q1 2800 140 1400 Retail Low 2025-01-05
write_row 33 East Clothing Jacket Q2 7200 60 3600 Online Medium 2025-06-18
write_row 34 East Clothing Shoes Q3 5500 88 2750 Retail Medium 2025-09-25
write_row 35 East Clothing Hat Q4 3600 105 1800 Online Low 2025-12-10
write_row 36 East Food Snacks Q1 1200 300 600 Retail Low 2025-03-15
write_row 37 East Food Juice Q2 2100 170 1050 Online Medium 2025-04-30
write_row 38 East Food Coffee Q3 2800 230 1400 Retail Medium 2025-07-22
write_row 39 East Food Snacks Q4 1600 250 800 Online Low 2025-10-28
write_row 40 West Electronics Laptop Q1 20500 68 12300 Online High 2024-01-10
write_row 41 West Electronics Phone Q2 16800 190 10080 Retail High 2024-04-05
write_row 42 West Electronics Tablet Q3 8900 55 5340 Online Medium 2024-08-12
write_row 43 West Electronics Laptop Q4 25000 82 15000 Retail High 2024-11-15
write_row 44 West Clothing Jacket Q1 11000 88 5500 Retail Medium 2024-02-22
write_row 45 West Clothing Shoes Q2 7500 95 3750 Online Medium 2024-05-30
write_row 46 West Clothing Hat Q3 4200 120 2100 Retail Low 2024-09-08
write_row 47 West Clothing Jacket Q4 13500 105 6750 Online High 2024-12-01
write_row 48 West Food Coffee Q1 4500 350 2250 Online Medium 2024-03-18
write_row 49 West Food Snacks Q2 2800 280 1400 Online Medium 2024-06-22
write_row 50 West Food Juice Q3 3200 260 1600 Retail Low 2024-07-30
write_row 51 West Food Coffee Q4 5800 400 2900 Online High 2024-10-25
# Chinese-locale source sheet
$CLI add "$FILE" / --type sheet --prop name=CNData
$CLI set "$FILE" /CNData/A1 --prop text=地区
$CLI set "$FILE" /CNData/B1 --prop text=品类
$CLI set "$FILE" /CNData/C1 --prop text=销售额
write_cn() { $CLI set "$FILE" "/CNData/A$1" --prop text="$2"; $CLI set "$FILE" "/CNData/B$1" --prop text="$3"; $CLI set "$FILE" "/CNData/C$1" --prop text="$4"; }
write_cn 2 华东 电子产品 18000
write_cn 3 华东 服装 9500
write_cn 4 华东 食品 4200
write_cn 5 华南 电子产品 22000
write_cn 6 华南 服装 12000
write_cn 7 华南 食品 5800
write_cn 8 华北 电子产品 15000
write_cn 9 华北 服装 7800
write_cn 10 华北 食品 3600
write_cn 11 西南 电子产品 11000
write_cn 12 西南 服装 6500
write_cn 13 西南 食品 2900
# ==========================================================================
# 17 Pivot Tables
# ==========================================================================
# Sheet: 1-Sales Overview
# Features: tabular layout, 2-level rows, column axis, 3 value fields,
# Cost as percent_of_row, dual page filters, repeat item labels, desc sort
$CLI add "$FILE" / --type sheet --prop name="1-Sales Overview"
$CLI add "$FILE" "/1-Sales Overview" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop 'values=Sales:sum,Quantity:sum,Cost:sum:percent_of_row' \
--prop 'filters=Channel,Priority' \
--prop layout=tabular \
--prop repeatlabels=true \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=desc \
--prop name=SalesOverview \
--prop style=PivotStyleDark2
# Sheet: 2-Market Share
# Features: outline layout, percent_of_col (each region's share per category)
$CLI add "$FILE" / --type sheet --prop name="2-Market Share"
$CLI add "$FILE" "/2-Market Share" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region \
--prop cols=Category \
--prop 'values=Sales:sum:percent_of_col' \
--prop filters=Channel \
--prop layout=outline \
--prop grandtotals=both \
--prop name=MarketShare \
--prop style=PivotStyleMedium4
# Sheet: 3-Product Deep Dive
# Features: 5 value fields (sum, average, max), no column axis — values
# become column headers via synthetic "Values" axis, row grand totals only
$CLI add "$FILE" / --type sheet --prop name="3-Product Deep Dive"
$CLI add "$FILE" "/3-Product Deep Dive" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Category,Product \
--prop 'values=Sales:sum,Sales:average,Sales:max,Quantity:sum,Cost:sum' \
--prop filters=Region \
--prop layout=tabular \
--prop grandtotals=rows \
--prop subtotals=on \
--prop sort=desc \
--prop name=ProductDeepDive \
--prop style=PivotStyleMedium9
# Sheet: 4-Channel Analysis
# Features: percent_of_total (global share), no filters
$CLI add "$FILE" / --type sheet --prop name="4-Channel Analysis"
$CLI add "$FILE" "/4-Channel Analysis" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Channel \
--prop cols=Quarter \
--prop 'values=Sales:sum:percent_of_total,Quantity:sum' \
--prop layout=outline \
--prop grandtotals=both \
--prop name=ChannelTrend \
--prop style=PivotStyleLight21
# Sheet: 5-Priority Matrix
# Features: blankRows — empty line after each outer group for visual separation
$CLI add "$FILE" / --type sheet --prop name="5-Priority Matrix"
$CLI add "$FILE" "/5-Priority Matrix" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Priority,Region \
--prop cols=Category \
--prop 'values=Sales:sum,Cost:sum:percent_of_row' \
--prop filters=Channel \
--prop layout=tabular \
--prop blankrows=true \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=asc \
--prop name=PriorityMatrix \
--prop style=PivotStyleDark6
# Sheet: 6-Compact 3-Level
# Features: compact layout — 3-level hierarchy in one indented column
$CLI add "$FILE" / --type sheet --prop name="6-Compact 3-Level"
$CLI add "$FILE" "/6-Compact 3-Level" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category,Product \
--prop 'values=Sales:sum,Quantity:sum' \
--prop filters=Priority \
--prop layout=compact \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=desc \
--prop name=Compact3Level \
--prop style=PivotStyleMedium14
# Sheet: 7-No Subtotals
# Features: subtotals=off (flat view), grandtotals=cols (bottom row only),
# repeatlabels=true (essential when subtotals off — otherwise outer labels vanish)
$CLI add "$FILE" / --type sheet --prop name="7-No Subtotals"
$CLI add "$FILE" "/7-No Subtotals" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop values=Sales:sum \
--prop layout=tabular \
--prop repeatlabels=true \
--prop grandtotals=cols \
--prop subtotals=off \
--prop sort=asc \
--prop name=FlatView \
--prop style=PivotStyleLight1
# Sheet: 8-Date Grouping
# Features: automatic date grouping — Date:year creates "2024","2025" buckets,
# Date:quarter creates "2024-Q1",... sub-buckets. Uses native Excel fieldGroup XML.
$CLI add "$FILE" / --type sheet --prop name="8-Date Grouping"
$CLI add "$FILE" "/8-Date Grouping" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop 'rows=Date:year,Date:quarter' \
--prop 'values=Sales:sum,Cost:sum' \
--prop filters=Region \
--prop layout=outline \
--prop grandtotals=both \
--prop subtotals=on \
--prop name=DateGrouping \
--prop style=PivotStyleMedium7
# Sheet: 9-Top 5 Products
# Features: topN=5 (only top 5 products by first value field), grandtotals=none
$CLI add "$FILE" / --type sheet --prop name="9-Top 5 Products"
$CLI add "$FILE" "/9-Top 5 Products" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Product \
--prop 'values=Sales:sum,Quantity:sum,Cost:sum' \
--prop layout=tabular \
--prop grandtotals=none \
--prop topN=5 \
--prop sort=desc \
--prop name=Top5Products \
--prop style=PivotStyleDark1
# Sheet: 10-Ultimate
# Features: ALL features combined — tabular + repeatLabels + blankRows +
# dual filters + 3 mixed-aggregation values + row-only grand totals
$CLI add "$FILE" / --type sheet --prop name="10-Ultimate"
$CLI add "$FILE" "/10-Ultimate" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop 'values=Sales:sum,Quantity:average,Cost:sum:percent_of_row' \
--prop 'filters=Channel,Priority' \
--prop layout=tabular \
--prop repeatlabels=true \
--prop blankrows=true \
--prop grandtotals=rows \
--prop subtotals=on \
--prop sort=desc \
--prop name=UltimatePivot \
--prop style=PivotStyleDark11
# Sheet: 11-Chinese Locale
# Features: sort=locale (Chinese pinyin: 华北 < 华东 < 华南 < 西南),
# grandTotalCaption=合计 (custom grand total label)
$CLI add "$FILE" / --type sheet --prop name="11-Chinese Locale"
$CLI add "$FILE" "/11-Chinese Locale" --type pivottable \
--prop source=CNData!A1:C13 \
--prop rows=地区,品类 \
--prop values=销售额:sum \
--prop layout=tabular \
--prop grandtotals=both \
--prop subtotals=on \
--prop sort=locale \
--prop grandTotalCaption=合计 \
--prop name=ChineseLocale \
--prop style=PivotStyleMedium2
# Sheet: 12-Position + Aggregates
# Features: position=D2 (anchor cell override, default is auto-place after source),
# aggregate=avg (default agg when omitted from a value tuple),
# value aggregations: count, min, product, countNums (sum/avg/max shown elsewhere)
$CLI add "$FILE" / --type sheet --prop name="12-Position + Aggregates"
$CLI add "$FILE" "/12-Position + Aggregates" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop position=D2 \
--prop rows=Category \
--prop 'values=Sales:count,Quantity:min,Quantity:product,Sales:countNums' \
--prop aggregate=avg \
--prop layout=tabular \
--prop grandtotals=both \
--prop name=PositionAggs \
--prop style=PivotStyleLight16
# Sheet: 13-Calculated Field
# Features: calculatedField1/2 — user-defined formula fields auto-added as
# data fields (no need to mention in values=). labelFilter — pre-cache row
# filter ('Region:beginsWith:N' keeps only Region values starting with N).
$CLI add "$FILE" / --type sheet --prop name="13-Calculated Field"
$CLI add "$FILE" "/13-Calculated Field" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop 'calculatedField1=Margin:=Sales-Cost' \
--prop 'calculatedField2=Tax:=Sales*0.1' \
--prop rows=Region \
--prop values=Sales:sum \
--prop 'labelFilter=Region:beginsWith:N' \
--prop layout=tabular \
--prop grandtotals=both \
--prop name=CalcField \
--prop style=PivotStyleMedium3
# Sheet: 14-Statistical
# Features: var / varP (sample + population variance) — completes the aggregate
# set. showDataAs=running_total as a standalone --prop (vs the tuple form
# 'Field:agg:mode'); applies as default display for all value fields.
$CLI add "$FILE" / --type sheet --prop name="14-Statistical"
$CLI add "$FILE" "/14-Statistical" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region \
--prop cols=Quarter \
--prop 'values=Sales:var,Sales:varP,Sales:sum' \
--prop showDataAs=running_total \
--prop layout=tabular \
--prop grandtotals=both \
--prop name=Statistical \
--prop style=PivotStyleLight10
# Sheet: 15-Independent Totals
# Features: rowGrandTotals + colGrandTotals as independent toggles
# (vs the combined grandtotals=both/rows/cols/none), defaultSubtotal=true
# (default-subtotal flag on every pivotField), sort=locale-desc (reverse
# pinyin: 西南 > 华南 > 华东 > 华北).
$CLI add "$FILE" / --type sheet --prop name="15-Independent Totals"
$CLI add "$FILE" "/15-Independent Totals" --type pivottable \
--prop source=CNData!A1:C13 \
--prop rows=地区 \
--prop cols=品类 \
--prop values=销售额:sum \
--prop rowGrandTotals=true \
--prop colGrandTotals=false \
--prop defaultSubtotal=true \
--prop layout=outline \
--prop subtotals=on \
--prop sort=locale-desc \
--prop name=IndepTotals \
--prop style=PivotStyleMedium11
# Sheet: 16-Style Flags
# Features: every pivotTableStyleInfo flag wired up — row/col banding,
# row/col header emphasis, last-column highlight. These map to the five
# checkboxes in Excel's PivotTable Styles ribbon.
$CLI add "$FILE" / --type sheet --prop name="16-Style Flags"
$CLI add "$FILE" "/16-Style Flags" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop cols=Quarter \
--prop values=Sales:sum \
--prop showRowStripes=true \
--prop showColStripes=true \
--prop showRowHeaders=true \
--prop showColHeaders=true \
--prop showLastColumn=true \
--prop layout=tabular \
--prop grandtotals=both \
--prop name=StyleFlags \
--prop style=PivotStyleMedium17
# Sheet: 17-Display Toggles
# Features: showDrill=false (hide +/- expand-collapse buttons on every field),
# mergeLabels=true (merge & center repeated outer-axis item cells —
# <pivotTableDefinition mergeItem='1'>).
$CLI add "$FILE" / --type sheet --prop name="17-Display Toggles"
$CLI add "$FILE" "/17-Display Toggles" --type pivottable \
--prop source=Sheet1!A1:J51 \
--prop rows=Region,Category \
--prop values=Sales:sum \
--prop showDrill=false \
--prop mergeLabels=true \
--prop layout=outline \
--prop grandtotals=both \
--prop subtotals=on \
--prop name=DisplayToggles \
--prop style=PivotStyleLight19
$CLI close "$FILE"
$CLI validate "$FILE"
echo "Generated: $FILE"
echo " 19 sheets (Sheet1 + CNData + 17 pivot tables)"
Binary file not shown.
+146
View File
@@ -0,0 +1,146 @@
# Excel Shapes Gallery
Exercises the xlsx `shape` property surface (`officecli help xlsx shape`) — the
drawing layer that floats above the grid. Three files work together:
- **shapes.sh** — Bash script that drives `officecli` (CLI) to build the workbook.
- **shapes.py** — Python script that drives the officecli **SDK** to build an equivalent workbook.
- **shapes.xlsx** — The generated single-sheet shape gallery (20 shapes).
## Regenerate
```bash
cd examples/excel
bash shapes.sh # CLI twin
# or
python3 shapes.py # SDK twin
# → shapes.xlsx
```
Both scripts use resident mode (`open``close` / SDK context manager) for
speed and end with a `validate`.
## Anchoring model (read this first)
Excel shapes are **cell-anchored**, not point/inch-positioned. The `x` / `y` /
`width` / `height` props are **TwoCellAnchor column/row indices** (integers),
*not* lengths:
```bash
officecli add file.xlsx /Sheet1 --type shape --prop geometry=rect \
--prop x=0 --prop y=4 --prop width=2 --prop height=3
```
means "top-left at column 0, row 4; span 2 columns × 3 rows". `Get` reads the
position back as those same integer indices.
The `anchor` prop is an **add-only** convenience that takes a cell range and is
normalized to `x/y/width/height` on readback (there is no `anchor` key in `Get`):
```bash
officecli add file.xlsx /Sheet1 --type shape --prop anchor=K25:L28 ...
# Get → x=10 y=24 width=1 height=3
```
Parent path for `add` is the sheet (`/Sheet1`); the address for
`set`/`get`/`remove` is `/Sheet1/shape[N]`.
## Shape property table
| Feature | Spec | Round-trips in `Get`? |
|---|---|---|
| Geometry preset | `geometry=rect\|roundRect\|ellipse\|triangle\|diamond\|parallelogram\|rightArrow\|star5\|…` (aliases `preset`, `shape`) | yes |
| Position / size | `x` `y` `width` `height` (cell indices; aliases `left`/`top`/`w`/`h`) | yes (integers) |
| Cell-range anchor | `anchor=K25:L28` (add-only; alias `ref`) | as `x/y/width/height` |
| Solid fill | `fill=4472C4` / `fill=accent1` (theme) / `fill=none` (aliases `background`) | yes (`#`-hex or scheme name) |
| Gradient fill | `gradientFill=C1-C2[:angle]` (2/3-stop linear; wins over `fill`; **add-only**) | no readback |
| Outline | `line=C00000:2:dash``color[:width[:style]]` (width in pt; style: `solid`/`dash`/`dot`/`dashdot`/`longdash`); plain `line=264653` still works; `line=none`; alias `border`/`lineColor` | no readback |
| Flip | `flipH=true` / `flipV=true` / `flipBoth=true` / `flip=h\|v\|both` | yes (as `flip`) |
| Rotation | `rotation=30` (degrees clockwise; aliases `rot`/`rotate`) | yes |
| Glow | `glow=FFD700` (color or `true`) | yes (`#RRGGBB-<radius>`) |
| Shadow | `shadow=000000` (color or `true`) | yes (`#`-hex) |
| Soft edge | `softEdge=4` (radius; alias `softedge`) | yes (`4pt`) |
| Reflection | `reflection=true` (**add-only**) | no readback |
| Text | `text="…"` | yes |
| Font | `font=Georgia`, `size=12`, `bold`, `italic`, `underline`, `color` | yes |
| Text align | `align=center` (paragraph), `valign=top\|center\|bottom` | yes |
| Text inset | `margin=6` (uniform padding) | yes (`6pt`) |
| Name | `name=MyStar` (overrides auto `Shape {id}`) | yes |
## Gallery layout
One `Gallery` sheet (`Sheet1`), 20 shapes in five bands of four, each shape
under a bold label cell:
| Band | Shapes |
|---|---|
| 1 | `rect`, `roundRect`, `ellipse`, `triangle` — solid fills, white/dark text |
| 2 | `diamond`, `parallelogram`, `rightArrow`, `star5` (with `name=MyStar`) |
| 3 | `flipH`, `flipV`, `flipBoth`, `rotation=30` |
| 4 | `glow`, `gradientFill`, `reflection`, `shadow`+`softEdge` |
| 5 | `line=C00000:2:dash` (compound `color:width:style`, `fill=none` outline-only), styled text (`font`/`size`/`italic`/`align`/`margin`), `fill=accent1` (theme), `anchor=K25:L28` |
```bash
# A geometry preset with solid fill + centered white text
officecli add shapes.xlsx /Sheet1 --type shape --prop geometry=roundRect \
--prop x=3 --prop y=4 --prop width=2 --prop height=3 \
--prop fill=2A9D8F --prop text="roundRect" --prop color=FFFFFF --prop bold=true
# Flip + rotation
officecli add shapes.xlsx /Sheet1 --type shape --prop geometry=rightArrow \
--prop x=0 --prop y=14 --prop width=2 --prop height=3 --prop fill=4472C4 --prop flipH=true
officecli add shapes.xlsx /Sheet1 --type shape --prop geometry=rightArrow \
--prop x=9 --prop y=14 --prop width=2 --prop height=3 --prop fill=E9C46A --prop rotation=30
# Effects
officecli add shapes.xlsx /Sheet1 --type shape --prop geometry=ellipse \
--prop x=0 --prop y=19 --prop width=2 --prop height=3 --prop fill=2A9D8F --prop glow=FFD700
officecli add shapes.xlsx /Sheet1 --type shape --prop geometry=roundRect \
--prop x=3 --prop y=19 --prop width=2 --prop height=3 --prop gradientFill=FF6B6B-4ECDC4:45
officecli add shapes.xlsx /Sheet1 --type shape --prop geometry=rect \
--prop x=9 --prop y=19 --prop width=2 --prop height=3 --prop fill=8E44AD --prop shadow=000000 --prop softEdge=4
# Styled text inside a shape
officecli add shapes.xlsx /Sheet1 --type shape --prop geometry=roundRect \
--prop x=3 --prop y=24 --prop width=2 --prop height=3 --prop fill=E9C46A \
--prop text="Georgia italic center-top" \
--prop font=Georgia --prop size=12 --prop italic=true --prop align=center --prop valign=top --prop margin=6
```
## Known limitations (xlsx shape)
- **`line` compound form works** (matches pptx shape line). Both `add` and `set`
accept `line=color[:width[:style]]` — e.g. `line=C00000:2:dash` sets a
2 pt dashed dark-red outline, emitting `<a:ln w="25400"><a:solidFill…/><a:prstDash val="dash"/></a:ln>`.
Width is in points; `style` is a `prstDash` token (`solid`/`dash`/`dot`/`dashdot`/`longdash`).
Plain `line=264653` and `line=none` still work. `Get` does not currently emit
a `line` key, so the outline is add/set-only on readback.
- **`gradientFill` and `reflection` are add-only.** They apply and survive to a
valid file, but `Get` does not emit a `gradientFill`/`reflection` key
(and a `gradientFill` shape reports no `fill` on readback either).
## Set → Get round-trip
Both scripts end by reading shapes back and printing the canonical keys. Sample:
```
/Sheet1/shape[8]: {'name': 'MyStar', 'geometry': 'star5'}
/Sheet1/shape[9]: {'flip': 'h', 'geometry': 'rightArrow'} # flipH → flip=h
/Sheet1/shape[12]: {'rotation': 30, 'geometry': 'rightArrow'}
/Sheet1/shape[13]: {'glow': '#FFD700-8', 'fill': '#2A9D8F'}
/Sheet1/shape[16]: {'shadow': '#000000', 'softEdge': '4pt'}
/Sheet1/shape[18]: {'font': 'Georgia', 'size': '12pt', 'align': 'center', 'margin': '6pt'}
/Sheet1/shape[20]: {'x': '10', 'y': '24', 'width': '1', 'height': '3'} # anchor=K25:L28
```
Note the normalization on `get`: colors gain a `#` prefix, sizes/margins/soft
edges become unit-qualified (`12pt`, `6pt`, `4pt`), and `flipH`/`flipV`/`flipBoth`
all read back under the single `flip` key.
## Inspect the Generated File
```bash
officecli query shapes.xlsx shape # list all 20 shapes
officecli get shapes.xlsx "/Sheet1/shape[8]" # the named star5
officecli get shapes.xlsx "/Sheet1/shape[18]" # styled-text shape
```
+182
View File
@@ -0,0 +1,182 @@
#!/usr/bin/env python3
"""
Excel Shapes Gallery — generates shapes.xlsx exercising the xlsx `shape`
property surface (officecli help xlsx shape).
SDK twin of shapes.sh (officecli CLI). Both produce an equivalent shapes.xlsx:
a single "Gallery" sheet laid out as a shape gallery — each demo shape sits in
a readable grid with a label cell above it. This one drives the **officecli
Python SDK** (`pip install officecli-sdk`): one resident is started and every
add/set ships over the named pipe in `doc.batch(...)` round-trips — the same
`{"command","parent","type","props"}` / `{"command","path","props"}` dicts you'd
put in an `officecli batch` list.
Shapes are anchored with cell-unit x/y/width/height (TwoCellAnchor column/row
indices — NOT points/inches). Get reads position back as those integer indices.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 shapes.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "shapes.xlsx")
def cell(path, **props):
"""One `set <path> --prop k=v ...` item in batch-shape (labels/title)."""
return {"command": "set", "path": f"/{path}" if not path.startswith("/") else path,
"props": props}
def shape(**props):
"""One `add /Sheet1 --type shape --prop k=v ...` item in batch-shape."""
return {"command": "add", "parent": "/Sheet1", "type": "shape", "props": props}
print("\n==========================================")
print(f"Generating Excel shapes gallery: {FILE}")
print("==========================================")
with officecli.create(FILE, "--force") as doc:
items = [
cell("Sheet1/A1", value="Excel Shapes Gallery",
**{"font.bold": "true", "font.size": "16", "fill": "1F4E79", "font.color": "FFFFFF"}),
cell("Sheet1/A2",
value="Each shape sits below its label. Shapes are cell-anchored (x/y/width/height in column/row units).",
**{"font.italic": "true", "font.color": "595959"}),
]
# -----------------------------------------------------------------------
# Band 1 — Geometry preset gallery (solid fill, white/dark text)
# -----------------------------------------------------------------------
print("--- Band 1: geometry presets ---")
items += [
cell("Sheet1/A4", value="geometry=rect", **{"font.bold": "true"}),
cell("Sheet1/D4", value="geometry=roundRect", **{"font.bold": "true"}),
cell("Sheet1/G4", value="geometry=ellipse", **{"font.bold": "true"}),
cell("Sheet1/J4", value="geometry=triangle", **{"font.bold": "true"}),
]
# Features: geometry preset, solid fill, text, color, bold
items.append(shape(geometry="rect", x="0", y="4", width="2", height="3", fill="4472C4", text="rect", color="FFFFFF", bold="true"))
items.append(shape(geometry="roundRect", x="3", y="4", width="2", height="3", fill="2A9D8F", text="roundRect", color="FFFFFF", bold="true"))
items.append(shape(geometry="ellipse", x="6", y="4", width="2", height="3", fill="E76F51", text="ellipse", color="FFFFFF", bold="true"))
items.append(shape(geometry="triangle", x="9", y="4", width="2", height="3", fill="E9C46A", text="triangle", color="264653", bold="true"))
# -----------------------------------------------------------------------
# Band 2 — More presets + name override
# -----------------------------------------------------------------------
print("--- Band 2: more presets + name ---")
items += [
cell("Sheet1/A9", value="geometry=diamond", **{"font.bold": "true"}),
cell("Sheet1/D9", value="geometry=parallelogram", **{"font.bold": "true"}),
cell("Sheet1/G9", value="geometry=rightArrow", **{"font.bold": "true"}),
cell("Sheet1/J9", value="geometry=star5 (name=MyStar)", **{"font.bold": "true"}),
]
items.append(shape(geometry="diamond", x="0", y="9", width="2", height="3", fill="8E44AD", text="diamond", color="FFFFFF", bold="true"))
items.append(shape(geometry="parallelogram", x="3", y="9", width="2", height="3", fill="457B9D", text="parallelogram", color="FFFFFF", bold="true"))
items.append(shape(geometry="rightArrow", x="6", y="9", width="2", height="3", fill="F4A261", text="rightArrow", color="264653", bold="true"))
# Features: name (override auto cNvPr @name)
items.append(shape(geometry="star5", x="9", y="9", width="2", height="3", fill="E63946", name="MyStar"))
# -----------------------------------------------------------------------
# Band 3 — Flips & rotation
# -----------------------------------------------------------------------
print("--- Band 3: flips & rotation ---")
items += [
cell("Sheet1/A14", value="flipH=true (mirrored arrow)", **{"font.bold": "true"}),
cell("Sheet1/D14", value="flipV=true", **{"font.bold": "true"}),
cell("Sheet1/G14", value="flipBoth=true", **{"font.bold": "true"}),
cell("Sheet1/J14", value="rotation=30", **{"font.bold": "true"}),
]
# Features: flipH / flipV / flipBoth (Office-API flip aliases → readback `flip`)
items.append(shape(geometry="rightArrow", x="0", y="14", width="2", height="3", fill="4472C4", flipH="true"))
items.append(shape(geometry="triangle", x="3", y="14", width="2", height="3", fill="2A9D8F", flipV="true"))
items.append(shape(geometry="parallelogram", x="6", y="14", width="2", height="3", fill="E76F51", flipBoth="true"))
# Features: rotation (degrees clockwise)
items.append(shape(geometry="rightArrow", x="9", y="14", width="2", height="3", fill="E9C46A", rotation="30", text="30°", color="264653", bold="true"))
# -----------------------------------------------------------------------
# Band 4 — Effects: glow, gradient fill, reflection, shadow, soft edge
# -----------------------------------------------------------------------
print("--- Band 4: effects ---")
items += [
cell("Sheet1/A19", value="glow=FFD700", **{"font.bold": "true"}),
cell("Sheet1/D19", value="gradientFill (2-stop)", **{"font.bold": "true"}),
cell("Sheet1/G19", value="reflection=true", **{"font.bold": "true"}),
cell("Sheet1/J19", value="shadow=000000 + softEdge=4", **{"font.bold": "true"}),
]
# Features: glow (color halo)
items.append(shape(geometry="ellipse", x="0", y="19", width="2", height="3", fill="2A9D8F", glow="FFD700"))
# Features: gradientFill (2-stop linear C1-C2:angle; mutually exclusive with fill, add-only)
items.append(shape(geometry="roundRect", x="3", y="19", width="2", height="3", gradientFill="FF6B6B-4ECDC4:45", text="gradient", color="FFFFFF", bold="true"))
# Features: reflection (mirror-below; 'true' = default reflection; add-only)
items.append(shape(geometry="roundRect", x="6", y="19", width="2", height="3", fill="457B9D", reflection="true", text="reflection", color="FFFFFF", bold="true"))
# Features: shadow (outer shadow) + softEdge (feathered radius)
items.append(shape(geometry="rect", x="9", y="19", width="2", height="3", fill="8E44AD", shadow="000000", softEdge="4", text="shadow", color="FFFFFF"))
# -----------------------------------------------------------------------
# Band 5 — Outline, text styling, theme fill, cell-range anchor
# -----------------------------------------------------------------------
print("--- Band 5: outline, text styling, theme fill, anchor ---")
items += [
cell("Sheet1/A24", value="line=C00000:2:dash (color:width:style)", **{"font.bold": "true"}),
cell("Sheet1/D24", value="styled text (font/size/align)", **{"font.bold": "true"}),
cell("Sheet1/G24", value="fill=accent1 (theme color)", **{"font.bold": "true"}),
cell("Sheet1/J24", value="anchor=K25:L28 (cell range)", **{"font.bold": "true"}),
]
# Features: line compound form 'color[:width[:style]]' (width in pt; style is
# a prstDash token: solid/dash/dot/dashdot/longdash — same grammar as
# pptx shape line), fill=none
items.append(shape(geometry="rect", x="0", y="24", width="2", height="3", fill="none", line="C00000:2:dash", text="outline", color="C00000"))
# Features: font / size / italic / align / valign / margin (text inset)
items.append(shape(geometry="roundRect", x="3", y="24", width="2", height="3", fill="E9C46A",
text="Georgia italic center-top, inset 6pt",
font="Georgia", size="12", italic="true", align="center", valign="top", margin="6", color="264653"))
# Features: fill with a theme (scheme) color name
items.append(shape(geometry="ellipse", x="6", y="24", width="2", height="3", fill="accent1", text="accent1", color="FFFFFF", bold="true"))
# Features: anchor (cell-range form; readback normalizes to x/y/width/height)
items.append(shape(geometry="roundRect", anchor="K25:L28", fill="1D3557", text="anchored K25:L28", color="FFFFFF", bold="true"))
# Widen columns so the label cells are readable.
for c in range(1, 13):
items.append(cell(f"Sheet1/col[{c}]", width="13"))
doc.batch(items)
# -----------------------------------------------------------------------
# Round-trip readback (in-session, pipe) — confirm props survive the write.
# -----------------------------------------------------------------------
print("\n--- Round-trip readback (Add then Get) ---")
for path, keys in [
("/Sheet1/shape[8]", ("name", "geometry")), # star5 + name=MyStar
("/Sheet1/shape[9]", ("flip", "geometry")), # flipH → flip=h
("/Sheet1/shape[12]", ("rotation", "geometry")), # rotation=30
("/Sheet1/shape[13]", ("glow", "fill")), # glow
("/Sheet1/shape[16]", ("shadow", "softEdge")), # shadow + softEdge
("/Sheet1/shape[18]", ("font", "size", "align", "margin")), # styled text
("/Sheet1/shape[20]", ("x", "y", "width", "height")), # anchor → x/y/width/height
]:
node = doc.send({"command": "get", "path": path})
try:
fmt = node["data"]["results"][0]["format"]
except Exception:
fmt = {}
shown = {k: fmt.get(k) for k in keys if k in fmt}
print(f" {path}: {shown}")
doc.send({"command": "save"})
# context exit closes the resident, flushing the workbook to disk.
print(f"\nCreated: {FILE}")
+193
View File
@@ -0,0 +1,193 @@
#!/bin/bash
# Excel Shapes Gallery — generates shapes.xlsx exercising the xlsx `shape`
# property surface (officecli help xlsx shape).
#
# CLI twin of shapes.py (officecli Python SDK). Both produce an equivalent
# shapes.xlsx: a single "Gallery" sheet laid out as a shape gallery — each
# demo shape sits in a readable grid with a label cell above it.
#
# Shapes are anchored with cell-unit x/y/width/height (TwoCellAnchor
# column/row indices — NOT points/inches). Get reads position back as those
# same integer indices.
#
# Usage: ./shapes.sh [officecli path]
# 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.
CLI="${1:-officecli}"
FILE="$(dirname "$0")/shapes.xlsx"
rm -f "$FILE"
$CLI create "$FILE"
$CLI open "$FILE"
# Title cell + column widths so labels are legible.
$CLI set "$FILE" /Sheet1/A1 --prop value="Excel Shapes Gallery" --prop font.bold=true --prop font.size=16 --prop fill=1F4E79 --prop font.color=FFFFFF
$CLI set "$FILE" /Sheet1/A2 --prop value="Each shape sits below its label. Shapes are cell-anchored (x/y/width/height in column/row units)." --prop font.italic=true --prop font.color=595959
# Label helper cells sit on a header row above each shape band.
# Band 1 labels (row 4), shapes span rows 5-8.
$CLI set "$FILE" /Sheet1/A4 --prop value="geometry=rect" --prop font.bold=true
$CLI set "$FILE" /Sheet1/D4 --prop value="geometry=roundRect" --prop font.bold=true
$CLI set "$FILE" /Sheet1/G4 --prop value="geometry=ellipse" --prop font.bold=true
$CLI set "$FILE" /Sheet1/J4 --prop value="geometry=triangle" --prop font.bold=true
# ─────────────────────────────────────────────────────────────────────────────
# Band 1 — Geometry preset gallery (solid theme-accent fill, white text)
# ─────────────────────────────────────────────────────────────────────────────
# Features: geometry=rect, solid fill, text, color, bold, valign
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=rect \
--prop x=0 --prop y=4 --prop width=2 --prop height=3 \
--prop fill=4472C4 --prop text="rect" --prop color=FFFFFF --prop bold=true
# Features: geometry=roundRect
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=roundRect \
--prop x=3 --prop y=4 --prop width=2 --prop height=3 \
--prop fill=2A9D8F --prop text="roundRect" --prop color=FFFFFF --prop bold=true
# Features: geometry=ellipse
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=ellipse \
--prop x=6 --prop y=4 --prop width=2 --prop height=3 \
--prop fill=E76F51 --prop text="ellipse" --prop color=FFFFFF --prop bold=true
# Features: geometry=triangle
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=triangle \
--prop x=9 --prop y=4 --prop width=2 --prop height=3 \
--prop fill=E9C46A --prop text="triangle" --prop color=264653 --prop bold=true
# Band 2 labels (row 9), shapes span rows 10-13.
$CLI set "$FILE" /Sheet1/A9 --prop value="geometry=diamond" --prop font.bold=true
$CLI set "$FILE" /Sheet1/D9 --prop value="geometry=parallelogram" --prop font.bold=true
$CLI set "$FILE" /Sheet1/G9 --prop value="geometry=rightArrow" --prop font.bold=true
$CLI set "$FILE" /Sheet1/J9 --prop value="geometry=star5 (name=MyStar)" --prop font.bold=true
# Features: geometry=diamond
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=diamond \
--prop x=0 --prop y=9 --prop width=2 --prop height=3 \
--prop fill=8E44AD --prop text="diamond" --prop color=FFFFFF --prop bold=true
# Features: geometry=parallelogram
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=parallelogram \
--prop x=3 --prop y=9 --prop width=2 --prop height=3 \
--prop fill=457B9D --prop text="parallelogram" --prop color=FFFFFF --prop bold=true
# Features: geometry=rightArrow
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=rightArrow \
--prop x=6 --prop y=9 --prop width=2 --prop height=3 \
--prop fill=F4A261 --prop text="rightArrow" --prop color=264653 --prop bold=true
# Features: geometry=star5, name (override cNvPr @name)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=star5 \
--prop x=9 --prop y=9 --prop width=2 --prop height=3 \
--prop fill=E63946 --prop name=MyStar
# Band 3 labels (row 14), shapes span rows 15-18.
$CLI set "$FILE" /Sheet1/A14 --prop value="flipH=true (mirrored arrow)" --prop font.bold=true
$CLI set "$FILE" /Sheet1/D14 --prop value="flipV=true" --prop font.bold=true
$CLI set "$FILE" /Sheet1/G14 --prop value="flipBoth=true" --prop font.bold=true
$CLI set "$FILE" /Sheet1/J14 --prop value="rotation=30" --prop font.bold=true
# ─────────────────────────────────────────────────────────────────────────────
# Band 3 — Flips & rotation
# ─────────────────────────────────────────────────────────────────────────────
# Features: flipH (horizontal mirror; Office-API alias of flip=h)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=rightArrow \
--prop x=0 --prop y=14 --prop width=2 --prop height=3 \
--prop fill=4472C4 --prop flipH=true
# Features: flipV (vertical mirror)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=triangle \
--prop x=3 --prop y=14 --prop width=2 --prop height=3 \
--prop fill=2A9D8F --prop flipV=true
# Features: flipBoth (both axes)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=parallelogram \
--prop x=6 --prop y=14 --prop width=2 --prop height=3 \
--prop fill=E76F51 --prop flipBoth=true
# Features: rotation (degrees clockwise; stored as 60000ths on @rot)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=rightArrow \
--prop x=9 --prop y=14 --prop width=2 --prop height=3 \
--prop fill=E9C46A --prop rotation=30 --prop text="30°" --prop color=264653 --prop bold=true
# Band 4 labels (row 19), shapes span rows 20-23.
$CLI set "$FILE" /Sheet1/A19 --prop value="glow=FFD700" --prop font.bold=true
$CLI set "$FILE" /Sheet1/D19 --prop value="gradientFill (2-stop)" --prop font.bold=true
$CLI set "$FILE" /Sheet1/G19 --prop value="reflection=true" --prop font.bold=true
$CLI set "$FILE" /Sheet1/J19 --prop value="shadow=000000 + softEdge=4" --prop font.bold=true
# ─────────────────────────────────────────────────────────────────────────────
# Band 4 — Effects: glow, gradient fill, reflection, shadow, soft edge
# ─────────────────────────────────────────────────────────────────────────────
# Features: glow (color halo; pass a color or 'true' for accent blue)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=ellipse \
--prop x=0 --prop y=19 --prop width=2 --prop height=3 \
--prop fill=2A9D8F --prop glow=FFD700
# Features: gradientFill (two-stop linear, C1-C2:angle; mutually exclusive with fill)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=roundRect \
--prop x=3 --prop y=19 --prop width=2 --prop height=3 \
--prop gradientFill=FF6B6B-4ECDC4:45 --prop text="gradient" --prop color=FFFFFF --prop bold=true
# Features: reflection (mirror-below effect; 'true' enables a default reflection)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=roundRect \
--prop x=6 --prop y=19 --prop width=2 --prop height=3 \
--prop fill=457B9D --prop reflection=true --prop text="reflection" --prop color=FFFFFF --prop bold=true
# Features: shadow (outer shadow) + softEdge (feathered border radius)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=rect \
--prop x=9 --prop y=19 --prop width=2 --prop height=3 \
--prop fill=8E44AD --prop shadow=000000 --prop softEdge=4 --prop text="shadow" --prop color=FFFFFF
# Band 5 labels (row 24), shapes span rows 25-28.
$CLI set "$FILE" /Sheet1/A24 --prop value="line=C00000:2:dash (color:width:style)" --prop font.bold=true
$CLI set "$FILE" /Sheet1/D24 --prop value="styled text (font/size/align)" --prop font.bold=true
$CLI set "$FILE" /Sheet1/G24 --prop value="fill=accent1 (theme color)" --prop font.bold=true
$CLI set "$FILE" /Sheet1/J24 --prop value="anchor=K25:L28 (cell range)" --prop font.bold=true
# ─────────────────────────────────────────────────────────────────────────────
# Band 5 — Outline, text styling, theme fill, cell-range anchor
# ─────────────────────────────────────────────────────────────────────────────
# Features: line compound form 'color[:width[:style]]' (width in pt; style is a
# prstDash token: solid/dash/dot/dashdot/longdash) — same grammar as
# pptx shape line. fill=none (no fill → outline-only shape).
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=rect \
--prop x=0 --prop y=24 --prop width=2 --prop height=3 \
--prop fill=none --prop line=C00000:2:dash --prop text="outline" --prop color=C00000
# Features: font, size, italic, align (paragraph), valign, margin (text inset)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=roundRect \
--prop x=3 --prop y=24 --prop width=2 --prop height=3 \
--prop fill=E9C46A --prop text="Georgia italic center-top, inset 6pt" \
--prop font=Georgia --prop size=12 --prop italic=true \
--prop align=center --prop valign=top --prop margin=6 --prop color=264653
# Features: fill with a theme (scheme) color name — follows the workbook theme
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=ellipse \
--prop x=6 --prop y=24 --prop width=2 --prop height=3 \
--prop fill=accent1 --prop text="accent1" --prop color=FFFFFF --prop bold=true
# Features: anchor (cell-range form B2:F7; readback normalizes to x/y/width/height)
$CLI add "$FILE" /Sheet1 --type shape --prop geometry=roundRect \
--prop anchor=K25:L28 \
--prop fill=1D3557 --prop text="anchored K25:L28" --prop color=FFFFFF --prop bold=true
# Widen columns so the label cells are readable.
for c in 1 2 3 4 5 6 7 8 9 10 11 12; do
$CLI set "$FILE" "/Sheet1/col[$c]" --prop width=13
done
$CLI close "$FILE"
# ─────────────────────────────────────────────────────────────────────────────
# Round-trip readback (fresh, from disk) — confirm props survive the write.
# ─────────────────────────────────────────────────────────────────────────────
$CLI query "$FILE" shape
$CLI get "$FILE" '/Sheet1/shape[8]' --json # star5 + name=MyStar
$CLI get "$FILE" '/Sheet1/shape[9]' --json # flipH
$CLI get "$FILE" '/Sheet1/shape[12]' --json # rotation=30
$CLI get "$FILE" '/Sheet1/shape[13]' --json # glow
$CLI get "$FILE" '/Sheet1/shape[20]' --json # anchor → x/y/width/height
$CLI validate "$FILE"
echo "Generated: $FILE"
Binary file not shown.
+152
View File
@@ -0,0 +1,152 @@
# Sheet Settings Showcase
Exercises the xlsx `sheet` element's **sheet-level** property surface — the
per-worksheet settings that live on `<sheetView>`, `<pageSetup>`,
`<headerFooter>`, `<sheetPr>`, `<sheetProtection>`, and the sheet's
defined-names. These are distinct from the workbook-level settings in
[workbook-settings](workbook-settings.md). Four files work together:
- **sheet-settings.sh** — builds the workbook via the `officecli` CLI (this file walks through it).
- **sheet-settings.py** — the same build via the **officecli Python SDK** (one `doc.send()` per command, mirroring the `.sh` line for line).
- **sheet-settings.xlsx** — the generated workbook (either script produces it).
- **sheet-settings.md** — this file.
The CLI commands shown below are exactly what `sheet-settings.sh` runs; the
`.py` issues the identical sequence over the SDK pipe.
## The `sheet` element
A `sheet` is addressed at path `/<sheetName>`. You `add`/`remove` sheets and
`set`/`get` their sheet-level properties. Each themed sheet in this example
carries a header row + a few data rows so freeze panes, print titles, and the
print area point at meaningful cells:
```bash
officecli set file.xlsx /Sheet1 --prop freeze=B2
officecli get file.xlsx /Sheet1
```
> **Print-only settings verify via `get`, not visual render.** Orientation,
> paper size, fit-to-page, margins, print area, and print titles change how the
> sheet *prints*, not how it looks on screen — a static screenshot won't show
> them. Confirm them with `officecli get`, which reads them straight back out of
> the OOXML.
## Regenerate
```bash
cd examples/excel
bash sheet-settings.sh # via the CLI
# — or —
pip install officecli-sdk # the SDK (officecli binary still required)
python3 sheet-settings.py # via the SDK, same result
# → sheet-settings.xlsx
```
## The four themed sheets
### 1-Freeze-Panes — freeze panes
```bash
officecli set file.xlsx /1-Freeze-Panes --prop freeze=B2
```
`freeze` takes the top-left cell of the *unfrozen* region: `A2` freezes row 1,
`B1` freezes column A, `B2` freezes **both** row 1 and column A. `none` /
`false` removes the freeze. Set-only on existing sheets.
### 2-Print-Setup — page setup, margins, print area & titles
```bash
officecli set file.xlsx /2-Print-Setup \
--prop orientation=landscape \
--prop paperSize=9 \ # OOXML code: 1=Letter, 9=A4
--prop fitToPage=1x1 \ # fit to WxH pages
--prop printArea=A1:D6 \ # _xlnm.Print_Area for this sheet
--prop printTitleRows=1:1 \ # repeat row 1 at top of every page (set-only)
--prop printTitleCols=A:A \ # repeat column A at left of every page (set-only)
--prop margin.top=1.0in --prop margin.bottom=1.0in \
--prop margin.left=0.5in --prop margin.right=0.5in \
--prop margin.header=0.3in --prop margin.footer=0.3in
```
`printTitleRows` / `printTitleCols` are **set-only** — they apply but do not
read back on `get` (they share the sheet's print-title defined-name). All the
others round-trip.
### 3-Headers-Footers — page header / footer
```bash
officecli set file.xlsx /3-Headers-Footers \
--prop header="&LQuarterly Report&C2026 Sales&R&D" \
--prop footer="&LConfidential&CPage &P of &N&R&F"
```
Excel header/footer **format codes** pass through verbatim:
| Code | Meaning | Code | Meaning |
|---|---|---|---|
| `&L` | left section | `&P` | page number |
| `&C` | center section | `&N` | page count |
| `&R` | right section | `&D` | date |
| `&F` | file name | `&T` | time |
### 4-Display-Protection — display toggles + sheet protection
```bash
officecli set file.xlsx /4-Display-Protection \
--prop tabColor=C0392B \
--prop gridlines=false \ # hide sheetView gridlines
--prop headings=false \ # hide row/column headings
--prop zoom=125 \ # sheetView zoom % (10-400)
--prop autoFilter=A1:B5 \ # AutoFilter over a range (or `true` for used range)
--prop direction=rtl # RTL layout — column A on the right
officecli set file.xlsx /4-Display-Protection \
--prop protect=true \ # enable sheet protection
--prop password=secret123 # legacy password hash (implicitly enables protect)
```
`gridlines` / `headings` are emitted only when hidden (default-on is omitted).
`zoom` is emitted only when ≠ 100.
### 5-Sorted / 6-Hidden — sort & hidden sheets
```bash
officecli add file.xlsx / --type sheet --prop name=5-Sorted --prop tabColor=27AE60
officecli set file.xlsx /5-Sorted --prop sort="B desc" # reorder rows by column B, descending
officecli add file.xlsx / --type sheet --prop name=6-Hidden --prop hidden=true
```
`sort` readback is `Col:dir` (e.g. `B:desc`). **A protected sheet can't be
sorted** (`sort` errors on it), so sorting lives on its own unprotected sheet
rather than on `4-Display-Protection`.
## Complete feature coverage
| Group | Keys |
|---|---|
| Freeze | `freeze` |
| Page setup | `orientation`, `paperSize`, `fitToPage`, `printArea`, `printTitleRows`*, `printTitleCols`*, `margin.top/bottom/left/right/header/footer` |
| Headers/footers | `header`, `footer` |
| Display | `tabColor`, `gridlines`, `headings`, `zoom`, `autoFilter`, `direction` |
| Protection | `protect`, `password`* |
| Structure | `name`, `hidden`, `visibility`, `sort` |
\* set-only (no `get` readback): `printTitleRows`, `printTitleCols`, `password`.
Full list: `officecli help xlsx sheet`.
## Set → Get round-trip
```
/1-Freeze-Panes freeze=B2
/2-Print-Setup orientation=landscape paperSize=9 fitToPage=1x1 printArea=A1:D6
margin.top=1in margin.bottom=1in margin.left=0.5in margin.right=0.5in
/3-Headers-Footers header=&LQuarterly Report&C2026 Sales&R&D footer=&LConfidential&CPage &P of &N&R&F
/4-Display-Protection zoom=125 gridlines=false headings=false direction=rtl
tabColor=#C0392B autoFilter=A1:B5 protect=true
/5-Sorted tabColor=#27AE60 sort=B:desc
/6-Hidden hidden=true visibility=hidden
```
+182
View File
@@ -0,0 +1,182 @@
#!/usr/bin/env python3
"""
Sheet Settings Showcase — generates sheet-settings.xlsx exercising the xlsx
`sheet` element's sheet-level property surface (schemas/help/xlsx/sheet.json):
the per-*worksheet* settings on <sheetView>, <pageSetup>, <headerFooter>,
<sheetPr>, <sheetProtection>, and the sheet's defined-names. Distinct from the
workbook-level settings in workbook-settings.{sh,py}.
Four themed sheets: freeze panes, print setup, headers/footers, display +
protection (plus a sorted sheet and a hidden sheet).
SDK twin of sheet-settings.sh. Drives the officecli Python SDK
(`pip install officecli-sdk`) and maps onto the shell script one-for-one:
officecli.create(...) ≈ officecli create + open (file + resident)
doc.send({...}) ≈ one officecli set/add (one call each, no batch)
doc.close() ≈ officecli close (flush to disk)
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 sheet-settings.py
"""
import os
import officecli # pip install officecli-sdk
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sheet-settings.xlsx")
print("\n==========================================")
print(f"Generating sheet-settings showcase: {FILE}")
print("==========================================")
doc = officecli.create(FILE, "--force") # create the .xlsx + start its resident
COLS = "ABCDEFGH"
def cell(path, **props): # one cell write = one `officecli set`
doc.send({"command": "set", "path": path, "props": props})
def sheet(path, **props): # one sheet-container `set`
doc.send({"command": "set", "path": path, "props": props})
def add_sheet(**props): # one `officecli add --type sheet`
doc.send({"command": "add", "parent": "/", "type": "sheet", "props": props})
def hdr(name, *titles): # bold header row (row 1)
for i, title in enumerate(titles):
cell(f"/{name}/{COLS[i]}1", value=title, **{"font.bold": "true"})
def rows(name, start, data): # data rows from `start` down
for r, values in enumerate(data, start=start):
for i, v in enumerate(values):
cell(f"/{name}/{COLS[i]}{r}", value=str(v))
# --- Sheet 1 — Freeze Panes (rename Sheet1) ---
print("\n--- 1-Freeze-Panes ---")
sheet("/Sheet1", name="1-Freeze-Panes")
hdr("1-Freeze-Panes", "Date", "Region", "Product", "Units", "Revenue")
rows("1-Freeze-Panes", 2, [
("2026-01", "North", "Widget", 120, 1140),
("2026-01", "South", "Gadget", 95, 1045),
("2026-02", "East", "Widget", 140, 1225),
("2026-02", "West", "Gizmo", 88, 968),
("2026-03", "North", "Gadget", 132, 1452),
])
# freeze panes: B2 freezes header row 1 AND first column A
sheet("/1-Freeze-Panes", freeze="B2")
# --- Sheet 2 — Print Setup ---
print("--- 2-Print-Setup ---")
add_sheet(name="2-Print-Setup")
hdr("2-Print-Setup", "Item", "Qty", "Unit", "Total")
rows("2-Print-Setup", 2, [
("Screws", 500, 0.02, 10.00),
("Bolts", 300, 0.05, 15.00),
("Washers", 800, 0.01, 8.00),
("Nuts", 450, 0.03, 13.50),
("Anchors", 120, 0.12, 14.40),
])
# print-only settings — verify via get, not visual render
sheet("/2-Print-Setup", **{
"orientation": "landscape",
"paperSize": "9", # 9 = A4
"fitToPage": "1x1", # fit to one page
"printArea": "A1:D6",
"printTitleRows": "1:1", # repeat row 1 at top of each page
"printTitleCols": "A:A", # repeat column A at left
"margin.top": "1.0in", "margin.bottom": "1.0in",
"margin.left": "0.5in", "margin.right": "0.5in",
"margin.header": "0.3in", "margin.footer": "0.3in",
})
# --- Sheet 3 — Headers & Footers ---
print("--- 3-Headers-Footers ---")
add_sheet(name="3-Headers-Footers")
hdr("3-Headers-Footers", "Quarter", "Sales", "Target")
rows("3-Headers-Footers", 2, [
("Q1", 45000, 40000),
("Q2", 52000, 48000),
("Q3", 61000, 55000),
("Q4", 58000, 60000),
])
# Excel format codes pass through verbatim:
# &L left &C center &R right &P page num &N page count &D date &F file
sheet("/3-Headers-Footers",
header="&LQuarterly Report&C2026 Sales&R&D",
footer="&LConfidential&CPage &P of &N&R&F")
# --- Sheet 4 — Display & Protection ---
print("--- 4-Display-Protection ---")
add_sheet(name="4-Display-Protection")
hdr("4-Display-Protection", "Metric", "Value")
rows("4-Display-Protection", 2, [
("Users", 1250),
("Sessions", 8400),
("Bounce", 32),
("Retention", 68),
])
# display: tab color, hide gridlines + headings, zoom, AutoFilter, RTL layout
sheet("/4-Display-Protection", **{
"tabColor": "C0392B",
"gridlines": "false",
"headings": "false",
"zoom": "125",
"autoFilter": "A1:B5",
"direction": "rtl",
})
# protection: enable + legacy password hash (do last — a protected sheet
# can't be sorted, so sorting is on its own sheet below)
sheet("/4-Display-Protection", protect="true", password="secret123")
# --- Sheet 5 — Sorted (sort can't coexist with protect) ---
print("--- 5-Sorted ---")
add_sheet(name="5-Sorted", tabColor="27AE60")
hdr("5-Sorted", "Name", "Score")
rows("5-Sorted", 2, [
("Carol", 88),
("Alice", 95),
("Bob", 72),
("Dave", 60),
])
sheet("/5-Sorted", sort="B desc") # highest score first
# --- Sheet 6 — Hidden at creation ---
print("--- 6-Hidden ---")
add_sheet(name="6-Hidden", hidden="true")
cell("/6-Hidden/A1", value="Hidden data sheet")
# --- Get round-trip: confirm sheet-level keys read back (over the pipe) ---
print("\n--- Round-trip readback ---")
for path, keys in [
("/1-Freeze-Panes", ["freeze"]),
("/2-Print-Setup", ["orientation", "paperSize", "fitToPage", "printArea"]),
("/3-Headers-Footers", ["header", "footer"]),
("/4-Display-Protection", ["tabColor", "gridlines", "headings", "zoom",
"autoFilter", "direction", "protect"]),
("/5-Sorted", ["sort", "tabColor"]),
("/6-Hidden", ["hidden", "visibility"]),
]:
node = doc.send({"command": "get", "path": path})
fmt = node.get("data", {}).get("results", [{}])[0].get("format", {})
got = {k: fmt[k] for k in keys if k in fmt}
print(f" {path}: {got}")
# --- Validate over the pipe (in-session, no extra process) ---
# `save` first so element order is normalized on disk before we validate —
# otherwise the pre-save in-memory model can report a transient schema-order
# note (e.g. sheetPr) that the save-time reserialization fixes.
print("\n--- Validate ---")
doc.send({"command": "save"})
v = doc.send({"command": "validate"})
print(" Validation passed: no errors found." if v.get("success")
else f" {v.get('warnings')}")
doc.close() # stop the resident (flushes to disk)
print(f"\nCreated: {FILE}")
+143
View File
@@ -0,0 +1,143 @@
#!/bin/bash
# sheet-settings.sh — exercise the xlsx `sheet` element's sheet-level property
# surface (schemas/help/xlsx/sheet.json) using the officecli CLI directly.
#
# These are the per-*worksheet* settings that live on <sheetView>, <pageSetup>,
# <headerFooter>, <sheetPr>, <sheetProtection> and the sheet's defined-names —
# distinct from the workbook-level settings in workbook-settings.{sh,py}. Four
# themed sheets: freeze panes, print setup, headers/footers, display+protection.
# CLI twin of sheet-settings.py (officecli SDK); both produce an equivalent
# sheet-settings.xlsx.
#
# 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")/sheet-settings.xlsx"
echo "Building $FILE ..."
rm -f "$FILE"
officecli create "$FILE"
officecli open "$FILE" # resident mode: many sets in one process
# column letters, indexed positionally by the helpers below
COLS=(A B C D E F G H)
# helper: header row in row 1 (bold). hdr <sheet> <c1> <c2> ...
hdr() {
local sheet="$1"; shift
local i=0
for title in "$@"; do
officecli set "$FILE" "/$sheet/${COLS[$i]}1" --prop value="$title" --prop font.bold=true
i=$((i+1))
done
}
# helper: one data row. row <sheet> <rownum> <c1> <c2> ...
row() {
local sheet="$1" r="$2"; shift 2
local i=0
for v in "$@"; do
officecli set "$FILE" "/$sheet/${COLS[$i]}${r}" --prop value="$v"
i=$((i+1))
done
}
# =====================================================================
# Sheet 1 — Freeze Panes (rename Sheet1)
# =====================================================================
officecli set "$FILE" /Sheet1 --prop name=1-Freeze-Panes
hdr 1-Freeze-Panes Date Region Product Units Revenue
row 1-Freeze-Panes 2 2026-01 North Widget 120 1140
row 1-Freeze-Panes 3 2026-01 South Gadget 95 1045
row 1-Freeze-Panes 4 2026-02 East Widget 140 1225
row 1-Freeze-Panes 5 2026-02 West Gizmo 88 968
row 1-Freeze-Panes 6 2026-03 North Gadget 132 1452
# Features: freeze panes — freeze the header row + the first (Date) column so
# they stay visible while scrolling. B2 = freeze row 1 AND column A.
officecli set "$FILE" /1-Freeze-Panes --prop freeze=B2
# =====================================================================
# Sheet 2 — Print Setup
# =====================================================================
officecli add "$FILE" / --type sheet --prop name=2-Print-Setup
hdr 2-Print-Setup Item Qty Unit Total
row 2-Print-Setup 2 Screws 500 0.02 10.00
row 2-Print-Setup 3 Bolts 300 0.05 15.00
row 2-Print-Setup 4 Washers 800 0.01 8.00
row 2-Print-Setup 5 Nuts 450 0.03 13.50
row 2-Print-Setup 6 Anchors 120 0.12 14.40
# Features: print setup — orientation, paper size (9=A4), fit-to-page, margins,
# a print area, and repeat-at-top print titles. These are print-only; verify via
# `get`, not visual render (the memo note applies).
officecli set "$FILE" /2-Print-Setup \
--prop orientation=landscape \
--prop paperSize=9 \
--prop fitToPage=1x1 \
--prop printArea=A1:D6 \
--prop printTitleRows=1:1 \
--prop printTitleCols=A:A \
--prop margin.top=1.0in --prop margin.bottom=1.0in \
--prop margin.left=0.5in --prop margin.right=0.5in \
--prop margin.header=0.3in --prop margin.footer=0.3in
# =====================================================================
# Sheet 3 — Headers & Footers
# =====================================================================
officecli add "$FILE" / --type sheet --prop name=3-Headers-Footers
hdr 3-Headers-Footers Quarter Sales Target
row 3-Headers-Footers 2 Q1 45000 40000
row 3-Headers-Footers 3 Q2 52000 48000
row 3-Headers-Footers 4 Q3 61000 55000
row 3-Headers-Footers 5 Q4 58000 60000
# Features: header/footer — Excel format codes pass through verbatim.
# &L left &C center &R right &P page num &N page count &D date
officecli set "$FILE" /3-Headers-Footers \
--prop header="&LQuarterly Report&C2026 Sales&R&D" \
--prop footer="&LConfidential&CPage &P of &N&R&F"
# =====================================================================
# Sheet 4 — Display & Protection
# =====================================================================
officecli add "$FILE" / --type sheet --prop name=4-Display-Protection
hdr 4-Display-Protection Metric Value
row 4-Display-Protection 2 Users 1250
row 4-Display-Protection 3 Sessions 8400
row 4-Display-Protection 4 Bounce 32
row 4-Display-Protection 5 Retention 68
# Features: display — tab color, hide gridlines + row/col headings, zoom, an
# AutoFilter over the used range, and RTL layout.
officecli set "$FILE" /4-Display-Protection \
--prop tabColor=C0392B \
--prop gridlines=false \
--prop headings=false \
--prop zoom=125 \
--prop autoFilter=A1:B5 \
--prop direction=rtl
# Features: protection — enable sheet protection with a legacy password hash.
# (Do this last on this sheet; a protected sheet can't be sorted, so sorting
# is demonstrated on its own sheet below.)
officecli set "$FILE" /4-Display-Protection \
--prop protect=true \
--prop password=secret123
# =====================================================================
# Sheet 5 — a hidden sheet + a sorted sheet (sort can't coexist w/ protect)
# =====================================================================
officecli add "$FILE" / --type sheet --prop name=5-Sorted --prop tabColor=27AE60
hdr 5-Sorted Name Score
row 5-Sorted 2 Carol 88
row 5-Sorted 3 Alice 95
row 5-Sorted 4 Bob 72
row 5-Sorted 5 Dave 60
# Features: sort — reorder rows by column B descending (highest score first).
officecli set "$FILE" /5-Sorted --prop sort="B desc"
# Features: hidden — a sheet hidden at creation time.
officecli add "$FILE" / --type sheet --prop name=6-Hidden --prop hidden=true
officecli set "$FILE" /6-Hidden/A1 --prop value="Hidden data sheet"
officecli close "$FILE" # flush resident to disk
officecli validate "$FILE"
echo "Created: $FILE"
Binary file not shown.
+141
View File
@@ -0,0 +1,141 @@
# Slicer Showcase
This demo consists of three files that work together:
- **slicers.sh** — Shell script that calls `officecli` commands to generate the workbook. Read this to learn the exact `officecli add --type slicer --prop ...` syntax.
- **slicers.py** — Python twin that drives the officecli SDK to produce an equivalent `slicers.xlsx`. Each `add`/`set` is shown as a copyable shell command in the comments, then shipped over the named pipe.
- **slicers.xlsx** — The generated workbook: a `Sheet1` source table and a `Dashboard` sheet holding one PivotTable with three slicers. Open in Excel to interact with the slicer buttons. Use `officecli get` or `officecli query` to inspect programmatically.
- **slicers.md** — This file. Explains how slicers bind to their source and maps each slicer to the feature it demonstrates.
## Regenerate
```bash
cd examples/excel
bash slicers.sh # CLI twin
# or
python3 slicers.py # SDK twin
# → slicers.xlsx
```
## How slicers bind to a source
A slicer is the interactive button panel that filters a PivotTable. In OOXML a
slicer is **not free-standing** — it is anchored to a *pivot cache field* and
therefore always requires an existing PivotTable as its source. The binding is
expressed with two add-time props:
- `pivotTable=` — the source pivot. Accepts a full path (`/Dashboard/pivottable[1]`)
or a **bare name** (`SalesPivot`) that resolves against the host sheet's pivots.
- `field=` — the pivot cache field to slice on (e.g. `Region`). Must match an
existing cacheField name (case-insensitive). This is add-time only: `set`
intentionally ignores `field=` because a slicer is anchored to its cache field
at creation.
So the build order is always: **source data → PivotTable → slicers**. This demo
builds a `Sheet1` sales table, a `SalesPivot` PivotTable on the `Dashboard`
sheet, then three slicers on that pivot's `Region`, `Product`, and `Quarter`
fields.
## Source Data
| Sheet | Rows | Columns | Purpose |
|-------|------|---------|---------|
| Sheet1 | 12 | Region, Product, Quarter, Sales | Sales data feeding the pivot cache |
| Dashboard | — | — | Hosts the SalesPivot PivotTable + 3 slicers |
## The PivotTable (slicer source)
```bash
officecli add slicers.xlsx /Dashboard --type pivottable \
--prop source=Sheet1!A1:D13 \
--prop rows=Region \
--prop cols=Quarter \
--prop values=Sales:sum \
--prop layout=outline \
--prop grandtotals=both \
--prop name=SalesPivot \
--prop style=PivotStyleMedium9
```
Named `SalesPivot` so the slicers can reference it by bare name.
## Slicers
### Slicer 1: Region
```bash
officecli add slicers.xlsx /Dashboard --type slicer \
--prop pivotTable=/Dashboard/pivottable[1] \
--prop field=Region \
--prop caption='Filter by Region' \
--prop columnCount=2 \
--prop rowHeight=250000 \
--prop name=RegionSlicer
```
**Features:** `pivotTable=` (full path reference), `field=Region`, custom `caption`, `columnCount=2` (two-column button grid), `rowHeight=250000` (EMU, ~19.7pt), explicit `name`.
### Slicer 2: Product
```bash
officecli add slicers.xlsx /Dashboard --type slicer \
--prop pivotTable=SalesPivot \
--prop field=Product \
--prop caption='Filter by Product' \
--prop columnCount=3 \
--prop name=ProductSlicer
```
**Features:** `pivotTable=SalesPivot` — resolves the source by **bare name** against the host sheet's pivots; `columnCount=3` (wide grid).
### Slicer 3: Quarter
```bash
officecli add slicers.xlsx /Dashboard --type slicer \
--prop pivotTable=SalesPivot \
--prop field=Quarter \
--prop columnCount=1 \
--prop name=QuarterSlicer
```
**Features:** `caption` omitted — defaults to the field name (`Quarter`); `rowHeight` omitted — defaults to `225425` EMU (~17.5pt). A minimal single-column slicer.
### Modifying a slicer
`caption` and `columnCount` are settable after creation; `field` is add-time only.
```bash
officecli set slicers.xlsx /Dashboard/slicer[1] \
--prop caption=Region --prop columnCount=1
```
## Property reference
| Property | Ops | Notes |
|----------|-----|-------|
| `pivotTable` | add/set/get | Source pivot. Full path or bare name. Aliases: `pivot`, `source`, `tableName`. |
| `field` | add/get | Pivot cache field to slice on. Add-time only (Set ignores). Alias: `column`. |
| `caption` | add/set/get | Header caption. Defaults to the field name. |
| `name` | add/set/get | Slicer name. Sanitized; defaults to `Slicer_<field>`. |
| `columnCount` | add/set/get | Button-grid columns. Range 1..20000. |
| `rowHeight` | add/set/get | Item row height in EMU. Default 225425 (~17.5pt). |
| `cache` | get | Slicer cache name (read-only). |
| `pivotCacheId` | get | Extension pivot cache id (read-only, auto-assigned). |
| `itemCount` | get | Number of items/buttons, derived from the pivot's shared items (read-only). |
> **Note on `position`:** unlike some Excel elements, the slicer element does not
> accept a `position=` anchor prop — the drawing anchor is auto-placed. Passing
> `position=` reports an `unsupported_property` warning and is ignored.
## Inspect
```bash
# List every slicer in the workbook
officecli query slicers.xlsx slicer
# Inspect one slicer (field / caption / columnCount / rowHeight round-trip)
officecli get slicers.xlsx '/Dashboard/slicer[1]'
# Validate the workbook (slicer + pivot XML is strictly checked)
officecli validate slicers.xlsx
```
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env python3
"""
Slicer Showcase — generates slicers.xlsx with a PivotTable and 3 slicers.
Slicers are the interactive button panels that filter a PivotTable. In OOXML a
slicer is NOT free-standing: it is anchored to a *pivot cache field*, so it
always binds to an existing PivotTable via `pivotTable=` + `field=`. This
script builds the prerequisites first (source data → PivotTable), then adds
several slicers on different fields of that pivot.
SDK twin of slicers.sh (officecli CLI). Both produce an equivalent slicers.xlsx.
This one drives the **officecli Python SDK** (`pip install officecli-sdk`): one
resident is started, the source-data cell writes ship in a single `doc.batch(...)`
round-trip, and each pivot/slicer is one `add` item shipped over the named pipe.
Each item is the same `{"command","parent","type","props"}` dict you'd put in an
`officecli batch` list.
See slicers.md for a per-slicer guide.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 slicers.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "slicers.xlsx")
def slicer(sheet, **props):
"""One `add slicer` item in batch-shape, anchored on the given sheet."""
return {"command": "add", "parent": f"/{sheet}", "type": "slicer", "props": props}
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ==========================================================================
# Source data — a realistic sales table: Region / Product / Quarter / Sales.
# batch is used here only for speed (many cell writes in one round-trip).
# ==========================================================================
print("\n--- Populating source data ---")
data_items = []
for j, h in enumerate(["Region", "Product", "Quarter", "Sales"]):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCD'[j]}1",
"props": {"text": h}})
rows = [
("North", "Laptop", "Q1", 12500),
("North", "Phone", "Q2", 8900),
("North", "Tablet", "Q3", 6200),
("South", "Laptop", "Q1", 22000),
("South", "Phone", "Q2", 18500),
("South", "Tablet", "Q4", 7800),
("East", "Laptop", "Q2", 19500),
("East", "Phone", "Q3", 13800),
("East", "Tablet", "Q1", 5400),
("West", "Laptop", "Q4", 25000),
("West", "Phone", "Q2", 16800),
("West", "Tablet", "Q3", 8900),
]
for i, row in enumerate(rows):
for j, val in enumerate(row):
data_items.append({"command": "set", "path": f"/Sheet1/{'ABCD'[j]}{i+2}",
"props": {"text": str(val)}})
doc.batch(data_items)
# ==========================================================================
# The slicer SOURCE — a PivotTable. Slicers anchor to this pivot's cache
# fields.
#
# officecli add slicers.xlsx /Dashboard --type pivottable \
# --prop source=Sheet1!A1:D13 \
# --prop rows=Region \
# --prop cols=Quarter \
# --prop values=Sales:sum \
# --prop layout=outline \
# --prop grandtotals=both \
# --prop name=SalesPivot \
# --prop style=PivotStyleMedium9
# ==========================================================================
print("\n--- Dashboard PivotTable (slicer source) ---")
doc.send({"command": "add", "parent": "/", "type": "sheet",
"props": {"name": "Dashboard"}})
doc.send({"command": "add", "parent": "/Dashboard", "type": "pivottable",
"props": {"source": "Sheet1!A1:D13", "rows": "Region",
"cols": "Quarter", "values": "Sales:sum",
"layout": "outline", "grandtotals": "both",
"name": "SalesPivot", "style": "PivotStyleMedium9"}})
# ==========================================================================
# Slicers — each binds to SalesPivot via a different cache field.
# ==========================================================================
# --------------------------------------------------------------------------
# Slicer 1: Region
#
# officecli add slicers.xlsx /Dashboard --type slicer \
# --prop pivotTable=/Dashboard/pivottable[1] \
# --prop field=Region \
# --prop caption='Filter by Region' \
# --prop columnCount=2 \
# --prop rowHeight=250000 \
# --prop name=RegionSlicer
#
# Features: pivotTable= (full path reference), field=Region, custom caption,
# columnCount=2 (two-column button grid), rowHeight in EMU, explicit name
# --------------------------------------------------------------------------
print("\n--- Slicer: Region ---")
doc.send(slicer("Dashboard",
pivotTable="/Dashboard/pivottable[1]",
field="Region",
caption="Filter by Region",
columnCount="2",
rowHeight="250000",
name="RegionSlicer"))
# --------------------------------------------------------------------------
# Slicer 2: Product
#
# officecli add slicers.xlsx /Dashboard --type slicer \
# --prop pivotTable=SalesPivot \
# --prop field=Product \
# --prop caption='Filter by Product' \
# --prop columnCount=3 \
# --prop name=ProductSlicer
#
# Features: pivotTable= by BARE NAME (resolves against the host sheet's
# pivots), columnCount=3 (wide grid)
# --------------------------------------------------------------------------
print("\n--- Slicer: Product ---")
doc.send(slicer("Dashboard",
pivotTable="SalesPivot",
field="Product",
caption="Filter by Product",
columnCount="3",
name="ProductSlicer"))
# --------------------------------------------------------------------------
# Slicer 3: Quarter
#
# officecli add slicers.xlsx /Dashboard --type slicer \
# --prop pivotTable=SalesPivot \
# --prop field=Quarter \
# --prop columnCount=1 \
# --prop name=QuarterSlicer
#
# Features: caption OMITTED — defaults to the field name ("Quarter");
# rowHeight OMITTED — defaults to 225425 EMU (~17.5pt). Minimal slicer.
# --------------------------------------------------------------------------
print("\n--- Slicer: Quarter ---")
doc.send(slicer("Dashboard",
pivotTable="SalesPivot",
field="Quarter",
columnCount="1",
name="QuarterSlicer"))
# ==========================================================================
# Modify an existing slicer with `set` (caption + columnCount are settable;
# `field` is add-time only and Set intentionally ignores it).
#
# officecli set slicers.xlsx /Dashboard/slicer[1] \
# --prop caption=Region --prop columnCount=1
# ==========================================================================
print("\n--- Set: slicer[1] caption + columnCount ---")
doc.send({"command": "set", "path": "/Dashboard/slicer[1]",
"props": {"caption": "Region", "columnCount": "1"}})
doc.send({"command": "save"})
print(f"\nDone! Generated: {FILE}")
print(" Sheet1 (source data) + Dashboard (1 PivotTable + 3 slicers)")
+121
View File
@@ -0,0 +1,121 @@
#!/bin/bash
# slicers.sh — exercise the full xlsx `slicer` element (schemas/help/xlsx/slicer.json)
# using the officecli CLI.
#
# Slicers are the interactive button panels that filter a PivotTable. In OOXML a
# slicer is NOT free-standing: it is anchored to a *pivot cache field*, so it
# always binds to an existing PivotTable via `pivotTable=` + `field=`. This
# script therefore builds the prerequisites first (source data → PivotTable),
# then adds several slicers on different fields of that pivot.
#
# CLI twin of slicers.py (officecli Python SDK). Both produce an equivalent
# slicers.xlsx. See slicers.md for a per-slicer guide.
#
# Usage: ./slicers.sh [officecli path]
# 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.
CLI="${1:-officecli}"
FILE="$(dirname "$0")/slicers.xlsx"
echo "Building $FILE ..."
rm -f "$FILE"
$CLI create "$FILE"
$CLI open "$FILE"
# ==========================================================================
# Source data — a realistic sales table: Region / Product / Quarter / Sales
# ==========================================================================
$CLI set "$FILE" /Sheet1/A1 --prop text=Region
$CLI set "$FILE" /Sheet1/B1 --prop text=Product
$CLI set "$FILE" /Sheet1/C1 --prop text=Quarter
$CLI set "$FILE" /Sheet1/D1 --prop text=Sales
# rows: Region Product Quarter Sales
write_row() {
local r="$1"; shift
local cols=(A B C D)
local i=0
for v in "$@"; do
$CLI set "$FILE" "/Sheet1/${cols[$i]}${r}" --prop text="$v"
i=$((i + 1))
done
}
write_row 2 North Laptop Q1 12500
write_row 3 North Phone Q2 8900
write_row 4 North Tablet Q3 6200
write_row 5 South Laptop Q1 22000
write_row 6 South Phone Q2 18500
write_row 7 South Tablet Q4 7800
write_row 8 East Laptop Q2 19500
write_row 9 East Phone Q3 13800
write_row 10 East Tablet Q1 5400
write_row 11 West Laptop Q4 25000
write_row 12 West Phone Q2 16800
write_row 13 West Tablet Q3 8900
# ==========================================================================
# The slicer SOURCE — a PivotTable. Slicers anchor to this pivot's cache fields.
# ==========================================================================
# Features: source range, 1-level rows + column axis, single sum value field,
# named so slicers can reference it by bare name (pivotTable=SalesPivot)
$CLI add "$FILE" / --type sheet --prop name=Dashboard
$CLI add "$FILE" /Dashboard --type pivottable \
--prop source=Sheet1!A1:D13 \
--prop rows=Region \
--prop cols=Quarter \
--prop values=Sales:sum \
--prop layout=outline \
--prop grandtotals=both \
--prop name=SalesPivot \
--prop style=PivotStyleMedium9
# ==========================================================================
# Slicers — each binds to SalesPivot via a different cache field.
# ==========================================================================
# Slicer 1: Region
# Features: pivotTable= (full path reference), field=Region, custom caption,
# columnCount=2 (two-column button grid), rowHeight in EMU, explicit name
$CLI add "$FILE" /Dashboard --type slicer \
--prop pivotTable=/Dashboard/pivottable[1] \
--prop field=Region \
--prop caption='Filter by Region' \
--prop columnCount=2 \
--prop rowHeight=250000 \
--prop name=RegionSlicer
# Slicer 2: Product
# Features: pivotTable= by BARE NAME (resolves against the host sheet's pivots),
# columnCount=3 (wide grid), caption defaulting shown on Slicer 3 instead
$CLI add "$FILE" /Dashboard --type slicer \
--prop pivotTable=SalesPivot \
--prop field=Product \
--prop caption='Filter by Product' \
--prop columnCount=3 \
--prop name=ProductSlicer
# Slicer 3: Quarter
# Features: caption OMITTED — defaults to the field name ("Quarter"); rowHeight
# OMITTED — defaults to 225425 EMU (~17.5pt). Minimal single-column slicer.
$CLI add "$FILE" /Dashboard --type slicer \
--prop pivotTable=SalesPivot \
--prop field=Quarter \
--prop columnCount=1 \
--prop name=QuarterSlicer
# ==========================================================================
# Modify an existing slicer with `set` (caption + columnCount are settable;
# `field` is add-time only and Set intentionally ignores it).
# ==========================================================================
# Features: set caption + set columnCount round-trip on an existing slicer
$CLI set "$FILE" '/Dashboard/slicer[1]' \
--prop caption='Region' \
--prop columnCount=1
$CLI close "$FILE"
$CLI validate "$FILE"
echo "Generated: $FILE"
echo " Sheet1 (source data) + Dashboard (1 PivotTable + 3 slicers)"
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More