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
@@ -0,0 +1,235 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- rid: osx-arm64
|
||||
name: officecli-mac-arm64
|
||||
os: macos-latest
|
||||
- rid: osx-x64
|
||||
name: officecli-mac-x64
|
||||
os: macos-latest
|
||||
- rid: linux-x64
|
||||
name: officecli-linux-x64
|
||||
os: ubuntu-latest
|
||||
- rid: linux-arm64
|
||||
name: officecli-linux-arm64
|
||||
os: ubuntu-latest
|
||||
- rid: linux-musl-x64
|
||||
name: officecli-linux-alpine-x64
|
||||
os: ubuntu-latest
|
||||
- rid: linux-musl-arm64
|
||||
name: officecli-linux-alpine-arm64
|
||||
os: ubuntu-latest
|
||||
- rid: win-x64
|
||||
name: officecli-win-x64.exe
|
||||
os: windows-latest
|
||||
- rid: win-arm64
|
||||
name: officecli-win-arm64.exe
|
||||
os: windows-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: '10.0.x'
|
||||
|
||||
- name: Publish
|
||||
run: dotnet publish src/officecli/officecli.csproj -c Release -r ${{ matrix.rid }} -o publish --nologo
|
||||
|
||||
- name: Rename output
|
||||
run: |
|
||||
if [ -f publish/officecli.exe ]; then
|
||||
mv publish/officecli.exe publish/${{ matrix.name }}
|
||||
else
|
||||
mv publish/officecli publish/${{ matrix.name }}
|
||||
fi
|
||||
|
||||
- name: Setup macOS code signing
|
||||
if: startsWith(matrix.rid, 'osx-')
|
||||
env:
|
||||
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
||||
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
run: |
|
||||
CERT_PATH="$RUNNER_TEMP/build_certificate.p12"
|
||||
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
|
||||
|
||||
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode > "$CERT_PATH"
|
||||
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security import "$CERT_PATH" -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security list-keychain -d user -s "$KEYCHAIN_PATH" login.keychain-db
|
||||
rm -f "$CERT_PATH"
|
||||
|
||||
- name: Codesign (macOS)
|
||||
if: startsWith(matrix.rid, 'osx-')
|
||||
run: |
|
||||
IDENTITY=$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -n 1 | sed -E 's/.*"(.+)"/\1/')
|
||||
echo "Signing with: $IDENTITY"
|
||||
# --options runtime (Hardened Runtime, required by notarization) denies the
|
||||
# self-contained CoreCLR's MAP_JIT executable memory by default, so the runtime
|
||||
# fails to start with "Failed to create CoreCLR, HRESULT: 0x80070008" on every
|
||||
# command. build/officecli.entitlements re-permits exactly allow-jit (the plist
|
||||
# holds no XML comments — codesign's AMFI parser rejects them).
|
||||
codesign --force --options runtime --entitlements build/officecli.entitlements --timestamp --sign "$IDENTITY" publish/${{ matrix.name }}
|
||||
codesign --verify --strict --verbose=2 publish/${{ matrix.name }}
|
||||
# Fail the build if allow-jit did not actually embed (e.g. a malformed plist
|
||||
# makes codesign silently drop entitlements) — otherwise the bug ships again.
|
||||
codesign -d --entitlements - --xml publish/${{ matrix.name }} | grep -q allow-jit
|
||||
|
||||
- name: Notarize (macOS)
|
||||
if: startsWith(matrix.rid, 'osx-')
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
|
||||
TEAM_ID: ${{ secrets.TEAM_ID }}
|
||||
run: |
|
||||
# A bare Mach-O binary cannot be stapled; notarytool requires a
|
||||
# zip/pkg/dmg container. Submit a zip — the notarization ticket is
|
||||
# recorded on Apple's servers and Gatekeeper validates online.
|
||||
ditto -c -k --keepParent publish/${{ matrix.name }} "$RUNNER_TEMP/notarize.zip"
|
||||
xcrun notarytool submit "$RUNNER_TEMP/notarize.zip" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--team-id "$TEAM_ID" \
|
||||
--password "$APP_SPECIFIC_PASSWORD" \
|
||||
--wait
|
||||
rm -f "$RUNNER_TEMP/notarize.zip"
|
||||
|
||||
- name: Smoke test - create document
|
||||
if: >-
|
||||
(matrix.rid == 'osx-arm64' && runner.arch == 'ARM64') ||
|
||||
(matrix.rid == 'osx-x64' && runner.arch == 'X64') ||
|
||||
(matrix.rid == 'linux-x64' && runner.os == 'Linux') ||
|
||||
(matrix.rid == 'win-x64' && runner.os == 'Windows')
|
||||
env:
|
||||
# Disable Git Bash (MSYS) POSIX-to-Windows path conversion on
|
||||
# windows-latest, which otherwise mangles `/body` into
|
||||
# `C:/Program Files/Git/body` before it reaches the CLI.
|
||||
MSYS_NO_PATHCONV: '1'
|
||||
MSYS2_ARG_CONV_EXCL: '*'
|
||||
run: |
|
||||
chmod +x publish/${{ matrix.name }}
|
||||
publish/${{ matrix.name }} create test_smoke.docx
|
||||
publish/${{ matrix.name }} add test_smoke.docx /body --type paragraph --prop text="Hello from CI"
|
||||
publish/${{ matrix.name }} get test_smoke.docx '/body/p[1]'
|
||||
publish/${{ matrix.name }} close test_smoke.docx
|
||||
rm -f test_smoke.docx
|
||||
|
||||
- name: Smoke test - .NET 8-only runtime (linux-x64)
|
||||
if: matrix.rid == 'linux-x64' && runner.os == 'Linux'
|
||||
run: |
|
||||
chmod +x publish/${{ matrix.name }}
|
||||
mkdir -p smoke_net8
|
||||
docker run --rm \
|
||||
-v "$PWD/publish:/app:ro" \
|
||||
-v "$PWD/smoke_net8:/work" \
|
||||
-w /work \
|
||||
mcr.microsoft.com/dotnet/runtime:8.0 \
|
||||
bash -c "set -e; \
|
||||
/app/${{ matrix.name }} create issue115.docx; \
|
||||
/app/${{ matrix.name }} add issue115.docx /body --type paragraph --prop text='net8 smoke'; \
|
||||
/app/${{ matrix.name }} close issue115.docx; \
|
||||
/app/${{ matrix.name }} view issue115.docx text --json"
|
||||
|
||||
- name: Smoke test - install
|
||||
if: >-
|
||||
(matrix.rid == 'osx-arm64' && runner.arch == 'ARM64') ||
|
||||
(matrix.rid == 'osx-x64' && runner.arch == 'X64') ||
|
||||
(matrix.rid == 'linux-x64' && runner.os == 'Linux') ||
|
||||
(matrix.rid == 'win-x64' && runner.os == 'Windows')
|
||||
env:
|
||||
MSYS_NO_PATHCONV: '1'
|
||||
MSYS2_ARG_CONV_EXCL: '*'
|
||||
run: |
|
||||
publish/${{ matrix.name }} install
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
test -f "$LOCALAPPDATA/OfficeCLI/officecli.exe" || { echo "FAIL: officecli.exe not found in %LOCALAPPDATA%\\OfficeCLI"; exit 1; }
|
||||
"$LOCALAPPDATA/OfficeCLI/officecli.exe" --version
|
||||
else
|
||||
test -f "$HOME/.local/bin/officecli" || { echo "FAIL: officecli not found in ~/.local/bin"; exit 1; }
|
||||
"$HOME/.local/bin/officecli" --version
|
||||
fi
|
||||
|
||||
- name: Smoke test - install.sh / install.ps1
|
||||
# Exercises the shell installers themselves (separate from the CLI's
|
||||
# own `install` subcommand tested above). Downloads from
|
||||
# d.officecli.ai with github fallback — validates the production
|
||||
# mirror is reachable AND the in-repo script is syntactically and
|
||||
# logically correct on each platform.
|
||||
if: >-
|
||||
(matrix.rid == 'osx-arm64' && runner.arch == 'ARM64') ||
|
||||
(matrix.rid == 'osx-x64' && runner.arch == 'X64') ||
|
||||
(matrix.rid == 'linux-x64' && runner.os == 'Linux') ||
|
||||
(matrix.rid == 'win-x64' && runner.os == 'Windows')
|
||||
shell: bash
|
||||
env:
|
||||
MSYS_NO_PATHCONV: '1'
|
||||
MSYS2_ARG_CONV_EXCL: '*'
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
pwsh -NoProfile -ExecutionPolicy Bypass -File ./install.ps1
|
||||
test -f "$LOCALAPPDATA/OfficeCLI/officecli.exe" || { echo "FAIL: officecli.exe not installed"; exit 1; }
|
||||
"$LOCALAPPDATA/OfficeCLI/officecli.exe" --version
|
||||
else
|
||||
bash install.sh
|
||||
test -f "$HOME/.local/bin/officecli" || { echo "FAIL: officecli not installed at ~/.local/bin"; exit 1; }
|
||||
"$HOME/.local/bin/officecli" --version
|
||||
fi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ matrix.name }}
|
||||
path: publish/${{ matrix.name }}
|
||||
|
||||
release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Flatten artifacts and generate checksums
|
||||
run: |
|
||||
mkdir -p flat
|
||||
find artifacts -type f -exec mv {} flat/ \;
|
||||
rm -rf artifacts
|
||||
mv flat artifacts
|
||||
cd artifacts
|
||||
sha256sum officecli-* > SHA256SUMS
|
||||
echo "=== SHA256SUMS ==="
|
||||
cat SHA256SUMS
|
||||
|
||||
- name: Create Draft Release
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
files: artifacts/**/*
|
||||
generate_release_notes: true
|
||||
draft: true
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Publish npm
|
||||
|
||||
# Fires when a GitHub Release is PUBLISHED (not on the draft created by
|
||||
# build.yml). By then the release assets — the platform binaries and
|
||||
# SHA256SUMS the npm postinstall downloads — are publicly live, so a user who
|
||||
# `npm install`s immediately can fetch them.
|
||||
#
|
||||
# Auth: npm Trusted Publishing (OIDC). No NPM_TOKEN / secret required — the
|
||||
# `id-token: write` permission below lets the runner mint a short-lived token.
|
||||
# Configure the trusted publisher for BOTH packages on npmjs.com:
|
||||
# org/user: iOfficeAI repo: OfficeCLI workflow: publish-npm.yml action: npm publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to publish (X.Y.Z, or X.Y.Z-pre for a test build). Required for manual runs.'
|
||||
required: false
|
||||
dist_tag:
|
||||
description: 'npm dist-tag (default: latest). Use e.g. "test" for a prerelease that must not move latest.'
|
||||
required: false
|
||||
default: 'latest'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # OIDC token for npm trusted publishing
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
- '@officecli/officecli'
|
||||
- '@aionui/officecli'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Upgrade npm (trusted publishing + provenance need >= 11.5.1)
|
||||
# Pinned to 11.x: npm 12.0.0 ships a broken provenance path
|
||||
# ("Cannot find module 'sigstore'" in libnpmpublish). Unpin once fixed.
|
||||
run: npm install -g npm@11
|
||||
|
||||
- name: Resolve version
|
||||
id: ver
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
V="${{ github.event.release.tag_name }}"
|
||||
else
|
||||
V="${{ github.event.inputs.version }}"
|
||||
fi
|
||||
V="${V#v}"
|
||||
if ! printf '%s' "$V" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$'; then
|
||||
echo "Invalid or missing version: '$V'"
|
||||
exit 1
|
||||
fi
|
||||
echo "version=$V" >> "$GITHUB_OUTPUT"
|
||||
echo "Publishing ${{ matrix.package }}@$V"
|
||||
|
||||
- name: Set package name, version, and match the README to this package
|
||||
id: pkg
|
||||
working-directory: npm
|
||||
run: |
|
||||
npm pkg set name='${{ matrix.package }}'
|
||||
npm pkg set version='${{ steps.ver.outputs.version }}'
|
||||
# Only the package name differs per scope; links stay officecli.ai.
|
||||
# Rewrite the install/npx commands to this package's name. No-op for
|
||||
# @officecli/officecli.
|
||||
sed -i "s|@officecli/officecli|${{ matrix.package }}|g" README.md
|
||||
# A brand-new package can't be created by trusted publishing (OIDC) —
|
||||
# skip (green) until its one-time manual first publish creates it.
|
||||
if npm view '${{ matrix.package }}' version >/dev/null 2>&1; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::${{ matrix.package }} does not exist on npm yet — first publish must be manual; skipping."
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.pkg.outputs.exists == 'true'
|
||||
working-directory: npm
|
||||
run: |
|
||||
TAG='${{ github.event.inputs.dist_tag }}'
|
||||
[ -z "$TAG" ] && TAG='latest'
|
||||
npm publish --provenance --access public --tag "$TAG"
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Publish SDK (PyPI)
|
||||
|
||||
# Publishes officecli-sdk (sdk/python) to PyPI on its OWN cadence — same model as
|
||||
# publish-sdk.yml for npm. Triggers on a push touching sdk/python/** (or manual
|
||||
# dispatch); a guard skips when that version is already on PyPI, so only a
|
||||
# version bump in pyproject.toml actually publishes. A CLI release does NOT
|
||||
# republish the SDK.
|
||||
#
|
||||
# Auth: PyPI Trusted Publishing (OIDC), no token / no API key. Configure the
|
||||
# trusted publisher for the officecli-sdk project on pypi.org:
|
||||
# owner: iOfficeAI repo: OfficeCLI workflow: publish-pypi.yml
|
||||
# (officecli-sdk already exists on PyPI, so the publisher can be added directly.)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'sdk/python/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # OIDC token for PyPI trusted publishing
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Resolve version and check if already on PyPI
|
||||
id: ver
|
||||
working-directory: sdk/python
|
||||
run: |
|
||||
NAME=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['name'])")
|
||||
VERSION=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
||||
echo "Publishing candidate: $NAME==$VERSION"
|
||||
CODE=$(curl -s -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/$NAME/$VERSION/json")
|
||||
if [ "$CODE" = "200" ]; then
|
||||
echo "already=true" >> "$GITHUB_OUTPUT"
|
||||
echo "$NAME==$VERSION is already on PyPI — nothing to publish (bump the version to release)."
|
||||
else
|
||||
echo "already=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Build sdist + wheel
|
||||
if: steps.ver.outputs.already == 'false'
|
||||
working-directory: sdk/python
|
||||
run: |
|
||||
python -m pip install --upgrade build
|
||||
python -m build
|
||||
|
||||
- name: Publish to PyPI
|
||||
if: steps.ver.outputs.already == 'false'
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
packages-dir: sdk/python/dist
|
||||
@@ -0,0 +1,82 @@
|
||||
name: Publish SDK (npm)
|
||||
|
||||
# Publishes @officecli/sdk (sdk/node) on its OWN cadence — decoupled from the
|
||||
# CLI release. The SDK depends on @officecli/officecli via a semver range
|
||||
# (^1.0.0), so a CLI version bump flows to users automatically (fresh installs
|
||||
# resolve the newest CLI; existing installs auto-update the bundled binary)
|
||||
# WITHOUT republishing the SDK. The SDK is republished only when its own source
|
||||
# changes — i.e. when sdk/node/package.json's version is bumped.
|
||||
#
|
||||
# Trigger: a push touching sdk/node/** (or manual dispatch). A guard skips the
|
||||
# publish when that version is already on npm, so only a version bump actually
|
||||
# publishes — pushes that don't change the version are no-ops.
|
||||
#
|
||||
# The same SDK is published under four names (one canonical + mirrors/aliases).
|
||||
# The version is shared (sdk/node/package.json); the name is swapped per leg.
|
||||
#
|
||||
# Auth: npm Trusted Publishing (OIDC), no token. Configure a trusted publisher
|
||||
# for EACH of the four packages on npmjs.com:
|
||||
# org/user: iOfficeAI repo: OfficeCLI workflow: publish-sdk.yml action: npm publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'sdk/node/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
- '@officecli/sdk'
|
||||
- '@officecli/officecli-sdk'
|
||||
- '@aionui/officecli-sdk'
|
||||
- 'officecli-sdk'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Upgrade npm (trusted publishing + provenance need >= 11.5.1)
|
||||
run: npm install -g npm@latest
|
||||
|
||||
- name: Set package name and check if already published
|
||||
id: ver
|
||||
working-directory: sdk/node
|
||||
run: |
|
||||
npm pkg set name='${{ matrix.package }}'
|
||||
# Only the package name differs per scope; links stay officecli.ai.
|
||||
# Rewrite install/require examples to this package's name.
|
||||
sed -i "s|@officecli/sdk|${{ matrix.package }}|g" README.md
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
echo "Publishing candidate: ${{ matrix.package }}@$VERSION"
|
||||
if ! npm view "${{ matrix.package }}" version >/dev/null 2>&1; then
|
||||
# A brand-new package can't be created by trusted publishing (OIDC) —
|
||||
# it has no package to attach the publisher config to, so the PUT 404s.
|
||||
# Skip (green) until the one-time MANUAL first publish creates it.
|
||||
echo "already=true" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::${{ matrix.package }} does not exist on npm yet — first publish must be manual; skipping."
|
||||
elif npm view "${{ matrix.package }}@$VERSION" version >/dev/null 2>&1; then
|
||||
echo "already=true" >> "$GITHUB_OUTPUT"
|
||||
echo "${{ matrix.package }}@$VERSION is already on npm — nothing to publish (bump the version to release)."
|
||||
else
|
||||
echo "already=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Publish to npm
|
||||
if: steps.ver.outputs.already == 'false'
|
||||
working-directory: sdk/node
|
||||
run: npm publish --provenance --access public
|
||||
@@ -0,0 +1,50 @@
|
||||
name: SDK smoke
|
||||
|
||||
# End-to-end smoke for both SDKs on all three OSes. The runners have no officecli
|
||||
# on PATH, so create() exercises the FULL auto-install path — install.sh on
|
||||
# unix, install.ps1 (irm | iex via PowerShell) on Windows — then a real pipe
|
||||
# round-trip. This is the only place the Windows installer path is exercised, and
|
||||
# it runs against the SDK source directly (no bundled dependency installed), so
|
||||
# the install.sh/install.ps1 fallback is what gets tested.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'sdk/**'
|
||||
- '.github/workflows/sdk-smoke.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'sdk/**'
|
||||
- '.github/workflows/sdk-smoke.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
smoke:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Node SDK auto-install + round-trip
|
||||
run: node sdk/node/smoke.js
|
||||
|
||||
- name: Python SDK auto-install + round-trip
|
||||
run: python sdk/python/smoke.py
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Skill parity
|
||||
|
||||
# Root SKILL.md is the file the public URL serves
|
||||
# (https://d.officecli.ai/SKILL.md, raw GitHub URL) and is what
|
||||
# `curl ... | bash` consumers fetch. skills/officecli/SKILL.md is a
|
||||
# symlink to it — the spec-conforming location that `gh skill install`
|
||||
# and `npx skills add` discover, and what the binary embeds.
|
||||
#
|
||||
# On macOS / Linux the symlink resolves transparently and this diff
|
||||
# always passes. The check exists for the Windows case: git on Windows
|
||||
# without core.symlinks=true checks out symlinks as plain text files
|
||||
# containing the link target path (e.g. literal "../../SKILL.md"), so
|
||||
# diff catches that corruption before it ships in a release build.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
paths:
|
||||
- 'SKILL.md'
|
||||
- 'skills/officecli/SKILL.md'
|
||||
- '.github/workflows/skill-parity.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
diff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Diff root SKILL.md vs skills/officecli/SKILL.md
|
||||
run: |
|
||||
if ! diff -q SKILL.md skills/officecli/SKILL.md; then
|
||||
echo "::error::SKILL.md and skills/officecli/SKILL.md are out of sync."
|
||||
echo "The two files must be byte-identical. The binary embeds skills/officecli/SKILL.md;"
|
||||
echo "the public URL (d.officecli.ai/SKILL.md, raw GitHub) serves the root copy."
|
||||
echo "Fix: cp SKILL.md skills/officecli/SKILL.md (or the reverse) and commit."
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: SKILL.md == skills/officecli/SKILL.md"
|
||||
@@ -0,0 +1,126 @@
|
||||
# Contributing to OfficeCLI
|
||||
|
||||
> 中文版 / Chinese version: [CONTRIBUTING.zh.md](./CONTRIBUTING.zh.md)
|
||||
|
||||
> You must follow the two rules below. Code style, dependencies, tests, and
|
||||
> docs are handled by the maintainer in post-merge cleanup — do not worry
|
||||
> about them.
|
||||
|
||||
## Rule 1: One PR = one atomic change
|
||||
|
||||
A PR must contain exactly one feature or one bug fix that cannot be further
|
||||
decomposed. If your change can be split into multiple pieces that each have
|
||||
standalone value, submit each piece as a separate PR.
|
||||
|
||||
### Self-check
|
||||
|
||||
Before opening the PR, ask your AI tool:
|
||||
|
||||
> "Analyze this diff. Can it be decomposed into multiple PRs where each
|
||||
> could be merged or reverted independently? If yes, list them."
|
||||
|
||||
If the answer is "yes, N PRs", split into N PRs before submitting.
|
||||
|
||||
### Examples
|
||||
|
||||
**✅ Single-PR bugs** — one root cause, one fix
|
||||
- `Picture added with only 'width' specified gets wrong default height`
|
||||
- `Body-level find: anchor throws ArgumentException`
|
||||
- `AddParagraph --index N is off-by-one when the body contains a table`
|
||||
|
||||
**✅ Single-PR features** — one coherent capability
|
||||
- `query ole: list embedded OLE objects with ProgID and dimensions`
|
||||
- `set wrap/hposition/vposition on floating pictures`
|
||||
|
||||
**❌ Must split** — multiple independent changes bundled together
|
||||
- `Fix picture index bug + add OLE detection + add HTML heading numbering`
|
||||
→ 3 PRs, zero shared code
|
||||
- `Add OLE object detection + add EMF→PNG conversion`
|
||||
→ 2 PRs, two independent layers
|
||||
- `Add auto aspect ratio + fix index off-by-one + fix line spacing clipping`
|
||||
→ 3 PRs, three unrelated root causes
|
||||
|
||||
**🤔 Judgment calls** — default to splitting
|
||||
- `Add helper function + its first consumer`
|
||||
→ 1 or 2 PRs; split if the helper has standalone reuse potential
|
||||
- `Add read support + add write support for the same property`
|
||||
→ 1 or 2 PRs; split if you want read to land before write is vetted
|
||||
|
||||
## Rule 2: Every PR must include a verifiable validation method
|
||||
|
||||
State in the PR description (or a linked issue) how a reviewer can confirm
|
||||
your change actually works.
|
||||
|
||||
### For bug-fix PRs — pick one (in order of preference)
|
||||
|
||||
1. **officecli command sequence** showing broken output before and fixed
|
||||
output after
|
||||
2. **Shell or Python script** that reproduces the bug and runs clean after
|
||||
the fix
|
||||
3. **Authoritative reference** showing what the correct behavior should be
|
||||
(OOXML spec, Microsoft / ECMA docs, etc.)
|
||||
4. **Screenshot** — only when the bug is purely visual
|
||||
|
||||
### For feature PRs — include at minimum
|
||||
|
||||
- **A screenshot** of the feature in action (Word / Excel / PowerPoint
|
||||
window, HTML preview, or terminal output)
|
||||
- Optionally a command sequence showing how to trigger it
|
||||
|
||||
### Examples
|
||||
|
||||
**Bug fix — command sequence (ideal):**
|
||||
|
||||
```bash
|
||||
# Before my fix:
|
||||
officecli blank test.docx
|
||||
officecli add test.docx picture --prop "path=photo-2x1.png" --prop "width=10cm"
|
||||
officecli query test.docx picture
|
||||
# → height: "10.2cm" ❌ WRONG (hardcoded 4-inch default)
|
||||
|
||||
# After my fix:
|
||||
officecli blank test.docx
|
||||
officecli add test.docx picture --prop "path=photo-2x1.png" --prop "width=10cm"
|
||||
officecli query test.docx picture
|
||||
# → height: "5.0cm" ✓ CORRECT (auto-computed from 2:1 pixel ratio)
|
||||
```
|
||||
|
||||
**Feature — screenshot (ideal):**
|
||||
|
||||
> **Heading auto-numbering from style chain**
|
||||
>
|
||||
> Before: ![heading-before.png] (plain "Chapter One" with no number)
|
||||
> After: ![heading-after.png] ("1. Chapter One" with auto-numbering span)
|
||||
>
|
||||
> How to trigger:
|
||||
> ```bash
|
||||
> officecli blank demo.docx
|
||||
> officecli add demo.docx paragraph --prop "style=Heading1" --prop "text=Chapter One"
|
||||
> officecli watch demo.docx
|
||||
> ```
|
||||
|
||||
## If you don't follow these rules
|
||||
|
||||
The maintainer reserves two options.
|
||||
|
||||
### Option A — Reject and ask for resubmission (preferred)
|
||||
|
||||
The maintainer closes the PR with a link to this guide and asks you to
|
||||
resubmit as properly decomposed PRs with validation methods.
|
||||
|
||||
**Your credit:** the PR is entirely yours, including the **"Merged"** badge
|
||||
after resubmission.
|
||||
|
||||
### Option B — Cherry-pick the valuable parts (last resort)
|
||||
|
||||
If part of your PR is clearly valuable and worth saving, the maintainer runs
|
||||
`git cherry-pick` on those commits into `main` directly and closes the
|
||||
original PR.
|
||||
|
||||
**Your credit:**
|
||||
- `git cherry-pick` preserves the original author, so `git log` and
|
||||
`git blame` still show you as author of those lines.
|
||||
- The maintainer's reconcile commit message carries a
|
||||
`Co-authored-by: <you> <your-email>` trailer, which counts toward your
|
||||
GitHub contribution graph.
|
||||
- **However, the original PR shows as "Closed" instead of "Merged"**.
|
||||
@@ -0,0 +1,118 @@
|
||||
# 为 OfficeCLI 贡献代码
|
||||
|
||||
> English / 英文主文件: [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
||||
> 你必须遵守下面两条规则。代码风格、依赖、测试、文档由维护者在 merge 之后通过
|
||||
> follow-up commit 处理 —— 不用操心。
|
||||
|
||||
## Rule 1: 一个 PR 只做一件不可再拆的事
|
||||
|
||||
一个 PR 必须包含且仅包含一个 feature 或一个 bug 修复,而且这个单元不能再被拆分。
|
||||
如果你的改动可以被拆成多个每个都有独立价值的部分,就拆成多个 PR 分别提交。
|
||||
|
||||
### 自检
|
||||
|
||||
提交前,先让你的 AI 做一次拆分分析:
|
||||
|
||||
> "分析下面这一坨 diff,它能不能拆成多个独立的 PR,每个都可以独立 merge 或独立
|
||||
> revert?如果可以,列出来。"
|
||||
|
||||
如果回答是"可以,N 个 PR",就先拆再提。
|
||||
|
||||
### Examples
|
||||
|
||||
**✅ 可以作为一个 PR 的 bug** —— 单一根因,单一修复
|
||||
- `图片只指定 width 时 height fallback 错了`
|
||||
- `body 级 find: 锚点抛 ArgumentException`
|
||||
- `AddParagraph --index N 在 body 含 table 时偏移`
|
||||
|
||||
**✅ 可以作为一个 PR 的 feature** —— 单一 coherent 能力
|
||||
- `query ole: 列出所有嵌入的 OLE 对象及其 ProgID 和尺寸`
|
||||
- `set wrap/hposition/vposition on floating pictures`
|
||||
|
||||
**❌ 必须拆** —— 多个独立改动被打包
|
||||
- `修图片索引 bug + 加 OLE 检测 + 加 HTML heading 编号`
|
||||
→ 3 个 PR,零共享代码
|
||||
- `加 OLE 对象检测 + 加 EMF→PNG 转换`
|
||||
→ 2 个 PR,两个独立 layer
|
||||
- `加自动宽高比 + 修索引 off-by-one + 修行距裁剪`
|
||||
→ 3 个 PR,三个不相关的根因
|
||||
|
||||
**🤔 可拆可不拆** —— 默认选拆
|
||||
- `加一个 helper 函数 + 第一处调用者`
|
||||
→ 1 或 2 个 PR;helper 有独立复用价值就拆
|
||||
- `加 read 支持 + 加 write 支持(同一属性)`
|
||||
→ 1 或 2 个 PR;希望 read 先被 vet 就拆
|
||||
|
||||
## Rule 2: 每个 PR 必须附带可验证的验证方法
|
||||
|
||||
在 PR description 或关联 issue 里写清楚:reviewer 怎么才能验证你的改动真的有效。
|
||||
|
||||
### Bug 修复 PR —— 至少给出一种(按优先顺序)
|
||||
|
||||
1. **officecli 命令序列**,展示改动前的错误输出和改动后的正确输出
|
||||
2. **shell 或 python 脚本**,能复现 bug、在修复后干净退出
|
||||
3. **权威文档引用**,说明正确行为应该是什么样(OOXML spec、Microsoft / ECMA
|
||||
文档等)
|
||||
4. **截图** —— 仅当 bug 纯粹是视觉问题时
|
||||
|
||||
### Feature PR —— 至少包含
|
||||
|
||||
- **一张截图**,展示 feature 实际效果(Word / Excel / PowerPoint 窗口、HTML
|
||||
预览、或终端输出)
|
||||
- 可选:一段 shell 命令序列说明如何触发这个 feature
|
||||
|
||||
### Examples
|
||||
|
||||
**Bug 修复 —— 命令序列格式(最理想):**
|
||||
|
||||
```bash
|
||||
# Before my fix:
|
||||
officecli blank test.docx
|
||||
officecli add test.docx picture --prop "path=photo-2x1.png" --prop "width=10cm"
|
||||
officecli query test.docx picture
|
||||
# → height: "10.2cm" ❌ 错(硬编码 4 英寸 fallback)
|
||||
|
||||
# After my fix:
|
||||
officecli blank test.docx
|
||||
officecli add test.docx picture --prop "path=photo-2x1.png" --prop "width=10cm"
|
||||
officecli query test.docx picture
|
||||
# → height: "5.0cm" ✓ 对(根据 2:1 像素比例自动计算)
|
||||
```
|
||||
|
||||
**Feature —— 截图格式(最理想):**
|
||||
|
||||
> **标题自动编号(从 style chain 解析)**
|
||||
>
|
||||
> Before: ![heading-before.png] (纯 "Chapter One",无编号)
|
||||
> After: ![heading-after.png] ("1. Chapter One",带自动编号 span)
|
||||
>
|
||||
> 如何触发:
|
||||
> ```bash
|
||||
> officecli blank demo.docx
|
||||
> officecli add demo.docx paragraph --prop "style=Heading1" --prop "text=Chapter One"
|
||||
> officecli watch demo.docx
|
||||
> ```
|
||||
|
||||
## 如果你不遵守这两条规则
|
||||
|
||||
维护者保留以下两种处理方式。
|
||||
|
||||
### Option A —— 拒绝并要求重新提交(首选)
|
||||
|
||||
维护者关闭 PR,留一条指向本 guide 的 comment,请你按规则拆分后重新提交。
|
||||
|
||||
**你的 credit:** PR 完全归你,重新提交成功后仍然拿 **"Merged"** badge。
|
||||
|
||||
### Option B —— Cherry-pick 有价值的部分(最后手段)
|
||||
|
||||
如果你的 PR 里有一部分明显有价值、值得保留,维护者会用 `git cherry-pick` 直接把
|
||||
这些 commit 摘到 `main`,然后关闭原 PR。
|
||||
|
||||
**你的 credit:**
|
||||
- `git cherry-pick` 保留原作者,所以 `git log` 和 `git blame` 里那些代码行仍然
|
||||
显示你是作者。
|
||||
- 维护者创建的 reconcile commit message 会附带
|
||||
`Co-authored-by: <you> <your-email>` trailer,GitHub 贡献图会把它算进你的
|
||||
contribution。
|
||||
- **但原 PR 会显示为 "Closed" 而不是 "Merged"**。
|
||||
@@ -0,0 +1,203 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. Please also get an
|
||||
OpenSourceInitiative.org approved license identifier and put it
|
||||
in the first line of your license text file.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Copyright 2026 OfficeCLI (https://OfficeCLI.AI)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
permissions and limitations under the License.
|
||||
@@ -0,0 +1,13 @@
|
||||
OfficeCLI
|
||||
Copyright 2026 OfficeCLI (https://OfficeCLI.AI)
|
||||
|
||||
Created and maintained by goworm.
|
||||
|
||||
This product is licensed under the Apache License, Version 2.0.
|
||||
You may obtain a copy of the License in the LICENSE file or at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
This NOTICE file is part of the required attribution under
|
||||
Section 4 of the Apache License, Version 2.0. Redistributions
|
||||
of this work, with or without modification, must retain this
|
||||
notice.
|
||||
@@ -0,0 +1,693 @@
|
||||
# OfficeCLI
|
||||
|
||||
> **OfficeCLI is the world's first and the best Office suite designed for AI agents.**
|
||||
|
||||
**Give any AI agent full control over Word, Excel, and PowerPoint — in one line of code.**
|
||||
|
||||
Open-source. Single binary. No Office installation. No dependencies. Works everywhere.
|
||||
|
||||
**OfficeCLI's built-in HTML rendering engine reproduces documents with high fidelity — and that's what gives AI eyes.** It renders `.docx` / `.xlsx` / `.pptx` to HTML or PNG, closing the *render → look → fix* loop.
|
||||
|
||||
[](https://github.com/iOfficeAI/OfficeCLI/releases)
|
||||
[](LICENSE)
|
||||
|
||||
**English** | [中文](README_zh.md) | [日本語](README_ja.md) | [한국어](README_ko.md)
|
||||
|
||||
<p align="center">
|
||||
<strong>🌐 Website:</strong> <a href="https://officecli.ai" target="_blank">officecli.ai</a> | <strong>💬 Community:</strong> <a href="https://discord.gg/2QAwJn7Egx" target="_blank">Discord</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/ppt-process.webp" alt="OfficeCLI creating a PowerPoint presentation on AionUi" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><em>PPT creation process using OfficeCLI on <a href="https://github.com/iOfficeAI/AionUi">AionUi</a></em></p>
|
||||
|
||||
<p align="center"><strong>PowerPoint Presentations</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/designwhatmovesyou.gif" alt="OfficeCLI design presentation (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/horizon.gif" alt="OfficeCLI business presentation (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/efforless.gif" alt="OfficeCLI tech presentation (PowerPoint)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/blackhole.gif" alt="OfficeCLI space presentation (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/first-ppt-aionui.gif" alt="OfficeCLI gaming presentation (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/shiba.gif" alt="OfficeCLI creative presentation (PowerPoint)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Word Documents</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/word1.gif" alt="OfficeCLI academic paper (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word2.gif" alt="OfficeCLI project proposal (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word3.gif" alt="OfficeCLI annual report (Word)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Excel Spreadsheets</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/excel1.gif" alt="OfficeCLI budget tracker (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel2.gif" alt="OfficeCLI gradebook (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel3.gif" alt="OfficeCLI sales dashboard (Excel)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center"><em>All documents above were created entirely by AI agents using OfficeCLI — no templates, no manual editing.</em></p>
|
||||
|
||||
## For AI Agents — Get Started in One Line
|
||||
|
||||
Paste this into your AI agent's chat — it will read the skill file and install everything automatically:
|
||||
|
||||
```
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
That's it. The skill file teaches the agent how to install the binary and use all commands.
|
||||
|
||||
## For Humans
|
||||
|
||||
**Option A — GUI:** Install [**AionUi**](https://github.com/iOfficeAI/AionUi) — a desktop app that lets you create and edit Office documents through natural language, powered by OfficeCLI under the hood. Just describe what you want, and AionUi handles the rest.
|
||||
|
||||
**Option B — CLI:** Download the binary for your platform from [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases), then run:
|
||||
|
||||
```bash
|
||||
officecli install
|
||||
```
|
||||
|
||||
This copies the binary to your PATH and installs the **officecli skill** into every AI coding agent it detects — Claude Code, Cursor, Windsurf, GitHub Copilot, and more. Your agent can immediately create, read, and edit Office documents on your behalf, no extra configuration needed.
|
||||
|
||||
## For Developers — See It Live in 30 Seconds
|
||||
|
||||
```bash
|
||||
# 1. Install (macOS / Linux) — or: brew install officecli / npm install -g @officecli/officecli
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
# Windows (PowerShell): irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
|
||||
# 2. Create a blank PowerPoint
|
||||
officecli create deck.pptx
|
||||
|
||||
# 3. Start live preview — opens http://localhost:26315 in your browser
|
||||
officecli watch deck.pptx
|
||||
|
||||
# 4. Open another terminal, add a slide — watch the browser update instantly
|
||||
officecli add deck.pptx / --type slide --prop title="Hello, World!"
|
||||
```
|
||||
|
||||
That's it. Every `add`, `set`, or `remove` command you run will refresh the preview in real time. Keep experimenting — the browser is your live feedback loop.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Create a presentation and add content
|
||||
officecli create deck.pptx
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report" --prop background=1A1A2E
|
||||
officecli add deck.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue grew 25%" --prop x=2cm --prop y=5cm \
|
||||
--prop font=Arial --prop size=24 --prop color=FFFFFF
|
||||
|
||||
# View as outline
|
||||
officecli view deck.pptx outline
|
||||
# → Slide 1: Q4 Report
|
||||
# → Shape 1 [TextBox]: Revenue grew 25%
|
||||
|
||||
# View as HTML — opens a rendered preview in your browser, no server needed
|
||||
officecli view deck.pptx html
|
||||
|
||||
# Get structured JSON for any element
|
||||
officecli get deck.pptx '/slide[1]/shape[1]' --json
|
||||
|
||||
# Save and close — flushes the resident session to disk
|
||||
officecli close deck.pptx
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": "shape",
|
||||
"path": "/slide[1]/shape[1]",
|
||||
"attributes": {
|
||||
"name": "TextBox 1",
|
||||
"text": "Revenue grew 25%",
|
||||
"x": "720000",
|
||||
"y": "1800000"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Why OfficeCLI?
|
||||
|
||||
What used to take 50 lines of Python and 3 separate libraries:
|
||||
|
||||
```python
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches, Pt
|
||||
prs = Presentation()
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[0])
|
||||
title = slide.shapes.title
|
||||
title.text = "Q4 Report"
|
||||
# ... 45 more lines ...
|
||||
prs.save('deck.pptx')
|
||||
```
|
||||
|
||||
Now takes one command:
|
||||
|
||||
```bash
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report"
|
||||
```
|
||||
|
||||
**What OfficeCLI can do:**
|
||||
|
||||
- **Create** documents from scratch -- blank or with content
|
||||
- **Read** text, structure, styles, formulas -- in plain text or structured JSON
|
||||
- **Analyze** formatting issues, style inconsistencies, and structural problems
|
||||
- **Modify** any element -- text, fonts, colors, layout, formulas, charts, images
|
||||
- **Reorganize** content -- add, remove, move, copy elements across documents
|
||||
|
||||
| Format | Read | Modify | Create |
|
||||
|--------|------|--------|--------|
|
||||
| Word (.docx) | ✅ | ✅ | ✅ |
|
||||
| Excel (.xlsx) | ✅ | ✅ | ✅ |
|
||||
| PowerPoint (.pptx) | ✅ | ✅ | ✅ |
|
||||
|
||||
**Word** — full [i18n & RTL support](https://github.com/iOfficeAI/OfficeCLI/wiki/i18n) (per-script font slots, per-script BCP-47 lang tags `lang.latin/ea/cs`, complex-script bold/italic/size, `direction=rtl` cascading through paragraph/run/section/table/style/header/footer/docDefaults, `rtlGutter` + `pgBorders` shorthand, locale-aware page numbering for Hindi/Arabic/Thai/CJK; `create --locale ar-SA` auto-enables RTL), [paragraphs](https://github.com/iOfficeAI/OfficeCLI/wiki/word-paragraph) (framePr, tabs shorthand, char-based indents), [runs](https://github.com/iOfficeAI/OfficeCLI/wiki/word-run) (underline.color, position half-pts), [tables](https://github.com/iOfficeAI/OfficeCLI/wiki/word-table) (virtual column ops add/remove/move/copyfrom, hMerge), [styles](https://github.com/iOfficeAI/OfficeCLI/wiki/word-style), [textbox](https://github.com/iOfficeAI/OfficeCLI/wiki/word-textbox) / [shape](https://github.com/iOfficeAI/OfficeCLI/wiki/word-shape) (textbox: rotation, `textDirection` `eaVert`/`vert270`, gradient, shadow, opacity), [headers/footers](https://github.com/iOfficeAI/OfficeCLI/wiki/word-header-footer), [images](https://github.com/iOfficeAI/OfficeCLI/wiki/word-picture) (PNG/JPG/GIF/SVG), [equations](https://github.com/iOfficeAI/OfficeCLI/wiki/word-equation) (LaTeX input), [diagrams](https://github.com/iOfficeAI/OfficeCLI/wiki/diagram) (mermaid → native editable shapes, or any mermaid type as a full-fidelity PNG), [comments](https://github.com/iOfficeAI/OfficeCLI/wiki/word-comment), [footnotes](https://github.com/iOfficeAI/OfficeCLI/wiki/word-footnote), [watermarks](https://github.com/iOfficeAI/OfficeCLI/wiki/word-watermark), [bookmarks](https://github.com/iOfficeAI/OfficeCLI/wiki/word-bookmark), [TOC](https://github.com/iOfficeAI/OfficeCLI/wiki/word-toc), [charts](https://github.com/iOfficeAI/OfficeCLI/wiki/word-chart), [hyperlinks](https://github.com/iOfficeAI/OfficeCLI/wiki/word-hyperlink), [sections](https://github.com/iOfficeAI/OfficeCLI/wiki/word-section), [form fields](https://github.com/iOfficeAI/OfficeCLI/wiki/word-formfield), [content controls (SDT)](https://github.com/iOfficeAI/OfficeCLI/wiki/word-sdt), [fields](https://github.com/iOfficeAI/OfficeCLI/wiki/word-field) (22 zero-param types + MERGEFIELD / REF / PAGEREF / SEQ / STYLEREF / DOCPROPERTY / IF), [OLE objects](https://github.com/iOfficeAI/OfficeCLI/wiki/word-ole), [revisions / tracked changes](https://github.com/iOfficeAI/OfficeCLI/wiki/word-revision) (`revision.type=ins\|del\|format\|moveFrom\|moveTo` + `revision.action=accept\|reject`, per-target `/revision[@author=Alice]` selector, tracked Find&Replace), page background color, [document properties](https://github.com/iOfficeAI/OfficeCLI/wiki/word-document)
|
||||
|
||||
**Excel** — [cells](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-cell) (phonetic guide / furigana on add, Excel-UI `--shift left\|up` on remove / `shift=right\|down` on add), formulas (350+ built-in functions with auto-evaluation, spilling dynamic arrays with `_xlfn.` auto-prefix, financial / bond and statistical families, OFFSET/INDIRECT, defined-name formula bodies inlined at parse, formula-ref rewrite on row/col insert), [sheets](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sheet) (visible/hidden/veryHidden, print margins, printTitleRows/Cols, RTL `sheetView`, cascade-aware sheet rename, empty-cell bloat filter on open), boolean `and`/`or` selectors (`row[Salary>5000 and Region=EMEA]`), [tables](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-table), [sort](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sort) (sheet / range, multi-key, sidecar-aware), [conditional formatting](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-conditionalformatting), [charts](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart) (including box-whisker, [pareto](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart-add) with auto-sort + cumulative-%, log axis), [pivot tables](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-pivottable) (multi-field, date grouping, showDataAs, sort, grandTotals, subtotals, compact/outline/tabular layout, repeat item labels, blank rows, calculated fields, persistent `labelFilter` / `topN` filters, cache CoW + cross-pivot sharing), [slicers](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-slicer), [named ranges](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-namedrange), [data validation](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-validation), [images](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-picture) (PNG/JPG/GIF/SVG with dual-representation fallback), [sparklines](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sparkline), [comments](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-comment) (RTL), [autofilter](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-autofilter), [shapes](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-shape), [OLE objects](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-ole), CSV/TSV import, `$Sheet:A1` cell addressing
|
||||
|
||||
**PowerPoint** — [slides](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-slide) (header/footer/date/slidenum toggles, hidden), [shapes](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-shape) (pattern fill, blur effect, hyperlink tooltip + slide-jump links, **highlight color** on runs, `slideMaster`/`slideLayout` typed add/set/remove, arrow alias, effective.X + effective.X.src), [images](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-picture) (PNG/JPG/GIF/SVG, fill modes: stretch/contain/cover/tile, brightness/contrast/glow/shadow, rotation, link + tooltip), [tables](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-table) (built-in PowerPoint style catalogue, virtual `/col[C]` get + swap/copyFrom, row/col Move/CopyFrom, fill/background alias), [charts](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-chart) (pieOfPie, barOfPie, per-attr axisLine/gridline setters, series add/remove with theme palette, `anchor=x,y,w,h` shorthand), [animations](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-shape-set) (15 emphasis + 16 exit template-backed presets, multi-effect chains, motion-path presets, repeat/restart/autoReverse, chart animations + `chartBuild`), [transitions](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-morph-check) (morph + p14 + 12 p15 PowerPoint 2013+ presets), [3D models (.glb)](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-3dmodel) (combined `rotation=ax,ay,az`), [slide zoom](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-zoom), [equations](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-equation) (LaTeX input), [diagrams](https://github.com/iOfficeAI/OfficeCLI/wiki/diagram) (mermaid flowchart / sequence → native editable shapes, or any mermaid type as a full-fidelity PNG), [themes](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-theme), [connectors](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-connector) (`from`/`to` accept a full `/slide[N]/shape[@name=Foo]` path), [video/audio](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-video) (loop, autoStart), [groups](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-group) (link + tooltip; Get/Query/Add/Remove all descend into groups), [notes](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-notes) (RTL, lang), [comments](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-comment) (RTL, legacy + modern p188 threaded round-trip), SmartArt (round-trip via add-part + raw-set), [OLE objects](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-ole), [placeholders](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-placeholder) (add/set by phType)
|
||||
|
||||
## Use Cases
|
||||
|
||||
**For Developers:**
|
||||
- Automate report generation from databases or APIs
|
||||
- Batch-process documents (bulk find/replace, style updates)
|
||||
- Build document pipelines in CI/CD environments (generate docs from test results)
|
||||
- Headless Office automation in Docker/containerized environments
|
||||
|
||||
**For AI Agents:**
|
||||
- Generate presentations from user prompts (see examples above)
|
||||
- Extract structured data from documents to JSON
|
||||
- Validate and check document quality before delivery
|
||||
|
||||
**For Teams:**
|
||||
- Clone document templates and populate with data
|
||||
- Automated document validation in CI/CD pipelines
|
||||
|
||||
## Installation
|
||||
|
||||
Ships as a single self-contained binary. The .NET runtime is embedded -- nothing to install, no runtime to manage.
|
||||
|
||||
**One-line install:**
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
**Or via a package manager:**
|
||||
|
||||
```bash
|
||||
# Homebrew (macOS / Linux)
|
||||
brew install officecli
|
||||
|
||||
# Scoop (Windows)
|
||||
scoop install officecli
|
||||
|
||||
# npm (all platforms — fetches the native binary for your platform)
|
||||
npm install -g @officecli/officecli
|
||||
```
|
||||
|
||||
**Or download manually** from [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases):
|
||||
|
||||
| Platform | Binary |
|
||||
|----------|--------|
|
||||
| macOS Apple Silicon | `officecli-mac-arm64` |
|
||||
| macOS Intel | `officecli-mac-x64` |
|
||||
| Linux x64 | `officecli-linux-x64` |
|
||||
| Linux ARM64 | `officecli-linux-arm64` |
|
||||
| Windows x64 | `officecli-win-x64.exe` |
|
||||
| Windows ARM64 | `officecli-win-arm64.exe` |
|
||||
|
||||
Verify installation: `officecli --version`
|
||||
|
||||
**Or self-install from a downloaded binary (or run bare `officecli` to auto-install):**
|
||||
|
||||
```bash
|
||||
officecli install # explicit
|
||||
officecli # bare invocation also triggers install
|
||||
```
|
||||
|
||||
Updates are checked automatically in the background. Disable with `officecli config autoUpdate false` or skip per-invocation with `OFFICECLI_SKIP_UPDATE=1`. Configuration lives under `~/.officecli/config.json`.
|
||||
|
||||
## Key Features
|
||||
|
||||
### Built-in Engines & Generation Primitives
|
||||
|
||||
OfficeCLI is self-contained. The capabilities below ship inside the binary — **no Office required**.
|
||||
|
||||
#### Rendering engine — high-fidelity, built-in
|
||||
|
||||
OfficeCLI's keystone: a from-scratch, high-fidelity HTML rendering engine that lets an AI agent *see* the rendered document instead of guessing from the DOM. It covers shapes, charts (trendlines, error bars, waterfall, candlestick, sparklines), equations (OMML → LaTeX, rendered with KaTeX), 3D `.glb` models via Three.js, morph transitions, slide zoom, and shape effects. Per-page PNG screenshots are produced by piping the rendered HTML through a headless browser. Three modes:
|
||||
|
||||
- **`view html`** — standalone HTML file, assets inlined. Open in any browser.
|
||||
- **`view screenshot`** — per-page PNG, ready for multimodal agents to read.
|
||||
- **`watch`** — local HTTP server with auto-refreshing preview; every `add` / `set` / `remove` updates the browser instantly. Excel watch supports inline cell editing and drag-to-reposition charts.
|
||||
|
||||
```bash
|
||||
officecli view deck.pptx html -o /tmp/deck.html
|
||||
officecli view deck.pptx screenshot -o /tmp/deck.png # add --page 1-N for more slides
|
||||
officecli watch deck.pptx # http://localhost:26315
|
||||
```
|
||||
|
||||
> Without visualization, an agent generating slides is flying blind — it can read the DOM but can't tell if the title overflows or two shapes overlap. Because rendering is built into the binary, the *render → look → fix* loop works in CI, in Docker, on a server with no display — anywhere the binary runs.
|
||||
|
||||
#### Formula & pivot engine
|
||||
|
||||
350+ built-in Excel functions evaluated automatically on write — write `=SUM(A1:A2)`, `get` the cell, the value is already there. No round-trip through Office to recalc. Covers spilling dynamic arrays (`FILTER` / `SORT` / `UNIQUE` / `SEQUENCE` / `LET` / `LAMBDA` / `MAP`), `VLOOKUP` / `XLOOKUP` / `INDEX` / `MATCH`, financial & bond math (`XIRR` / `PRICE` / `YIELD` / `DURATION` / `COUPNUM`), statistical distributions, tests & regression (`NORM.DIST` / `T.TEST` / `LINEST`), and date & text functions.
|
||||
|
||||
Plus native OOXML pivot tables from a source range with one command — multi-field rows/cols/filters, 10 aggregations, `showDataAs` modes, date grouping, calculated fields, top-N, layouts. Pivot cache + definition are written to OOXML, so Excel opens the file with the aggregation already populated:
|
||||
|
||||
```bash
|
||||
officecli add sales.xlsx '/Sheet1' --type pivottable \
|
||||
--prop source='Data!A1:E10000' --prop rows='Region,Category' \
|
||||
--prop cols=Quarter --prop values='Revenue:sum,Units:avg' \
|
||||
--prop showDataAs=percentOfTotal
|
||||
```
|
||||
|
||||
#### Template merge — generate once, fill many
|
||||
|
||||
`merge` replaces `{{key}}` placeholders in any `.docx` / `.xlsx` / `.pptx` with JSON data — across paragraphs, table cells, shapes, headers, footers, and chart titles. Agent designs the layout once (expensive); production code fills it N times (cheap, deterministic, zero token cost). Avoids the failure mode where an agent regenerates each report from scratch and produces N inconsistent layouts.
|
||||
|
||||
```bash
|
||||
officecli merge invoice-template.docx out-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
officecli merge q4-template.pptx q4-acme.pptx data.json
|
||||
```
|
||||
|
||||
#### Round-trip dump — learn from existing docs
|
||||
|
||||
`dump` serializes any `.docx`, `.pptx`, or `.xlsx` — whole document **or any subtree** (a single paragraph, table, slide, worksheet, the styles part, numbering, theme, or settings) — into a replayable batch JSON; `batch` replays it. Given a sample the user wants to imitate, an agent reads the structured spec instead of raw OOXML XML, mutates, and replays. Bridges "I have an existing template" and "generate me 100 variations."
|
||||
|
||||
```bash
|
||||
officecli dump existing.docx -o blueprint.json # whole document
|
||||
officecli dump existing.docx /body/tbl[1] -o table.json # any subtree
|
||||
officecli dump existing.xlsx /Sheet1 -o sheet.json # a single worksheet
|
||||
officecli batch new.docx --input blueprint.json
|
||||
```
|
||||
|
||||
### Resident Mode & Batch
|
||||
|
||||
For multi-step workflows, resident mode keeps the document in memory. Batch mode applies multiple operations in a single pass.
|
||||
|
||||
```bash
|
||||
# Resident mode — near-zero latency via named pipes
|
||||
officecli open report.docx
|
||||
officecli set report.docx /body/p[1]/r[1] --prop bold=true
|
||||
officecli set report.docx /body/p[2]/r[1] --prop color=FF0000
|
||||
officecli close report.docx
|
||||
|
||||
# Batch mode — multi-command execution (continues on error by default; --stop-on-error to abort)
|
||||
echo '[{"command":"set","path":"/slide[1]/shape[1]","props":{"text":"Hello"}},
|
||||
{"command":"set","path":"/slide[1]/shape[2]","props":{"fill":"FF0000"}}]' \
|
||||
| officecli batch deck.pptx --json
|
||||
|
||||
# Inline batch with --commands (no stdin needed)
|
||||
officecli batch deck.pptx --commands '[{"op":"set","path":"/slide[1]/shape[1]","props":{"text":"Hi"}}]'
|
||||
|
||||
# Abort on the first failing command (default is continue-on-error)
|
||||
officecli batch deck.pptx --input updates.json --stop-on-error --json
|
||||
```
|
||||
|
||||
> **Reading the file with another tool? Flush to disk first.**
|
||||
> officecli's own reads (`get`/`query`/`view`) always see your latest edits, so within officecli you never need to save. But a live resident defers the disk write, so **before a non‑officecli program reads the file** — python‑docx/openpyxl, Microsoft Word, a renderer, delivery/upload — flush it:
|
||||
> ```bash
|
||||
> officecli set report.docx /body/p[1] --prop bold=true
|
||||
> officecli save report.docx # flush, keep the resident warm (or `close` to flush + release)
|
||||
> python my_reader.py report.docx # now sees the edit
|
||||
> ```
|
||||
> A live resident also auto‑flushes shortly after going idle (adaptive 2–10s, scaled to the document's measured save cost). For a pipeline where another program reads after every command, set `OFFICECLI_RESIDENT_FLUSH=each` — every mutation is on disk before the command returns, while the resident stays warm. Full flush model (`each`/`auto`/fixed/`off`, save / close, env tuning): [wiki → open / close](https://github.com/iOfficeAI/OfficeCLI/wiki/command-open#when-the-file-on-disk-is-refreshed).
|
||||
|
||||
### Three-Layer Architecture
|
||||
|
||||
Start simple, go deep only when needed.
|
||||
|
||||
| Layer | Purpose | Commands |
|
||||
|-------|---------|----------|
|
||||
| **L1: Read** | Semantic views of content | `view` (text, annotated, outline, stats, issues, html, svg, screenshot) |
|
||||
| **L2: DOM** | Structured element operations | `get`, `query`, `set`, `add`, `remove`, `move`, `swap` |
|
||||
| **L3: Raw XML** | Direct XPath access — universal fallback | `raw`, `raw-set`, `add-part`, `validate` |
|
||||
|
||||
```bash
|
||||
# L1 — high-level views
|
||||
officecli view report.docx annotated
|
||||
officecli view budget.xlsx text --cols A,B,C --max-lines 50
|
||||
|
||||
# L2 — element-level operations
|
||||
officecli query report.docx "run:contains(TODO)"
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q2 Report"
|
||||
officecli move report.docx /body/p[5] --to /body --index 1
|
||||
|
||||
# L3 — raw XML when L2 isn't enough
|
||||
officecli raw deck.pptx '/slide[1]'
|
||||
officecli raw-set report.docx document \
|
||||
--xpath "//w:p[1]" --action append \
|
||||
--xml '<w:r><w:t>Injected text</w:t></w:r>'
|
||||
```
|
||||
|
||||
## AI Integration
|
||||
|
||||
### MCP Server
|
||||
|
||||
Built-in [MCP](https://modelcontextprotocol.io) server — register with one command:
|
||||
|
||||
```bash
|
||||
officecli mcp claude # Claude Code
|
||||
officecli mcp cursor # Cursor
|
||||
officecli mcp vscode # VS Code / Copilot
|
||||
officecli mcp lmstudio # LM Studio
|
||||
officecli mcp list # Check registration status
|
||||
```
|
||||
|
||||
Exposes all document operations as tools over JSON-RPC — no shell access needed.
|
||||
|
||||
### Direct CLI Integration
|
||||
|
||||
Get OfficeCLI working with your AI agent in two steps:
|
||||
|
||||
1. **Install the binary** -- one command (see [Installation](#installation))
|
||||
2. **Done.** OfficeCLI automatically detects your AI tools (Claude Code, GitHub Copilot, Codex) by checking known config directories and installs its skill file. Your agent can immediately create, read, and modify any Office document.
|
||||
|
||||
<details>
|
||||
<summary><strong>Manual setup (optional)</strong></summary>
|
||||
|
||||
If auto-install doesn't cover your setup, you can install the skill file manually:
|
||||
|
||||
**Feed SKILL.md to your agent directly:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
**Install as a local skill for Claude Code:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md -o ~/.claude/skills/officecli.md
|
||||
```
|
||||
|
||||
**Other agents:** Include the contents of `SKILL.md` in your agent's system prompt or tool description.
|
||||
|
||||
</details>
|
||||
|
||||
### Why your agent will thrive on OfficeCLI
|
||||
|
||||
- **Deterministic JSON output** — every command supports `--json` with consistent schemas. No regex parsing, no scraping stdout.
|
||||
- **Path-based addressing** — every element has a stable path (`/slide[1]/shape[2]`). Agents navigate documents without understanding XML namespaces. (OfficeCLI syntax: 1-based indexing, element local names — not XPath.)
|
||||
- **Progressive complexity (L1 → L2 → L3)** — agents start with read-only views, escalate to DOM ops, fall back to raw XML only when needed. Minimizes token usage.
|
||||
- **Self-healing workflow** — `validate`, `view issues`, and the structured error codes (`not_found`, `invalid_value`, `unsupported_property`) return suggestions and valid ranges. Agents self-correct without human intervention.
|
||||
- **Built-in agent-friendly rendering engine** — `view html` / `view screenshot` / `watch` emit HTML and PNG natively. No Office required. Agents can *see* their output and fix layout issues, even inside CI / Docker / headless environments.
|
||||
- **Built-in formula & pivot engine** — 350+ Excel functions auto-evaluated on write (incl. spilling dynamic arrays, financial / bond and statistical families); native OOXML pivot tables from a source range with one command. Agents read computed values and shipped aggregations immediately, without round-tripping through Office.
|
||||
- **Template merge** — agent designs the layout once, downstream code fills `{{key}}` placeholders N times. Avoids burning tokens regenerating every report from scratch.
|
||||
- **Round-trip dump** — `dump` turns any `.docx`, `.pptx`, or `.xlsx` into replayable batch JSON. Agents learn from human-authored samples by reading a structured spec, not raw OOXML XML.
|
||||
- **Built-in help** — when unsure about property names or value formats, the agent runs `officecli <format> set <element>` instead of guessing.
|
||||
- **Auto-install** — OfficeCLI detects your AI tooling (Claude Code, Cursor, VS Code, …) and configures itself. No manual skill-file setup.
|
||||
|
||||
### Built-in Help
|
||||
|
||||
Don't guess property names — drill into the help:
|
||||
|
||||
```bash
|
||||
officecli pptx set # All settable elements and properties
|
||||
officecli pptx set shape # Detail for one element type
|
||||
officecli pptx set shape.fill # One property: format and examples
|
||||
officecli docx query # Selector reference: attributes, :contains, :has(), etc.
|
||||
```
|
||||
|
||||
Run `officecli --help` for the full overview.
|
||||
|
||||
### JSON Output Schemas
|
||||
|
||||
All commands support `--json`. The general response shapes:
|
||||
|
||||
**Single element** (`get --json`):
|
||||
|
||||
```json
|
||||
{"tag": "shape", "path": "/slide[1]/shape[1]", "attributes": {"name": "TextBox 1", "text": "Hello"}}
|
||||
```
|
||||
|
||||
**List of elements** (`query --json`):
|
||||
|
||||
```json
|
||||
[
|
||||
{"tag": "paragraph", "path": "/body/p[1]", "attributes": {"style": "Heading1", "text": "Title"}},
|
||||
{"tag": "paragraph", "path": "/body/p[5]", "attributes": {"style": "Heading1", "text": "Summary"}}
|
||||
]
|
||||
```
|
||||
|
||||
**Errors** return a non-zero exit code with a structured error object including error code, suggestion, and valid values when available:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"error": "Slide 50 not found (total: 8)",
|
||||
"code": "not_found",
|
||||
"suggestion": "Valid Slide index range: 1-8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Error codes: `not_found`, `invalid_value`, `unsupported_property`, `invalid_path`, `unsupported_type`, `missing_property`, `file_not_found`, `file_locked`, `invalid_selector`. Property names are auto-corrected -- misspelling a property returns a suggestion with the closest match.
|
||||
|
||||
**Error Recovery** -- Agents self-correct by inspecting available elements:
|
||||
|
||||
```bash
|
||||
# Agent tries an invalid path
|
||||
officecli get report.docx /body/p[99] --json
|
||||
# Returns: {"success": false, "error": {"error": "...", "code": "not_found", "suggestion": "..."}}
|
||||
|
||||
# Agent self-corrects by checking available elements
|
||||
officecli get report.docx /body --depth 1 --json
|
||||
# Returns the list of available children, agent picks the right path
|
||||
```
|
||||
|
||||
**Mutation confirmations** (`set`, `add`, `remove`, `move`, `create` with `--json`):
|
||||
|
||||
```json
|
||||
{"success": true, "path": "/slide[1]/shape[1]"}
|
||||
```
|
||||
|
||||
See `officecli --help` for full details on exit codes and error formats.
|
||||
|
||||
## Comparison
|
||||
|
||||
| | OfficeCLI | Microsoft Office | LibreOffice | python-docx / openpyxl |
|
||||
|---|---|---|---|---|
|
||||
| Open source & free | ✓ (Apache 2.0) | ✗ (paid license) | ✓ | ✓ |
|
||||
| AI-native CLI + JSON | ✓ | ✗ | ✗ | ✗ |
|
||||
| Zero install (single binary) | ✓ | ✗ | ✗ | ✗ (Python + pip) |
|
||||
| Call from any language | ✓ (CLI) | ✗ (COM/Add-in) | ✗ (UNO API) | Python only |
|
||||
| Path-based element access | ✓ | ✗ | ✗ | ✗ |
|
||||
| Raw XML fallback | ✓ | ✗ | ✗ | Partial |
|
||||
| Built-in agent-friendly rendering engine | ✓ | ✗ | ✗ | ✗ |
|
||||
| Headless HTML/PNG output | ✓ | ✗ | Partial | ✗ |
|
||||
| Template merge (`{{key}}`) across formats | ✓ | ✗ | ✗ | ✗ |
|
||||
| Round-trip dump → batch JSON | ✓ | ✗ | ✗ | ✗ |
|
||||
| Live preview (auto-refresh on edit) | ✓ | ✗ | ✗ | ✗ |
|
||||
| Headless / CI | ✓ | ✗ | Partial | ✓ |
|
||||
| Cross-platform | ✓ | Windows/Mac | ✓ | ✓ |
|
||||
| Word + Excel + PowerPoint | ✓ | ✓ | ✓ | Separate libs |
|
||||
|
||||
## Command Reference
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| [`create`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-create) | Create a blank .docx, .xlsx, or .pptx (type from extension) |
|
||||
| [`view`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-view) | View content (modes: `outline`, `text`, `annotated`, `stats` (`--page-count`), `issues`, `html`, `svg`, `screenshot`, `pdf` (via exporter plugin), `forms` (via format-handler plugin)). docx supports `--render auto\|native\|html`. |
|
||||
| [`load_skill`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-skills) | Print embedded SKILL.md content for a specialized skill (no install) |
|
||||
| [`get`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-get) | Get element and children (`--depth N`, `--json`) |
|
||||
| [`query`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-query) | CSS-like query with boolean `and`/`or`, row-by-column-name (`row[Salary>5000]`), `--find` flag |
|
||||
| [`set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-set) | Modify element properties; accepts selectors and Excel-native paths (parity with `get`/`query`), `--find`/`--replace` flags |
|
||||
| [`add`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-add) | Add element (or clone with `--from <path>`) |
|
||||
| [`remove`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-remove) | Remove an element |
|
||||
| [`move`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-move) | Move element (`--to <parent>`, `--index N`, `--after <path>`, `--before <path>`) |
|
||||
| [`swap`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-swap) | Swap two elements |
|
||||
| [`validate`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-validate) | Validate against OpenXML schema |
|
||||
| `view <file> issues` | Enumerate document issues (text overflow, missing alt text, formula errors, ...) |
|
||||
| [`batch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-batch) | Multiple operations applied in a single pass (stdin, `--input`, or `--commands`; continues on error by default, `--stop-on-error` to abort) |
|
||||
| [`dump`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-dump) | Serialize a `.docx`, `.pptx`, or `.xlsx` into a replayable batch JSON (round-trip via `batch`); accepts a subtree path |
|
||||
| [`refresh`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-refresh) | Recalculate TOC page numbers / `PAGE` / cross-references (`.docx`; Word backend on Windows, headless-HTML fallback) |
|
||||
| [`plugins`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-plugins) | List / inspect / lint installed plugins (extend to `.doc`, `.hwpx`, `.pdf` export via dump-reader / exporter / format-handler kinds) |
|
||||
| [`merge`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-merge) | Template merge — replace `{{key}}` placeholders with JSON data |
|
||||
| [`watch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-watch) | Live HTML preview in browser with auto-refresh |
|
||||
| [`mcp`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-mcp) | Start MCP server for AI tool integration |
|
||||
| [`raw`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | View raw XML of a document part |
|
||||
| [`raw-set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | Modify raw XML via XPath |
|
||||
| `add-part` | Add a new document part (header, chart, etc.) |
|
||||
| [`open`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-open) | Start resident mode (keep document in memory) |
|
||||
| `close` | Save and close resident mode |
|
||||
| [`install`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-install) | Install binary + skills + MCP (`all`, `claude`, `cursor`, etc.) |
|
||||
| `config` | Get or set configuration |
|
||||
| `<format> <command>` | [Built-in help](https://github.com/iOfficeAI/OfficeCLI/wiki/command-reference) (e.g. `officecli pptx set shape`) |
|
||||
|
||||
## End-to-End Workflow Example
|
||||
|
||||
A typical self-healing agent workflow: create a presentation, populate it, verify, and fix issues -- all without human intervention.
|
||||
|
||||
```bash
|
||||
# 1. Create
|
||||
officecli create report.pptx
|
||||
|
||||
# 2. Add content
|
||||
officecli add report.pptx / --type slide --prop title="Q4 Results"
|
||||
officecli add report.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue: $4.2M" --prop x=2cm --prop y=5cm --prop size=28
|
||||
officecli add report.pptx / --type slide --prop title="Details"
|
||||
officecli add report.pptx '/slide[2]' --type shape \
|
||||
--prop text="Growth driven by new markets" --prop x=2cm --prop y=5cm
|
||||
|
||||
# 3. Verify
|
||||
officecli view report.pptx outline
|
||||
officecli validate report.pptx
|
||||
|
||||
# 4. Fix any issues found
|
||||
officecli view report.pptx issues --json
|
||||
# Address issues based on output, e.g.:
|
||||
officecli set report.pptx '/slide[1]/shape[1]' --prop font=Arial
|
||||
```
|
||||
|
||||
### Units & Colors
|
||||
|
||||
All dimension and color properties accept flexible input formats:
|
||||
|
||||
| Type | Accepted formats | Examples |
|
||||
|------|-----------------|----------|
|
||||
| **Dimensions** | cm, in, pt, px, or raw EMU | `2cm`, `1in`, `72pt`, `96px`, `914400` |
|
||||
| **Colors** | Hex, named, RGB, theme | `#FF0000`, `FF0000`, `red`, `rgb(255,0,0)`, `accent1` |
|
||||
| **Font sizes** | Bare number or pt-suffixed | `14`, `14pt`, `10.5pt` |
|
||||
| **Spacing** | pt, cm, in, or multiplier | `12pt`, `0.5cm`, `1.5x`, `150%` |
|
||||
|
||||
## Common Patterns
|
||||
|
||||
```bash
|
||||
# Replace all Heading1 text in a Word doc
|
||||
officecli query report.docx "paragraph[style=Heading1]" --json | ...
|
||||
officecli set report.docx /body/p[1]/r[1] --prop text="New Title"
|
||||
|
||||
# Export all slide content as JSON
|
||||
officecli get deck.pptx / --depth 2 --json
|
||||
|
||||
# Bulk-update Excel cells
|
||||
officecli batch budget.xlsx --input updates.json --json
|
||||
|
||||
# Import CSV data into an Excel sheet
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q1 Data" --prop csv=sales.csv
|
||||
|
||||
# Template merge for batch reports
|
||||
officecli merge invoice-template.docx invoice-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
|
||||
# Check document quality before delivery
|
||||
officecli validate report.docx && officecli view report.docx issues --json
|
||||
```
|
||||
|
||||
**From Python or Node.js** — install one of the thin resident-pipe SDKs (no per-call process spawn):
|
||||
|
||||
```python
|
||||
# Python — `pip install officecli-sdk`
|
||||
from officecli import Doc
|
||||
with Doc("deck.pptx") as d:
|
||||
d.add("/", type="slide", title="Q4 Report")
|
||||
print(d.get("/slide[1]"))
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Node.js — `npm install @officecli/sdk`
|
||||
import { Doc } from "@officecli/sdk";
|
||||
await using d = await Doc.open("deck.pptx");
|
||||
await d.add("/", { type: "slide", title: "Q4 Report" });
|
||||
console.log(await d.get("/slide[1]"));
|
||||
```
|
||||
|
||||
Both SDKs auto-provision the native CLI when missing (mirror-first, Windows-capable) and announce the install rather than doing it silently.
|
||||
|
||||
Or wrap subprocess directly, one-shot:
|
||||
|
||||
```python
|
||||
import json, subprocess
|
||||
def cli(*args):
|
||||
return json.loads(subprocess.check_output(["officecli", *args, "--json"], text=True))
|
||||
cli("create", "deck.pptx")
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
The [Wiki](https://github.com/iOfficeAI/OfficeCLI/wiki) has detailed guides for every command, element type, and property:
|
||||
|
||||
- **By format:** [Word](https://github.com/iOfficeAI/OfficeCLI/wiki/word-reference) | [Excel](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-reference) | [PowerPoint](https://github.com/iOfficeAI/OfficeCLI/wiki/powerpoint-reference)
|
||||
- **Workflows:** [End-to-end examples](https://github.com/iOfficeAI/OfficeCLI/wiki/workflows) -- Word reports, Excel dashboards, PowerPoint decks, batch modifications, resident mode
|
||||
- **Runnable examples:** [examples/](examples/) -- copy-paste scripts (.sh/.py) for Word, Excel, and PowerPoint, with output files included
|
||||
- **Troubleshooting:** [Common errors and solutions](https://github.com/iOfficeAI/OfficeCLI/wiki/troubleshooting)
|
||||
- **AI agent guide:** [Decision tree for navigating the wiki](https://github.com/iOfficeAI/OfficeCLI/wiki/agent-guide)
|
||||
|
||||
## Build from Source
|
||||
|
||||
Requires [.NET 10 SDK](https://dotnet.microsoft.com/download) for compilation only. The output is a self-contained, native binary -- .NET is embedded in the binary and is not needed at runtime.
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[Apache License 2.0](LICENSE)
|
||||
|
||||
Bug reports and contributions are welcome on [GitHub Issues](https://github.com/iOfficeAI/OfficeCLI/issues).
|
||||
|
||||
---
|
||||
|
||||
If you find OfficeCLI useful, please [give it a star on GitHub](https://github.com/iOfficeAI/OfficeCLI) — it helps others discover the project.
|
||||
|
||||
[OfficeCLI.AI](https://OfficeCLI.AI) | [GitHub](https://github.com/iOfficeAI/OfficeCLI)
|
||||
|
||||
<!--
|
||||
yaml-frontmatter:
|
||||
name: officecli
|
||||
version: see GitHub Releases
|
||||
description: The standard CLI for AI agents to create, read, and modify Office documents
|
||||
formats: [docx, xlsx, pptx]
|
||||
platforms: [macos, linux, windows]
|
||||
license: Apache-2.0
|
||||
keywords: [office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint]
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
|
||||
<!-- LLM/agent discovery metadata
|
||||
tool: officecli
|
||||
type: cli
|
||||
canonical: the standard CLI for AI-driven Office document manipulation
|
||||
formats: docx, xlsx, pptx
|
||||
capabilities: create, read, modify, validate, batch, resident-mode, mcp-server, live-preview, auto-install
|
||||
output: json, text, annotated
|
||||
platforms: macos, linux, windows
|
||||
license: Apache-2.0
|
||||
keywords: office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint, ai-tools, command-line, structured-output
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
skill-file-lines: 403
|
||||
alternatives: python-docx, openpyxl, python-pptx, libreoffice --headless
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`iOfficeAI/OfficeCLI`
|
||||
- 原始仓库:https://github.com/iOfficeAI/OfficeCLI
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
@@ -0,0 +1,660 @@
|
||||
# OfficeCLI
|
||||
|
||||
> **OfficeCLI は世界初にして最高の、AI エージェント向けに設計された Office スイートです。**
|
||||
|
||||
**あらゆる AI エージェントに Word、Excel、PowerPoint の完全な制御権を — たった一行のコードで。**
|
||||
|
||||
オープンソース。単一バイナリ。Office のインストール不要。依存関係ゼロ。全プラットフォーム対応。
|
||||
|
||||
**OfficeCLI の内蔵 HTML レンダリングエンジンは、ドキュメントを高忠実度で再現 — これが AI に「目」を与えます。** `.docx` / `.xlsx` / `.pptx` を HTML または PNG にレンダリングし、"レンダリング → 見る → 修正" のループを完結させます。
|
||||
|
||||
[](https://github.com/iOfficeAI/OfficeCLI/releases)
|
||||
[](LICENSE)
|
||||
|
||||
[English](README.md) | [中文](README_zh.md) | **日本語** | [한국어](README_ko.md)
|
||||
|
||||
<p align="center">
|
||||
<strong>🌐 公式サイト:</strong> <a href="https://officecli.ai" target="_blank">officecli.ai</a> | <strong>💬 コミュニティ:</strong> <a href="https://discord.gg/2QAwJn7Egx" target="_blank">Discord</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/ppt-process.webp" alt="AionUi で OfficeCLI を使った PPT 作成プロセス" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><em><a href="https://github.com/iOfficeAI/AionUi">AionUi</a> で OfficeCLI を使った PPT 作成プロセス</em></p>
|
||||
|
||||
<p align="center"><strong>PowerPoint プレゼンテーション</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/designwhatmovesyou.gif" alt="OfficeCLI デザインプレゼン (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/horizon.gif" alt="OfficeCLI ビジネスプレゼン (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/efforless.gif" alt="OfficeCLI テクノロジープレゼン (PowerPoint)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/blackhole.gif" alt="OfficeCLI 宇宙プレゼン (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/first-ppt-aionui.gif" alt="OfficeCLI ゲームプレゼン (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/shiba.gif" alt="OfficeCLI クリエイティブプレゼン (PowerPoint)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Word 文書</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/word1.gif" alt="OfficeCLI 学術論文 (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word2.gif" alt="OfficeCLI プロジェクト提案書 (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word3.gif" alt="OfficeCLI 年次報告書 (Word)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Excel スプレッドシート</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/excel1.gif" alt="OfficeCLI 予算管理 (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel2.gif" alt="OfficeCLI 成績管理 (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel3.gif" alt="OfficeCLI 売上ダッシュボード (Excel)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center"><em>上記の文書はすべて AI エージェントが OfficeCLI を使って全自動で作成 — テンプレートなし、手動編集なし。</em></p>
|
||||
|
||||
## AI エージェント向け — 一行で開始
|
||||
|
||||
これを AI エージェントのチャットに貼り付けるだけ — スキルファイルを自動で読み込み、インストールを完了します:
|
||||
|
||||
```
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
これだけです。スキルファイルがエージェントにバイナリのインストール方法と全コマンドの使い方を教えます。
|
||||
|
||||
## 一般ユーザー向け
|
||||
|
||||
**オプション A — GUI:** [**AionUi**](https://github.com/iOfficeAI/AionUi) をインストール — 自然言語で Office 文書を作成・編集できるデスクトップアプリ。内部で OfficeCLI が動いています。やりたいことを説明するだけで、AionUi がすべて処理します。
|
||||
|
||||
**オプション B — CLI:** [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases) からお使いのプラットフォーム用バイナリをダウンロードして、以下を実行:
|
||||
|
||||
```bash
|
||||
officecli install
|
||||
```
|
||||
|
||||
バイナリを PATH にコピーし、検出されたすべての AI コーディングエージェント(Claude Code、Cursor、Windsurf、GitHub Copilot など)に **officecli スキル**を自動インストールします。エージェントはすぐに Office 文書の作成・読み取り・編集が可能になります。追加設定は不要です。
|
||||
|
||||
## 開発者向け — 30秒でライブ体験
|
||||
|
||||
```bash
|
||||
# 1. インストール(macOS / Linux)— または: brew install officecli / npm install -g @officecli/officecli
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
# Windows (PowerShell): irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
|
||||
# 2. 空の PowerPoint を作成
|
||||
officecli create deck.pptx
|
||||
|
||||
# 3. ライブプレビューを開始 — ブラウザで http://localhost:26315 が開きます
|
||||
officecli watch deck.pptx
|
||||
|
||||
# 4. 別のターミナルを開いてスライドを追加 — ブラウザが即座に更新されます
|
||||
officecli add deck.pptx / --type slide --prop title="Hello, World!"
|
||||
```
|
||||
|
||||
これだけです。`add`、`set`、`remove` コマンドを実行するたびに、プレビューがリアルタイムで更新されます。どんどん試してみてください — ブラウザがあなたのライブフィードバックループです。
|
||||
|
||||
## クイックスタート
|
||||
|
||||
```bash
|
||||
# プレゼンテーションを作成してコンテンツを追加
|
||||
officecli create deck.pptx
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report" --prop background=1A1A2E
|
||||
officecli add deck.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue grew 25%" --prop x=2cm --prop y=5cm \
|
||||
--prop font=Arial --prop size=24 --prop color=FFFFFF
|
||||
|
||||
# アウトラインを表示
|
||||
officecli view deck.pptx outline
|
||||
# → Slide 1: Q4 Report
|
||||
# → Shape 1 [TextBox]: Revenue grew 25%
|
||||
|
||||
# HTML で表示 — サーバー不要、ブラウザでレンダリングされたプレビューを開きます
|
||||
officecli view deck.pptx html
|
||||
|
||||
# 任意の要素の構造化 JSON を取得
|
||||
officecli get deck.pptx '/slide[1]/shape[1]' --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": "shape",
|
||||
"path": "/slide[1]/shape[1]",
|
||||
"attributes": {
|
||||
"name": "TextBox 1",
|
||||
"text": "Revenue grew 25%",
|
||||
"x": "720000",
|
||||
"y": "1800000"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## なぜ OfficeCLI?
|
||||
|
||||
以前は 50行の Python と 3つのライブラリが必要でした:
|
||||
|
||||
```python
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches, Pt
|
||||
prs = Presentation()
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[0])
|
||||
title = slide.shapes.title
|
||||
title.text = "Q4 Report"
|
||||
# ... さらに 45行 ...
|
||||
prs.save('deck.pptx')
|
||||
```
|
||||
|
||||
今はコマンド一つで:
|
||||
|
||||
```bash
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report"
|
||||
```
|
||||
|
||||
**OfficeCLI でできること:**
|
||||
|
||||
- **作成** ドキュメント -- 空白またはコンテンツ付き
|
||||
- **読み取り** テキスト、構造、スタイル、数式 -- プレーンテキストまたは構造化 JSON
|
||||
- **分析** フォーマットの問題、スタイルの不整合、構造的な欠陥
|
||||
- **修正** 任意の要素 -- テキスト、フォント、色、レイアウト、数式、チャート、画像
|
||||
- **再構成** コンテンツ -- 要素の追加、削除、移動、文書間コピー
|
||||
|
||||
| フォーマット | 読み取り | 修正 | 作成 |
|
||||
|-------------|---------|------|------|
|
||||
| Word (.docx) | ✅ | ✅ | ✅ |
|
||||
| Excel (.xlsx) | ✅ | ✅ | ✅ |
|
||||
| PowerPoint (.pptx) | ✅ | ✅ | ✅ |
|
||||
|
||||
**Word** — 完全な [i18n & RTL サポート](https://github.com/iOfficeAI/OfficeCLI/wiki/i18n)(スクリプト別フォントスロット、スクリプト別 BCP-47 言語タグ `lang.latin/ea/cs`、複雑スクリプトの太字/斜体/サイズ、段落/ラン/セクション/表/スタイル/ヘッダー/フッター/docDefaults をカスケードする `direction=rtl`、`rtlGutter` + `pgBorders` ショートハンド、ヒンディー語/アラビア語/タイ語/CJK のロケール対応ページ番号)、[段落](https://github.com/iOfficeAI/OfficeCLI/wiki/word-paragraph)、[ラン](https://github.com/iOfficeAI/OfficeCLI/wiki/word-run)、[表](https://github.com/iOfficeAI/OfficeCLI/wiki/word-table)、[スタイル](https://github.com/iOfficeAI/OfficeCLI/wiki/word-style)、[ヘッダー/フッター](https://github.com/iOfficeAI/OfficeCLI/wiki/word-header-footer)、[画像](https://github.com/iOfficeAI/OfficeCLI/wiki/word-picture)(PNG/JPG/GIF/SVG)、[数式](https://github.com/iOfficeAI/OfficeCLI/wiki/word-equation)、[コメント](https://github.com/iOfficeAI/OfficeCLI/wiki/word-comment)、[脚注](https://github.com/iOfficeAI/OfficeCLI/wiki/word-footnote)、[透かし](https://github.com/iOfficeAI/OfficeCLI/wiki/word-watermark)、[ブックマーク](https://github.com/iOfficeAI/OfficeCLI/wiki/word-bookmark)、[目次](https://github.com/iOfficeAI/OfficeCLI/wiki/word-toc)、[チャート](https://github.com/iOfficeAI/OfficeCLI/wiki/word-chart)、[ハイパーリンク](https://github.com/iOfficeAI/OfficeCLI/wiki/word-hyperlink)、[セクション](https://github.com/iOfficeAI/OfficeCLI/wiki/word-section)、[フォームフィールド](https://github.com/iOfficeAI/OfficeCLI/wiki/word-formfield)、[コンテンツコントロール (SDT)](https://github.com/iOfficeAI/OfficeCLI/wiki/word-sdt)、[フィールド](https://github.com/iOfficeAI/OfficeCLI/wiki/word-field)(22 種類のゼロ引数 + MERGEFIELD / REF / PAGEREF / SEQ / STYLEREF / DOCPROPERTY / IF)、[OLE オブジェクト](https://github.com/iOfficeAI/OfficeCLI/wiki/word-ole)、[文書プロパティ](https://github.com/iOfficeAI/OfficeCLI/wiki/word-document)
|
||||
|
||||
**Excel** — [セル](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-cell)(追加時にふりがな対応)、数式(150以上の組み込み関数を自動計算、動的配列関数に `_xlfn.` 自動プレフィックス)、[シート](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sheet)(visible/hidden/veryHidden、印刷余白、printTitleRows/Cols、RTL `sheetView`、カスケード対応のシート名変更)、[テーブル](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-table)、[ソート](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sort)(シート/範囲、マルチキー、サイドカー対応)、[条件付き書式](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-conditionalformatting)、[チャート](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart)(箱ひげ図、[パレート図](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart-add) 自動ソート + 累積%、対数軸を含む)、[ピボットテーブル](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-pivottable)(マルチフィールド、日付グループ化、showDataAs、ソート、総計、小計、コンパクト/アウトライン/表形式レイアウト、項目ラベル繰り返し、空白行、計算フィールド)、[スライサー](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-slicer)、[名前付き範囲](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-namedrange)、[データ入力規則](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-validation)、[画像](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-picture)(PNG/JPG/GIF/SVG、デュアル表現フォールバック)、[スパークライン](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sparkline)、[コメント](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-comment)(RTL)、[オートフィルター](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-autofilter)、[図形](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-shape)、[OLE オブジェクト](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-ole)、CSV/TSV インポート、`$Sheet:A1` セルアドレッシング
|
||||
|
||||
**PowerPoint** — [スライド](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-slide)(ヘッダー/フッター/日付/スライド番号トグル、非表示)、[図形](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-shape)(パターン塗りつぶし、ぼかし効果、ハイパーリンクツールチップ + スライドジャンプリンク)、[画像](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-picture)(PNG/JPG/GIF/SVG、塗りモード: stretch/contain/cover/tile、明るさ/コントラスト/光彩/影)、[表](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-table)、[チャート](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-chart)、[アニメーション](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-slide)、[モーフトランジション](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-morph-check)、[3D モデル (.glb)](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-3dmodel)、[スライドズーム](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-zoom)、[数式](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-equation)、[テーマ](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-theme)、[コネクタ](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-connector)、[ビデオ/オーディオ](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-video)、[グループ](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-group)、[ノート](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-notes)(RTL、lang)、[コメント](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-comment)(RTL)、[OLE オブジェクト](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-ole)、[プレースホルダー](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-placeholder)(phType で追加/設定)
|
||||
|
||||
## 使用シーン
|
||||
|
||||
**開発者向け:**
|
||||
- データベースや API からのレポート自動生成
|
||||
- 文書の一括処理(一括検索/置換、スタイル更新)
|
||||
- CI/CD 環境でのドキュメントパイプライン構築(テスト結果からドキュメント生成)
|
||||
- Docker/コンテナ環境でのヘッドレス Office 自動化
|
||||
|
||||
**AI エージェント向け:**
|
||||
- ユーザーのプロンプトからプレゼンテーションを生成(上記の例を参照)
|
||||
- ドキュメントから構造化データを JSON に抽出
|
||||
- 納品前のドキュメント品質検証
|
||||
|
||||
**チーム向け:**
|
||||
- ドキュメントテンプレートを複製してデータを入力
|
||||
- CI/CD パイプラインでの自動ドキュメント検証
|
||||
|
||||
## インストール
|
||||
|
||||
単一の自己完結型バイナリとして配布。.NET ランタイムは内蔵 -- インストール不要、ランタイム管理不要。
|
||||
|
||||
**ワンライナーインストール:**
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
**またはパッケージマネージャーで:**
|
||||
|
||||
```bash
|
||||
# Homebrew(macOS / Linux)
|
||||
brew install officecli
|
||||
|
||||
# Scoop(Windows)
|
||||
scoop install officecli
|
||||
|
||||
# npm(全プラットフォーム — インストール時にプラットフォームに合ったネイティブバイナリを取得)
|
||||
npm install -g @officecli/officecli
|
||||
```
|
||||
|
||||
**または手動ダウンロード** [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases):
|
||||
|
||||
| プラットフォーム | バイナリ |
|
||||
|----------------|---------|
|
||||
| macOS Apple Silicon | `officecli-mac-arm64` |
|
||||
| macOS Intel | `officecli-mac-x64` |
|
||||
| Linux x64 | `officecli-linux-x64` |
|
||||
| Linux ARM64 | `officecli-linux-arm64` |
|
||||
| Windows x64 | `officecli-win-x64.exe` |
|
||||
| Windows ARM64 | `officecli-win-arm64.exe` |
|
||||
|
||||
インストール確認:`officecli --version`
|
||||
|
||||
**またはダウンロード済みバイナリからセルフインストール(`officecli` を直接実行してもインストールがトリガーされます):**
|
||||
|
||||
```bash
|
||||
officecli install # 明示的インストール
|
||||
officecli # 直接実行でもインストールがトリガー
|
||||
```
|
||||
|
||||
更新はバックグラウンドで自動チェックされます。`officecli config autoUpdate false` で無効化、または `OFFICECLI_SKIP_UPDATE=1` で単回スキップ可能。設定は `~/.officecli/config.json` にあります。
|
||||
|
||||
## 主な機能
|
||||
|
||||
### 内蔵エンジンと生成プリミティブ
|
||||
|
||||
OfficeCLI は自己完結型です。以下の機能はすべてバイナリ内蔵 — **Office 不要**。
|
||||
|
||||
#### レンダリングエンジン — 高忠実度・内蔵
|
||||
|
||||
OfficeCLI の要 (キーストーン): ゼロから実装した高忠実度の HTML レンダリングエンジンが、AI エージェントに DOM から推測させるのではなく、レンダリングされたドキュメントを "見せ" ます。シェイプ、チャート (トレンドライン、エラーバー、ウォーターフォール、ローソク足、スパークライン)、数式 (OMML → LaTeX、KaTeX でレンダリング)、Three.js による 3D `.glb` モデル、モーフトランジション、スライドズーム、シェイプエフェクトをカバー。ページごとの PNG スクリーンショットは、レンダリングされた HTML をヘッドレスブラウザに渡して生成されます。3 つのモード:
|
||||
|
||||
- **`view html`** — スタンドアロン HTML ファイル、アセットインライン。任意のブラウザで開けます。
|
||||
- **`view screenshot`** — ページごとの PNG、マルチモーダルエージェント向け。
|
||||
- **`watch`** — ローカル HTTP サーバー + 自動更新プレビュー。`add` / `set` / `remove` でブラウザが即座に更新。Excel watch はインラインセル編集とチャートのドラッグ再配置をサポート。
|
||||
|
||||
```bash
|
||||
officecli view deck.pptx html -o /tmp/deck.html
|
||||
officecli view deck.pptx screenshot -o /tmp/deck.png # 複数ページは --page 1-N
|
||||
officecli watch deck.pptx # http://localhost:26315
|
||||
```
|
||||
|
||||
> 可視化なしでは、スライドを生成するエージェントは盲目的に飛んでいるようなもの — DOM は読めても、タイトルがオーバーフローしているか、2 つのシェイプが重なっているかは判断できません。レンダリングがバイナリに内蔵されているため、"レンダリング → 見る → 修正" のループは CI、Docker、ディスプレイのないサーバー — バイナリが動くあらゆる場所で動作します。
|
||||
|
||||
#### 数式 & ピボットエンジン
|
||||
|
||||
350+ の Excel 関数が書き込み時に自動評価 — `=SUM(A1:A2)` を書いて、セルを `get` する、値はすでにそこに。Office で再計算するラウンドトリップは不要。スピルする動的配列 (`FILTER` / `SORT` / `UNIQUE` / `SEQUENCE` / `LET` / `LAMBDA`、`_xlfn.` 自動プレフィックス)、`VLOOKUP` / `XLOOKUP` / `INDEX` / `MATCH`、財務・債券関数、統計分布・検定・回帰、日付・テキスト関数などをカバー。
|
||||
|
||||
加えて、ソース範囲から 1 コマンドでネイティブな OOXML ピボットテーブル — マルチフィールドの行/列/フィルター、10 種類の集計、`showDataAs` モード、日付グループ化、計算フィールド、Top-N、レイアウト。ピボットキャッシュ + 定義は OOXML に書き込まれ、Excel で開くと集計済みの状態で表示されます:
|
||||
|
||||
```bash
|
||||
officecli add sales.xlsx '/Sheet1' --type pivottable \
|
||||
--prop source='Data!A1:E10000' --prop rows='Region,Category' \
|
||||
--prop cols=Quarter --prop values='Revenue:sum,Units:avg' \
|
||||
--prop showDataAs=percentOfTotal
|
||||
```
|
||||
|
||||
#### テンプレートマージ — 一度設計、N 回入力
|
||||
|
||||
`merge` は任意の `.docx` / `.xlsx` / `.pptx` の `{{key}}` プレースホルダーを JSON データで置換 — 段落、表セル、シェイプ、ヘッダー/フッター、チャートタイトル全体で動作。エージェントが一度レイアウトを設計 (高コスト)、本番コードが N 回入力 (低コスト、決定論的、トークンコストゼロ)。エージェントが各レポートを毎回ゼロから再生成し、N 個の一貫性のないレイアウトを生み出す失敗モードを回避します。
|
||||
|
||||
```bash
|
||||
officecli merge invoice-template.docx out-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
officecli merge q4-template.pptx q4-acme.pptx data.json
|
||||
```
|
||||
|
||||
#### Dump によるラウンドトリップ — 既存ドキュメントから学ぶ
|
||||
|
||||
`dump` は任意の `.docx`・`.pptx`・`.xlsx` — ドキュメント全体**または任意のサブツリー**(単一の段落、表、スライド、ワークシート、styles、numbering、theme、settings)— を再生可能なバッチ JSON にシリアライズし、`batch` で再生。ユーザーが模倣したいサンプルから、エージェントは生の OOXML XML ではなく構造化された仕様を読み、変更して再生します。"既存テンプレートがある" と "100 個のバリエーションを生成して" を繋ぎます。
|
||||
|
||||
```bash
|
||||
officecli dump existing.docx -o blueprint.json # ドキュメント全体
|
||||
officecli dump existing.docx /body/tbl[1] -o table.json # 任意のサブツリー
|
||||
officecli dump existing.xlsx /Sheet1 -o sheet.json # 単一ワークシート
|
||||
officecli batch new.docx --input blueprint.json
|
||||
```
|
||||
|
||||
### レジデントモードとバッチ
|
||||
|
||||
複数ステップのワークフローでは、レジデントモードがドキュメントをメモリに保持。バッチモードは一度の open/save サイクルで複数操作を実行します。
|
||||
|
||||
```bash
|
||||
# レジデントモード — 名前付きパイプ経由で遅延ほぼゼロ
|
||||
officecli open report.docx
|
||||
officecli set report.docx /body/p[1]/r[1] --prop bold=true
|
||||
officecli set report.docx /body/p[2]/r[1] --prop color=FF0000
|
||||
officecli close report.docx
|
||||
|
||||
# バッチモード — アトミックなマルチコマンド実行(デフォルトで最初のエラーで停止)
|
||||
echo '[{"command":"set","path":"/slide[1]/shape[1]","props":{"text":"Hello"}},
|
||||
{"command":"set","path":"/slide[1]/shape[2]","props":{"fill":"FF0000"}}]' \
|
||||
| officecli batch deck.pptx --json
|
||||
|
||||
# インラインバッチ — stdin 不要
|
||||
officecli batch deck.pptx --commands '[{"op":"set","path":"/slide[1]/shape[1]","props":{"text":"Hi"}}]'
|
||||
|
||||
# --force でエラーをスキップして続行
|
||||
officecli batch deck.pptx --input updates.json --force --json
|
||||
```
|
||||
|
||||
### 三層アーキテクチャ
|
||||
|
||||
シンプルに始めて、必要な時だけ深く。
|
||||
|
||||
| レイヤー | 用途 | コマンド |
|
||||
|---------|------|---------|
|
||||
| **L1:読み取り** | コンテンツのセマンティックビュー | `view`(text、annotated、outline、stats、issues、html、svg、screenshot) |
|
||||
| **L2:DOM** | 構造化された要素操作 | `get`、`query`、`set`、`add`、`remove`、`move`、`swap` |
|
||||
| **L3:生 XML** | XPath による直接アクセス — 万能フォールバック | `raw`、`raw-set`、`add-part`、`validate` |
|
||||
|
||||
```bash
|
||||
# L1 — 高レベルビュー
|
||||
officecli view report.docx annotated
|
||||
officecli view budget.xlsx text --cols A,B,C --max-lines 50
|
||||
|
||||
# L2 — 要素レベルの操作
|
||||
officecli query report.docx "run:contains(TODO)"
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q2 Report"
|
||||
officecli move report.docx /body/p[5] --to /body --index 1
|
||||
|
||||
# L3 — L2 では足りない時に生 XML
|
||||
officecli raw deck.pptx '/slide[1]'
|
||||
officecli raw-set report.docx document \
|
||||
--xpath "//w:p[1]" --action append \
|
||||
--xml '<w:r><w:t>Injected text</w:t></w:r>'
|
||||
```
|
||||
|
||||
## AI 統合
|
||||
|
||||
### MCP サーバー
|
||||
|
||||
組み込み [MCP](https://modelcontextprotocol.io) サーバー — コマンド一つで登録:
|
||||
|
||||
```bash
|
||||
officecli mcp claude # Claude Code
|
||||
officecli mcp cursor # Cursor
|
||||
officecli mcp vscode # VS Code / Copilot
|
||||
officecli mcp lmstudio # LM Studio
|
||||
officecli mcp list # 登録状態を確認
|
||||
```
|
||||
|
||||
JSON-RPC で全ドキュメント操作を公開 — シェルアクセス不要。
|
||||
|
||||
### 直接 CLI 統合
|
||||
|
||||
2ステップで OfficeCLI を任意の AI エージェントに統合:
|
||||
|
||||
1. **バイナリをインストール** -- コマンド一つ([インストール](#インストール)参照)
|
||||
2. **完了。** OfficeCLI は AI ツール(Claude Code、GitHub Copilot、Codex)を自動検出し、既知の設定ディレクトリを確認してスキルファイルをインストールします。エージェントはすぐに Office 文書の作成・読み取り・変更が可能です。
|
||||
|
||||
<details>
|
||||
<summary><strong>手動設定(オプション)</strong></summary>
|
||||
|
||||
自動インストールがお使いの環境に対応していない場合、手動でスキルファイルをインストールできます:
|
||||
|
||||
**SKILL.md を直接エージェントに読み込ませる:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
**Claude Code のローカルスキルとしてインストール:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md -o ~/.claude/skills/officecli.md
|
||||
```
|
||||
|
||||
**その他のエージェント:** `SKILL.md` の内容をエージェントのシステムプロンプトまたはツール説明に含めてください。
|
||||
|
||||
</details>
|
||||
|
||||
### エージェントが OfficeCLI で活躍する理由
|
||||
|
||||
- **決定論的 JSON 出力** — すべてのコマンドが `--json` をサポートし、スキーマは一貫。正規表現パース不要、stdout スクレイピング不要。
|
||||
- **パスベースのアドレッシング** — すべての要素に安定したパス (`/slide[1]/shape[2]`)。エージェントは XML 名前空間を理解せずにドキュメントをナビゲート可能。(OfficeCLI 独自の構文: 1-based インデックス、要素ローカル名 — XPath ではない。)
|
||||
- **段階的複雑度 (L1 → L2 → L3)** — エージェントは読み取り専用ビューから始め、DOM 操作にエスカレート、必要な時のみ raw XML にフォールバック。トークン消費を最小化。
|
||||
- **自己修復ワークフロー** — `validate`、`view issues`、構造化エラーコード (`not_found`、`invalid_value`、`unsupported_property`) は suggestion と有効範囲を返します。エージェントは人間の介入なしに自己修正します。
|
||||
- **内蔵エージェントフレンドリーレンダリングエンジン** — `view html` / `view screenshot` / `watch` がネイティブに HTML と PNG を出力。Office 不要。エージェントは CI / Docker / ヘッドレス環境でも自分の出力を "見て" レイアウトの問題を修正できます。
|
||||
- **内蔵数式 & ピボットエンジン** — 350+ の Excel 関数が書き込み時に自動評価 (スピルする動的配列、財務・債券・統計関数群を含む); ソース範囲から 1 コマンドでネイティブ OOXML ピボットテーブル。エージェントは Office で再計算せずに、計算値と集計結果を即座に読み取れます。
|
||||
- **テンプレートマージ** — エージェントがレイアウトを一度設計し、下流コードが `{{key}}` プレースホルダーを N 回入力。各レポートを再生成してトークンを焼くことを避けます。
|
||||
- **ラウンドトリップ Dump** — `dump` が任意の `.docx`・`.pptx`・`.xlsx` を再生可能なバッチ JSON に変換。エージェントは生の OOXML XML ではなく構造化された仕様を読んで、人間が作成したサンプルから学習。
|
||||
- **内蔵ヘルプ** — プロパティ名や値形式に迷ったら、エージェントは推測せず `officecli <format> set <element>` を実行。
|
||||
- **自動インストール** — OfficeCLI は使っているツール (Claude Code、Cursor、VS Code…) を検出して自己構成します。手動の skill ファイルセットアップ不要。
|
||||
|
||||
### 組み込みヘルプ
|
||||
|
||||
プロパティ名がわからない時は、階層型ヘルプで確認:
|
||||
|
||||
```bash
|
||||
officecli pptx set # 全設定可能な要素とプロパティ
|
||||
officecli pptx set shape # 特定の要素タイプの詳細
|
||||
officecli pptx set shape.fill # 単一プロパティのフォーマットと例
|
||||
officecli docx query # セレクタリファレンス:属性、:contains、:has() など
|
||||
```
|
||||
|
||||
`pptx` を `docx` や `xlsx` に置き換え可能。動詞は `view`、`get`、`query`、`set`、`add`、`raw`。
|
||||
|
||||
`officecli --help` で全体概要を確認。
|
||||
|
||||
### JSON 出力スキーマ
|
||||
|
||||
全コマンドが `--json` に対応。一般的なレスポンス形式:
|
||||
|
||||
**単一要素**(`get --json`):
|
||||
|
||||
```json
|
||||
{"tag": "shape", "path": "/slide[1]/shape[1]", "attributes": {"name": "TextBox 1", "text": "Hello"}}
|
||||
```
|
||||
|
||||
**要素リスト**(`query --json`):
|
||||
|
||||
```json
|
||||
[
|
||||
{"tag": "paragraph", "path": "/body/p[1]", "attributes": {"style": "Heading1", "text": "Title"}},
|
||||
{"tag": "paragraph", "path": "/body/p[5]", "attributes": {"style": "Heading1", "text": "Summary"}}
|
||||
]
|
||||
```
|
||||
|
||||
**エラー** は構造化エラーオブジェクトを返却。エラーコード、修正提案、利用可能な値を含みます:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"error": "Slide 50 not found (total: 8)",
|
||||
"code": "not_found",
|
||||
"suggestion": "Valid Slide index range: 1-8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
エラーコード:`not_found`、`invalid_value`、`unsupported_property`、`invalid_path`、`unsupported_type`、`missing_property`、`file_not_found`、`file_locked`、`invalid_selector`。プロパティ名は自動修正対応 -- プロパティ名のスペルミスは最も近い候補を提案します。
|
||||
|
||||
**エラー回復** -- エージェントは利用可能な要素を確認して自己修正:
|
||||
|
||||
```bash
|
||||
# エージェントが無効なパスを試行
|
||||
officecli get report.docx /body/p[99] --json
|
||||
# 返却: {"success": false, "error": {"error": "...", "code": "not_found", "suggestion": "..."}}
|
||||
|
||||
# エージェントが利用可能な要素を確認して自己修正
|
||||
officecli get report.docx /body --depth 1 --json
|
||||
# 利用可能な子要素のリストを返却、エージェントが正しいパスを選択
|
||||
```
|
||||
|
||||
**変更確認**(`set`、`add`、`remove`、`move`、`create` で `--json` 使用時):
|
||||
|
||||
```json
|
||||
{"success": true, "path": "/slide[1]/shape[1]"}
|
||||
```
|
||||
|
||||
`officecli --help` で終了コードとエラー形式の完全な説明を確認。
|
||||
|
||||
## 比較
|
||||
|
||||
| | OfficeCLI | Microsoft Office | LibreOffice | python-docx / openpyxl |
|
||||
|---|---|---|---|---|
|
||||
| オープンソース&無料 | ✓ (Apache 2.0) | ✗(有料ライセンス) | ✓ | ✓ |
|
||||
| AI ネイティブ CLI + JSON | ✓ | ✗ | ✗ | ✗ |
|
||||
| ゼロインストール(単一バイナリ) | ✓ | ✗ | ✗ | ✗(Python + pip 必要) |
|
||||
| 任意の言語から呼び出し | ✓ (CLI) | ✗ (COM/Add-in) | ✗ (UNO API) | Python のみ |
|
||||
| パスベースの要素アクセス | ✓ | ✗ | ✗ | ✗ |
|
||||
| 生 XML フォールバック | ✓ | ✗ | ✗ | 部分対応 |
|
||||
| 内蔵エージェントフレンドリーレンダリングエンジン | ✓ | ✗ | ✗ | ✗ |
|
||||
| ヘッドレス HTML/PNG 出力 | ✓ | ✗ | 部分対応 | ✗ |
|
||||
| クロスフォーマットテンプレートマージ (`{{key}}`) | ✓ | ✗ | ✗ | ✗ |
|
||||
| Dump → batch JSON ラウンドトリップ | ✓ | ✗ | ✗ | ✗ |
|
||||
| ライブプレビュー (編集後自動更新) | ✓ | ✗ | ✗ | ✗ |
|
||||
| ヘッドレス / CI | ✓ | ✗ | 部分対応 | ✓ |
|
||||
| クロスプラットフォーム | ✓ | Windows/Mac | ✓ | ✓ |
|
||||
| Word + Excel + PowerPoint | ✓ | ✓ | ✓ | 複数ライブラリが必要 |
|
||||
|
||||
## コマンドリファレンス
|
||||
|
||||
| コマンド | 説明 |
|
||||
|---------|------|
|
||||
| [`create`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-create) | 空白の .docx、.xlsx、.pptx を作成(拡張子からタイプを判定) |
|
||||
| [`view`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-view) | コンテンツを表示(モード:`outline`、`text`、`annotated`、`stats`、`issues`、`html`) |
|
||||
| [`get`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-get) | 要素と子要素を取得(`--depth N`、`--json`) |
|
||||
| [`query`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-query) | CSS スタイルのクエリ(`[attr=value]`、`:contains()`、`:has()` など) |
|
||||
| [`set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-set) | 要素のプロパティを変更 |
|
||||
| [`add`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-add) | 要素を追加(または `--from <path>` でクローン) |
|
||||
| [`remove`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-remove) | 要素を削除 |
|
||||
| [`move`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-move) | 要素を移動(`--to <parent>`、`--index N`、`--after <path>`、`--before <path>`) |
|
||||
| [`swap`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-swap) | 2つの要素を交換 |
|
||||
| [`validate`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-validate) | OpenXML スキーマ検証 |
|
||||
| [`batch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-batch) | 一度の open/save サイクルで複数操作を実行(stdin、`--input`、または `--commands`;デフォルトで最初のエラーで停止、`--force` で続行) |
|
||||
| [`merge`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-merge) | テンプレートマージ — `{{key}}` プレースホルダーを JSON データで置換 |
|
||||
| [`watch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-watch) | ブラウザでライブ HTML プレビュー、自動更新 |
|
||||
| [`mcp`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-mcp) | AI ツール統合用の MCP サーバーを起動 |
|
||||
| [`raw`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | ドキュメントパートの生 XML を表示 |
|
||||
| [`raw-set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | XPath で生 XML を変更 |
|
||||
| `add-part` | 新しいドキュメントパート(ヘッダー、チャートなど)を追加 |
|
||||
| [`open`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-open) | レジデントモードを開始(ドキュメントをメモリに保持) |
|
||||
| `close` | 保存してレジデントモードを終了 |
|
||||
| [`install`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-install) | バイナリ + スキル + MCP をインストール(`all`、`claude`、`cursor` など) |
|
||||
| `config` | 設定の取得または変更 |
|
||||
| `<format> <command>` | [組み込みヘルプ](https://github.com/iOfficeAI/OfficeCLI/wiki/command-reference)(例:`officecli pptx set shape`) |
|
||||
|
||||
## エンドツーエンドワークフロー例
|
||||
|
||||
典型的なエージェント自己修復ワークフロー:プレゼンテーションの作成、コンテンツの入力、検証、問題の修正 -- すべて人間の介入なし。
|
||||
|
||||
```bash
|
||||
# 1. 作成
|
||||
officecli create report.pptx
|
||||
|
||||
# 2. コンテンツを追加
|
||||
officecli add report.pptx / --type slide --prop title="Q4 Results"
|
||||
officecli add report.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue: $4.2M" --prop x=2cm --prop y=5cm --prop size=28
|
||||
officecli add report.pptx / --type slide --prop title="Details"
|
||||
officecli add report.pptx '/slide[2]' --type shape \
|
||||
--prop text="Growth driven by new markets" --prop x=2cm --prop y=5cm
|
||||
|
||||
# 3. 検証
|
||||
officecli view report.pptx outline
|
||||
officecli validate report.pptx
|
||||
|
||||
# 4. 問題の修正
|
||||
officecli view report.pptx issues --json
|
||||
# 出力に基づいて問題を修正:
|
||||
officecli set report.pptx '/slide[1]/shape[1]' --prop font=Arial
|
||||
```
|
||||
|
||||
### 単位と色
|
||||
|
||||
すべての寸法・色プロパティは柔軟な入力形式に対応:
|
||||
|
||||
| タイプ | 対応形式 | 例 |
|
||||
|-------|---------|-----|
|
||||
| **寸法** | cm、in、pt、px または生 EMU | `2cm`、`1in`、`72pt`、`96px`、`914400` |
|
||||
| **色** | 16進数、色名、RGB、テーマ色 | `#FF0000`、`FF0000`、`red`、`rgb(255,0,0)`、`accent1` |
|
||||
| **フォントサイズ** | 数値のみまたは pt 接尾辞付き | `14`、`14pt`、`10.5pt` |
|
||||
| **間隔** | pt、cm、in または倍率 | `12pt`、`0.5cm`、`1.5x`、`150%` |
|
||||
|
||||
## よく使うパターン
|
||||
|
||||
```bash
|
||||
# Word 文書の全 Heading1 テキストを置換
|
||||
officecli query report.docx "paragraph[style=Heading1]" --json | ...
|
||||
officecli set report.docx /body/p[1]/r[1] --prop text="New Title"
|
||||
|
||||
# 全スライドのコンテンツを JSON でエクスポート
|
||||
officecli get deck.pptx / --depth 2 --json
|
||||
|
||||
# Excel セルを一括更新
|
||||
officecli batch budget.xlsx --input updates.json --json
|
||||
|
||||
# CSV データを Excel シートにインポート
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q1 Data" --prop csv=sales.csv
|
||||
|
||||
# テンプレートマージでレポートを一括生成
|
||||
officecli merge invoice-template.docx invoice-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
|
||||
# 納品前にドキュメント品質をチェック
|
||||
officecli validate report.docx && officecli view report.docx issues --json
|
||||
```
|
||||
|
||||
**Python から呼び出し** — 一度ラップすれば、すべての呼び出しでパース済み JSON が返ります:
|
||||
|
||||
```python
|
||||
import json, subprocess
|
||||
|
||||
def cli(*args):
|
||||
return json.loads(subprocess.check_output(["officecli", *args, "--json"], text=True))
|
||||
|
||||
cli("create", "deck.pptx")
|
||||
cli("add", "deck.pptx", "/", "--type", "slide", "--prop", "title=Q4 レポート")
|
||||
slide = cli("get", "deck.pptx", "/slide[1]")
|
||||
print(slide["attributes"]["text"])
|
||||
```
|
||||
|
||||
## ドキュメント
|
||||
|
||||
[Wiki](https://github.com/iOfficeAI/OfficeCLI/wiki) に全コマンド、要素タイプ、プロパティの詳細ガイドがあります:
|
||||
|
||||
- **フォーマット別:**[Word](https://github.com/iOfficeAI/OfficeCLI/wiki/word-reference) | [Excel](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-reference) | [PowerPoint](https://github.com/iOfficeAI/OfficeCLI/wiki/powerpoint-reference)
|
||||
- **ワークフロー:**[エンドツーエンド例](https://github.com/iOfficeAI/OfficeCLI/wiki/workflows) -- Word レポート、Excel ダッシュボード、PPT プレゼン、一括変更、レジデントモード
|
||||
- **トラブルシューティング:**[よくあるエラーと解決策](https://github.com/iOfficeAI/OfficeCLI/wiki/troubleshooting)
|
||||
- **AI エージェントガイド:**[Wiki ナビゲーション決定木](https://github.com/iOfficeAI/OfficeCLI/wiki/agent-guide)
|
||||
|
||||
## ソースからビルド
|
||||
|
||||
コンパイルには [.NET 10 SDK](https://dotnet.microsoft.com/download) が必要です。出力は自己完結型のネイティブバイナリ -- .NET は内蔵されているため、実行時にはインストール不要です。
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
## ライセンス
|
||||
|
||||
[Apache License 2.0](LICENSE)
|
||||
|
||||
バグ報告やコントリビューションは [GitHub Issues](https://github.com/iOfficeAI/OfficeCLI/issues) まで。
|
||||
|
||||
---
|
||||
|
||||
OfficeCLI が役に立ったら、ぜひ [GitHub でスターを付けてください](https://github.com/iOfficeAI/OfficeCLI) — より多くの人にプロジェクトを届ける力になります。
|
||||
|
||||
[OfficeCLI.AI](https://OfficeCLI.AI) | [GitHub](https://github.com/iOfficeAI/OfficeCLI)
|
||||
|
||||
<!--
|
||||
yaml-frontmatter:
|
||||
name: officecli
|
||||
version: see GitHub Releases
|
||||
description: The standard CLI for AI agents to create, read, and modify Office documents
|
||||
formats: [docx, xlsx, pptx]
|
||||
platforms: [macos, linux, windows]
|
||||
license: Apache-2.0
|
||||
keywords: [office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint]
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
|
||||
<!-- LLM/agent discovery metadata
|
||||
tool: officecli
|
||||
type: cli
|
||||
canonical: the standard CLI for AI-driven Office document manipulation
|
||||
formats: docx, xlsx, pptx
|
||||
capabilities: create, read, modify, validate, batch, resident-mode, mcp-server, live-preview, auto-install
|
||||
output: json, text, annotated
|
||||
platforms: macos, linux, windows
|
||||
license: Apache-2.0
|
||||
keywords: office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint, ai-tools, command-line, structured-output
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
alternatives: python-docx, openpyxl, python-pptx, libreoffice --headless
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
@@ -0,0 +1,660 @@
|
||||
# OfficeCLI
|
||||
|
||||
> **OfficeCLI는 세계 최초이자 최고의, AI 에이전트를 위해 설계된 Office 스위트입니다.**
|
||||
|
||||
**모든 AI 에이전트에게 Word, Excel, PowerPoint의 완전한 제어권을 — 단 한 줄의 코드로.**
|
||||
|
||||
오픈소스. 단일 바이너리. Office 설치 불필요. 의존성 제로. 모든 플랫폼 지원.
|
||||
|
||||
**OfficeCLI의 내장 HTML 렌더링 엔진은 문서를 고충실도로 재현합니다 — 이것이 AI에게 "눈"을 줍니다.** `.docx` / `.xlsx` / `.pptx`를 HTML 또는 PNG로 렌더링하여 *렌더링 → 보기 → 수정* 루프를 닫습니다.
|
||||
|
||||
[](https://github.com/iOfficeAI/OfficeCLI/releases)
|
||||
[](LICENSE)
|
||||
|
||||
[English](README.md) | [中文](README_zh.md) | [日本語](README_ja.md) | **한국어**
|
||||
|
||||
<p align="center">
|
||||
<strong>🌐 공식 웹사이트:</strong> <a href="https://officecli.ai" target="_blank">officecli.ai</a> | <strong>💬 커뮤니티:</strong> <a href="https://discord.gg/2QAwJn7Egx" target="_blank">Discord</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/ppt-process.webp" alt="AionUi에서 OfficeCLI로 PPT 제작 과정" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><em><a href="https://github.com/iOfficeAI/AionUi">AionUi</a>에서 OfficeCLI로 PPT 제작 과정</em></p>
|
||||
|
||||
<p align="center"><strong>PowerPoint 프레젠테이션</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/designwhatmovesyou.gif" alt="OfficeCLI 디자인 프레젠테이션 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/horizon.gif" alt="OfficeCLI 비즈니스 프레젠테이션 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/efforless.gif" alt="OfficeCLI 테크 프레젠테이션 (PowerPoint)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/blackhole.gif" alt="OfficeCLI 우주 프레젠테이션 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/first-ppt-aionui.gif" alt="OfficeCLI 게임 프레젠테이션 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/shiba.gif" alt="OfficeCLI 크리에이티브 프레젠테이션 (PowerPoint)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Word 문서</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/word1.gif" alt="OfficeCLI 학술 논문 (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word2.gif" alt="OfficeCLI 프로젝트 제안서 (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word3.gif" alt="OfficeCLI 연간 보고서 (Word)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Excel 스프레드시트</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/excel1.gif" alt="OfficeCLI 예산 관리 (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel2.gif" alt="OfficeCLI 성적 관리 (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel3.gif" alt="OfficeCLI 매출 대시보드 (Excel)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center"><em>위의 모든 문서는 AI 에이전트가 OfficeCLI를 사용하여 완전 자동으로 생성 — 템플릿 없음, 수동 편집 없음.</em></p>
|
||||
|
||||
## AI 에이전트용 — 한 줄로 시작
|
||||
|
||||
이 한 줄을 AI 에이전트 채팅에 붙여넣기만 하면 — 스킬 파일을 자동으로 읽고 설치를 완료합니다:
|
||||
|
||||
```
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
이게 전부입니다. 스킬 파일이 에이전트에게 바이너리 설치 방법과 모든 명령어 사용법을 알려줍니다.
|
||||
|
||||
## 일반 사용자용
|
||||
|
||||
**옵션 A — GUI:** [**AionUi**](https://github.com/iOfficeAI/AionUi)를 설치하세요 — 자연어로 Office 문서를 만들고 편집할 수 있는 데스크톱 앱입니다. 내부적으로 OfficeCLI가 구동됩니다. 원하는 것을 설명하기만 하면 AionUi가 모든 것을 처리합니다.
|
||||
|
||||
**옵션 B — CLI:** [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases)에서 플랫폼에 맞는 바이너리를 다운로드한 후 실행:
|
||||
|
||||
```bash
|
||||
officecli install
|
||||
```
|
||||
|
||||
바이너리를 PATH에 복사하고, 감지된 모든 AI 코딩 에이전트(Claude Code, Cursor, Windsurf, GitHub Copilot 등)에 **officecli 스킬**을 자동 설치합니다. 에이전트는 즉시 Office 문서를 생성, 읽기, 편집할 수 있으며 추가 설정이 필요 없습니다.
|
||||
|
||||
## 개발자용 — 30초 만에 라이브로 확인
|
||||
|
||||
```bash
|
||||
# 1. 설치 (macOS / Linux) — 또는: brew install officecli / npm install -g @officecli/officecli
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
# Windows (PowerShell): irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
|
||||
# 2. 빈 PowerPoint 생성
|
||||
officecli create deck.pptx
|
||||
|
||||
# 3. 라이브 미리보기 시작 — 브라우저에서 http://localhost:26315 이 열립니다
|
||||
officecli watch deck.pptx
|
||||
|
||||
# 4. 다른 터미널을 열고 슬라이드 추가 — 브라우저가 즉시 업데이트됩니다
|
||||
officecli add deck.pptx / --type slide --prop title="Hello, World!"
|
||||
```
|
||||
|
||||
이게 전부입니다. `add`, `set`, `remove` 명령을 실행할 때마다 미리보기가 실시간으로 갱신됩니다. 계속 실험해 보세요 — 브라우저가 바로 여러분의 라이브 피드백 루프입니다.
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
```bash
|
||||
# 프레젠테이션을 생성하고 콘텐츠 추가
|
||||
officecli create deck.pptx
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report" --prop background=1A1A2E
|
||||
officecli add deck.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue grew 25%" --prop x=2cm --prop y=5cm \
|
||||
--prop font=Arial --prop size=24 --prop color=FFFFFF
|
||||
|
||||
# 개요 보기
|
||||
officecli view deck.pptx outline
|
||||
# → Slide 1: Q4 Report
|
||||
# → Shape 1 [TextBox]: Revenue grew 25%
|
||||
|
||||
# HTML로 보기 — 서버 없이 브라우저에서 렌더링된 미리보기를 엽니다
|
||||
officecli view deck.pptx html
|
||||
|
||||
# 모든 요소의 구조화된 JSON 가져오기
|
||||
officecli get deck.pptx '/slide[1]/shape[1]' --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": "shape",
|
||||
"path": "/slide[1]/shape[1]",
|
||||
"attributes": {
|
||||
"name": "TextBox 1",
|
||||
"text": "Revenue grew 25%",
|
||||
"x": "720000",
|
||||
"y": "1800000"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 왜 OfficeCLI인가?
|
||||
|
||||
이전에는 50줄의 Python과 3개의 라이브러리가 필요했습니다:
|
||||
|
||||
```python
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches, Pt
|
||||
prs = Presentation()
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[0])
|
||||
title = slide.shapes.title
|
||||
title.text = "Q4 Report"
|
||||
# ... 45줄 더 ...
|
||||
prs.save('deck.pptx')
|
||||
```
|
||||
|
||||
이제 명령어 하나면 됩니다:
|
||||
|
||||
```bash
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report"
|
||||
```
|
||||
|
||||
**OfficeCLI로 할 수 있는 것:**
|
||||
|
||||
- **생성** 문서 -- 빈 문서 또는 콘텐츠 포함
|
||||
- **읽기** 텍스트, 구조, 스타일, 수식 -- 일반 텍스트 또는 구조화된 JSON
|
||||
- **분석** 서식 문제, 스타일 불일치, 구조적 결함
|
||||
- **수정** 모든 요소 -- 텍스트, 글꼴, 색상, 레이아웃, 수식, 차트, 이미지
|
||||
- **재구성** 콘텐츠 -- 요소 추가, 삭제, 이동, 문서 간 복사
|
||||
|
||||
| 형식 | 읽기 | 수정 | 생성 |
|
||||
|------|------|------|------|
|
||||
| Word (.docx) | ✅ | ✅ | ✅ |
|
||||
| Excel (.xlsx) | ✅ | ✅ | ✅ |
|
||||
| PowerPoint (.pptx) | ✅ | ✅ | ✅ |
|
||||
|
||||
**Word** — 완전한 [i18n 및 RTL 지원](https://github.com/iOfficeAI/OfficeCLI/wiki/i18n) (스크립트별 글꼴 슬롯, 스크립트별 BCP-47 언어 태그 `lang.latin/ea/cs`, 복합 스크립트 굵게/기울임/크기, 단락/런/섹션/표/스타일/머리글/바닥글/docDefaults에 캐스케이드되는 `direction=rtl`, `rtlGutter` + `pgBorders` 단축형, 힌디/아랍어/태국어/CJK 로캘 인식 페이지 번호), [단락](https://github.com/iOfficeAI/OfficeCLI/wiki/word-paragraph), [런](https://github.com/iOfficeAI/OfficeCLI/wiki/word-run), [표](https://github.com/iOfficeAI/OfficeCLI/wiki/word-table), [스타일](https://github.com/iOfficeAI/OfficeCLI/wiki/word-style), [머리글/바닥글](https://github.com/iOfficeAI/OfficeCLI/wiki/word-header-footer), [이미지](https://github.com/iOfficeAI/OfficeCLI/wiki/word-picture) (PNG/JPG/GIF/SVG), [수식](https://github.com/iOfficeAI/OfficeCLI/wiki/word-equation), [메모](https://github.com/iOfficeAI/OfficeCLI/wiki/word-comment), [각주](https://github.com/iOfficeAI/OfficeCLI/wiki/word-footnote), [워터마크](https://github.com/iOfficeAI/OfficeCLI/wiki/word-watermark), [북마크](https://github.com/iOfficeAI/OfficeCLI/wiki/word-bookmark), [목차](https://github.com/iOfficeAI/OfficeCLI/wiki/word-toc), [차트](https://github.com/iOfficeAI/OfficeCLI/wiki/word-chart), [하이퍼링크](https://github.com/iOfficeAI/OfficeCLI/wiki/word-hyperlink), [섹션](https://github.com/iOfficeAI/OfficeCLI/wiki/word-section), [양식 필드](https://github.com/iOfficeAI/OfficeCLI/wiki/word-formfield), [콘텐츠 컨트롤 (SDT)](https://github.com/iOfficeAI/OfficeCLI/wiki/word-sdt), [필드](https://github.com/iOfficeAI/OfficeCLI/wiki/word-field) (22개 무인수 + MERGEFIELD / REF / PAGEREF / SEQ / STYLEREF / DOCPROPERTY / IF), [OLE 객체](https://github.com/iOfficeAI/OfficeCLI/wiki/word-ole), [문서 속성](https://github.com/iOfficeAI/OfficeCLI/wiki/word-document)
|
||||
|
||||
**Excel** — [셀](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-cell) (추가 시 음성 가이드/후리가나), 수식(150개 이상의 내장 함수 자동 계산, 동적 배열 함수에 `_xlfn.` 자동 접두사), [시트](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sheet) (visible/hidden/veryHidden, 인쇄 여백, printTitleRows/Cols, RTL `sheetView`, 캐스케이드 인식 시트 이름 변경), [테이블](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-table), [정렬](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sort) (시트/범위, 다중 키, 사이드카 인식), [조건부 서식](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-conditionalformatting), [차트](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart) (상자 수염, [파레토](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart-add) 자동 정렬 + 누적%, 로그 축 포함), [피벗 테이블](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-pivottable) (다중 필드, 날짜 그룹화, showDataAs, 정렬, 총합계, 부분합, 압축/개요/표 형식 레이아웃, 항목 레이블 반복, 빈 행, 계산 필드), [슬라이서](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-slicer), [이름 범위](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-namedrange), [데이터 유효성 검사](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-validation), [이미지](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-picture) (PNG/JPG/GIF/SVG, 이중 표현 폴백), [스파크라인](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sparkline), [메모](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-comment) (RTL), [자동 필터](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-autofilter), [도형](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-shape), [OLE 객체](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-ole), CSV/TSV 가져오기, `$Sheet:A1` 셀 주소 지정
|
||||
|
||||
**PowerPoint** — [슬라이드](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-slide) (머리글/바닥글/날짜/슬라이드 번호 토글, 숨김), [도형](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-shape) (패턴 채우기, 흐림 효과, 하이퍼링크 툴팁 + 슬라이드 점프 링크), [이미지](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-picture) (PNG/JPG/GIF/SVG, 채우기 모드: stretch/contain/cover/tile, 밝기/대비/광선/그림자), [표](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-table), [차트](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-chart), [애니메이션](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-slide), [모프 전환](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-morph-check), [3D 모델 (.glb)](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-3dmodel), [슬라이드 줌](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-zoom), [수식](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-equation), [테마](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-theme), [연결선](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-connector), [비디오/오디오](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-video), [그룹](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-group), [노트](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-notes) (RTL, lang), [메모](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-comment) (RTL), [OLE 객체](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-ole), [플레이스홀더](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-placeholder) (phType로 추가/설정)
|
||||
|
||||
## 사용 사례
|
||||
|
||||
**개발자용:**
|
||||
- 데이터베이스나 API에서 보고서 자동 생성
|
||||
- 문서 일괄 처리(일괄 검색/교체, 스타일 업데이트)
|
||||
- CI/CD 환경에서 문서 파이프라인 구축(테스트 결과에서 문서 생성)
|
||||
- Docker/컨테이너 환경에서의 헤드리스 Office 자동화
|
||||
|
||||
**AI 에이전트용:**
|
||||
- 사용자 프롬프트에서 프레젠테이션 생성(위 예시 참조)
|
||||
- 문서에서 구조화된 데이터를 JSON으로 추출
|
||||
- 납품 전 문서 품질 검증
|
||||
|
||||
**팀용:**
|
||||
- 문서 템플릿을 복제하고 데이터 입력
|
||||
- CI/CD 파이프라인에서 자동 문서 검증
|
||||
|
||||
## 설치
|
||||
|
||||
단일 자체 완결형 바이너리로 제공. .NET 런타임 내장 -- 설치할 것도, 관리할 런타임도 없습니다.
|
||||
|
||||
**원라인 설치:**
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
**또는 패키지 매니저로 설치:**
|
||||
|
||||
```bash
|
||||
# Homebrew (macOS / Linux)
|
||||
brew install officecli
|
||||
|
||||
# Scoop (Windows)
|
||||
scoop install officecli
|
||||
|
||||
# npm (모든 플랫폼 — 설치 시 플랫폼에 맞는 네이티브 바이너리를 받아옵니다)
|
||||
npm install -g @officecli/officecli
|
||||
```
|
||||
|
||||
**또는 수동 다운로드** [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases):
|
||||
|
||||
| 플랫폼 | 바이너리 |
|
||||
|--------|---------|
|
||||
| macOS Apple Silicon | `officecli-mac-arm64` |
|
||||
| macOS Intel | `officecli-mac-x64` |
|
||||
| Linux x64 | `officecli-linux-x64` |
|
||||
| Linux ARM64 | `officecli-linux-arm64` |
|
||||
| Windows x64 | `officecli-win-x64.exe` |
|
||||
| Windows ARM64 | `officecli-win-arm64.exe` |
|
||||
|
||||
설치 확인: `officecli --version`
|
||||
|
||||
**또는 다운로드한 바이너리에서 셀프 설치 (`officecli`를 직접 실행해도 설치가 트리거됩니다):**
|
||||
|
||||
```bash
|
||||
officecli install # 명시적 설치
|
||||
officecli # 직접 실행으로도 설치 트리거
|
||||
```
|
||||
|
||||
업데이트는 백그라운드에서 자동 확인됩니다. `officecli config autoUpdate false`로 비활성화하거나 `OFFICECLI_SKIP_UPDATE=1`로 단일 실행 시 건너뛸 수 있습니다. 설정은 `~/.officecli/config.json`에 있습니다.
|
||||
|
||||
## 주요 기능
|
||||
|
||||
### 내장 엔진과 생성 프리미티브
|
||||
|
||||
OfficeCLI는 자체 포함입니다. 아래 기능은 모두 바이너리 내장 — **Office 불필요**.
|
||||
|
||||
#### 렌더링 엔진 — 고충실도, 내장
|
||||
|
||||
OfficeCLI의 핵심: 처음부터 구현한 고충실도 HTML 렌더링 엔진으로, AI 에이전트가 DOM으로 추측하는 대신 렌더링된 문서를 "볼" 수 있게 합니다. 도형, 차트 (추세선, 오차 막대, 워터폴, 캔들스틱, 스파크라인), 수식 (OMML → LaTeX, KaTeX로 렌더링), Three.js로 렌더링되는 3D `.glb` 모델, 모프 전환, 슬라이드 줌, 도형 효과를 커버합니다. 페이지별 PNG 스크린샷은 렌더링된 HTML을 헤드리스 브라우저로 캡처해 생성됩니다. 세 가지 모드:
|
||||
|
||||
- **`view html`** — 독립형 HTML 파일, 에셋 인라인. 모든 브라우저에서 열 수 있습니다.
|
||||
- **`view screenshot`** — 페이지별 PNG, 멀티모달 에이전트용.
|
||||
- **`watch`** — 로컬 HTTP 서버 + 자동 새로고침 미리보기. `add` / `set` / `remove`마다 브라우저 즉시 업데이트. Excel watch는 인라인 셀 편집과 차트 드래그 재배치 지원.
|
||||
|
||||
```bash
|
||||
officecli view deck.pptx html -o /tmp/deck.html
|
||||
officecli view deck.pptx screenshot -o /tmp/deck.png # 여러 페이지는 --page 1-N
|
||||
officecli watch deck.pptx # http://localhost:26315
|
||||
```
|
||||
|
||||
> 시각화 없이는 슬라이드를 생성하는 에이전트는 눈먼 채로 비행하는 것과 같습니다 — DOM은 읽을 수 있지만 제목이 넘쳤는지, 두 도형이 겹쳤는지는 판단할 수 없습니다. 렌더링이 바이너리에 내장되어 있어 "렌더링 → 보기 → 수정" 루프는 CI, Docker, 디스플레이 없는 서버 — 바이너리가 실행되는 어디서나 작동합니다.
|
||||
|
||||
#### 수식 & 피벗 엔진
|
||||
|
||||
350+ Excel 함수가 작성 시 자동 평가 — `=SUM(A1:A2)`를 작성하고, 셀을 `get` 하면, 값이 이미 거기. Office에서 재계산하는 라운드트립 불필요. 스필되는 동적 배열 (`FILTER` / `SORT` / `UNIQUE` / `SEQUENCE` / `LET` / `LAMBDA`, `_xlfn.` 자동 접두사), `VLOOKUP` / `XLOOKUP` / `INDEX` / `MATCH`, 재무·채권 함수, 통계 분포·검정·회귀, 날짜 & 텍스트 함수 등 커버.
|
||||
|
||||
또한 소스 범위에서 단일 명령으로 네이티브 OOXML 피벗 테이블 — 멀티 필드 행/열/필터, 10가지 집계, `showDataAs` 모드, 날짜 그룹화, 계산 필드, Top-N, 레이아웃. 피벗 캐시 + 정의가 OOXML에 기록되어 Excel은 집계가 채워진 상태로 파일을 엽니다:
|
||||
|
||||
```bash
|
||||
officecli add sales.xlsx '/Sheet1' --type pivottable \
|
||||
--prop source='Data!A1:E10000' --prop rows='Region,Category' \
|
||||
--prop cols=Quarter --prop values='Revenue:sum,Units:avg' \
|
||||
--prop showDataAs=percentOfTotal
|
||||
```
|
||||
|
||||
#### 템플릿 병합 — 한 번 설계, N번 채우기
|
||||
|
||||
`merge`는 모든 `.docx` / `.xlsx` / `.pptx`의 `{{key}}` 자리표시자를 JSON 데이터로 교체 — 단락, 표 셀, 도형, 머리글/바닥글, 차트 제목 전체에서 작동. 에이전트가 한 번 레이아웃을 설계 (비싸다), 프로덕션 코드가 N번 채운다 (싸고, 결정론적, 토큰 비용 제로). 에이전트가 각 보고서를 처음부터 재생성하여 N개의 일관성 없는 레이아웃을 만드는 실패 모드를 피합니다.
|
||||
|
||||
```bash
|
||||
officecli merge invoice-template.docx out-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
officecli merge q4-template.pptx q4-acme.pptx data.json
|
||||
```
|
||||
|
||||
#### Dump 라운드트립 — 기존 문서에서 학습
|
||||
|
||||
`dump`는 모든 `.docx`, `.pptx`, `.xlsx`를 — 전체 문서 **또는 임의의 서브트리** (단일 단락, 표, 슬라이드, 워크시트, styles, numbering, theme, settings) — 재생 가능한 batch JSON으로 직렬화하고, `batch`가 재생합니다. 사용자가 모방하고 싶은 샘플 문서가 주어지면, 에이전트는 원시 OOXML XML이 아닌 구조화된 사양을 읽고, 변경하여 재생합니다. "기존 템플릿이 있다"와 "100개 변형을 생성해 줘" 사이의 다리.
|
||||
|
||||
```bash
|
||||
officecli dump existing.docx -o blueprint.json # 전체 문서
|
||||
officecli dump existing.docx /body/tbl[1] -o table.json # 임의의 서브트리
|
||||
officecli dump existing.xlsx /Sheet1 -o sheet.json # 단일 워크시트
|
||||
officecli batch new.docx --input blueprint.json
|
||||
```
|
||||
|
||||
### 레지던트 모드와 배치
|
||||
|
||||
다단계 워크플로우에서 레지던트 모드는 문서를 메모리에 유지합니다. 배치 모드는 한 번의 open/save 사이클에서 여러 작업을 실행합니다.
|
||||
|
||||
```bash
|
||||
# 레지던트 모드 — 명명된 파이프로 거의 제로 지연
|
||||
officecli open report.docx
|
||||
officecli set report.docx /body/p[1]/r[1] --prop bold=true
|
||||
officecli set report.docx /body/p[2]/r[1] --prop color=FF0000
|
||||
officecli close report.docx
|
||||
|
||||
# 배치 모드 — 원자적 다중 명령 실행 (기본적으로 첫 오류에서 중지)
|
||||
echo '[{"command":"set","path":"/slide[1]/shape[1]","props":{"text":"Hello"}},
|
||||
{"command":"set","path":"/slide[1]/shape[2]","props":{"fill":"FF0000"}}]' \
|
||||
| officecli batch deck.pptx --json
|
||||
|
||||
# 인라인 배치 — stdin 불필요
|
||||
officecli batch deck.pptx --commands '[{"op":"set","path":"/slide[1]/shape[1]","props":{"text":"Hi"}}]'
|
||||
|
||||
# --force로 오류를 건너뛰고 계속 실행
|
||||
officecli batch deck.pptx --input updates.json --force --json
|
||||
```
|
||||
|
||||
### 3계층 아키텍처
|
||||
|
||||
간단하게 시작하고, 필요할 때만 깊이 들어가세요.
|
||||
|
||||
| 레이어 | 용도 | 명령어 |
|
||||
|--------|------|--------|
|
||||
| **L1: 읽기** | 콘텐츠의 시맨틱 뷰 | `view` (text, annotated, outline, stats, issues, html, svg, screenshot) |
|
||||
| **L2: DOM** | 구조화된 요소 작업 | `get`, `query`, `set`, `add`, `remove`, `move`, `swap` |
|
||||
| **L3: 원시 XML** | XPath 직접 접근 — 범용 폴백 | `raw`, `raw-set`, `add-part`, `validate` |
|
||||
|
||||
```bash
|
||||
# L1 — 고수준 뷰
|
||||
officecli view report.docx annotated
|
||||
officecli view budget.xlsx text --cols A,B,C --max-lines 50
|
||||
|
||||
# L2 — 요소 수준 작업
|
||||
officecli query report.docx "run:contains(TODO)"
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q2 Report"
|
||||
officecli move report.docx /body/p[5] --to /body --index 1
|
||||
|
||||
# L3 — L2로 부족할 때 원시 XML
|
||||
officecli raw deck.pptx '/slide[1]'
|
||||
officecli raw-set report.docx document \
|
||||
--xpath "//w:p[1]" --action append \
|
||||
--xml '<w:r><w:t>Injected text</w:t></w:r>'
|
||||
```
|
||||
|
||||
## AI 통합
|
||||
|
||||
### MCP 서버
|
||||
|
||||
내장 [MCP](https://modelcontextprotocol.io) 서버 — 명령어 하나로 등록:
|
||||
|
||||
```bash
|
||||
officecli mcp claude # Claude Code
|
||||
officecli mcp cursor # Cursor
|
||||
officecli mcp vscode # VS Code / Copilot
|
||||
officecli mcp lmstudio # LM Studio
|
||||
officecli mcp list # 등록 상태 확인
|
||||
```
|
||||
|
||||
JSON-RPC로 모든 문서 작업을 제공 — 셸 접근 불필요.
|
||||
|
||||
### 직접 CLI 통합
|
||||
|
||||
2단계로 OfficeCLI를 모든 AI 에이전트에 통합:
|
||||
|
||||
1. **바이너리 설치** -- 명령어 하나 ([설치](#설치) 참조)
|
||||
2. **완료.** OfficeCLI가 AI 도구(Claude Code, GitHub Copilot, Codex)를 자동 감지하고, 알려진 설정 디렉토리를 확인하여 스킬 파일을 설치합니다. 에이전트는 즉시 Office 문서를 생성, 읽기, 수정할 수 있습니다.
|
||||
|
||||
<details>
|
||||
<summary><strong>수동 설정 (선택사항)</strong></summary>
|
||||
|
||||
자동 설치가 환경을 지원하지 않는 경우, 스킬 파일을 수동으로 설치할 수 있습니다:
|
||||
|
||||
**SKILL.md를 에이전트에 직접 제공:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
**Claude Code 로컬 스킬로 설치:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md -o ~/.claude/skills/officecli.md
|
||||
```
|
||||
|
||||
**기타 에이전트:** `SKILL.md`의 내용을 에이전트의 시스템 프롬프트 또는 도구 설명에 포함하세요.
|
||||
|
||||
</details>
|
||||
|
||||
### 에이전트가 OfficeCLI에서 잘 동작하는 이유
|
||||
|
||||
- **결정론적 JSON 출력** — 모든 명령이 `--json`을 지원하며 스키마가 일관됩니다. 정규표현식 파싱 불필요, stdout 스크래핑 불필요.
|
||||
- **경로 기반 주소 지정** — 모든 요소에 안정적인 경로 (`/slide[1]/shape[2]`). 에이전트는 XML 네임스페이스를 이해하지 않고도 문서를 탐색합니다. (OfficeCLI 자체 구문: 1-based 인덱스, 요소 로컬 이름 — XPath 아님.)
|
||||
- **점진적 복잡도 (L1 → L2 → L3)** — 에이전트는 읽기 전용 뷰부터 시작해, DOM 작업으로 에스컬레이트, 필요할 때만 raw XML로 폴백. 토큰 사용을 최소화.
|
||||
- **자가 치유 워크플로우** — `validate`, `view issues`, 그리고 구조화된 에러 코드 (`not_found`, `invalid_value`, `unsupported_property`) 가 suggestion과 유효 범위를 반환합니다. 에이전트는 사람의 개입 없이 자가 수정.
|
||||
- **내장 에이전트 친화적 렌더링 엔진** — `view html` / `view screenshot` / `watch`가 네이티브로 HTML과 PNG를 출력. Office 불필요. 에이전트는 CI / Docker / 헤드리스 환경에서도 자신의 출력을 "보고" 레이아웃 문제를 수정할 수 있습니다.
|
||||
- **내장 수식 & 피벗 엔진** — 350+ Excel 함수 작성 시 자동 평가 (스필되는 동적 배열, 재무·채권·통계 함수군 포함); 소스 범위에서 단일 명령으로 네이티브 OOXML 피벗 테이블. 에이전트는 Office에서 재계산할 필요 없이 계산값과 집계 결과를 즉시 읽습니다.
|
||||
- **템플릿 병합** — 에이전트가 한 번 레이아웃을 설계, 다운스트림 코드가 `{{key}}` 자리표시자를 N번 채움. 각 보고서를 재생성하며 토큰을 태우는 것을 방지.
|
||||
- **라운드트립 Dump** — `dump`가 모든 `.docx`, `.pptx`, `.xlsx`를 재생 가능한 batch JSON으로. 에이전트는 raw OOXML XML이 아닌 구조화된 사양을 읽어 인간이 작성한 샘플에서 학습.
|
||||
- **내장 도움말** — 속성명이나 값 형식이 헷갈릴 때, 에이전트는 추측하지 않고 `officecli <format> set <element>`를 실행.
|
||||
- **자동 설치** — OfficeCLI는 AI 도구 (Claude Code, Cursor, VS Code…) 를 감지하고 자가 구성합니다. 수동 skill 파일 설정 불필요.
|
||||
|
||||
### 내장 도움말
|
||||
|
||||
속성 이름을 모를 때, 계층형 도움말로 확인:
|
||||
|
||||
```bash
|
||||
officecli pptx set # 모든 설정 가능한 요소와 속성
|
||||
officecli pptx set shape # 특정 요소 유형의 세부사항
|
||||
officecli pptx set shape.fill # 단일 속성 형식과 예시
|
||||
officecli docx query # 셀렉터 참조: 속성, :contains, :has() 등
|
||||
```
|
||||
|
||||
`pptx`를 `docx`나 `xlsx`로 대체 가능. 동사는 `view`, `get`, `query`, `set`, `add`, `raw`.
|
||||
|
||||
`officecli --help`로 전체 개요 확인.
|
||||
|
||||
### JSON 출력 스키마
|
||||
|
||||
모든 명령어가 `--json`을 지원합니다. 일반적인 응답 형식:
|
||||
|
||||
**단일 요소** (`get --json`):
|
||||
|
||||
```json
|
||||
{"tag": "shape", "path": "/slide[1]/shape[1]", "attributes": {"name": "TextBox 1", "text": "Hello"}}
|
||||
```
|
||||
|
||||
**요소 목록** (`query --json`):
|
||||
|
||||
```json
|
||||
[
|
||||
{"tag": "paragraph", "path": "/body/p[1]", "attributes": {"style": "Heading1", "text": "Title"}},
|
||||
{"tag": "paragraph", "path": "/body/p[5]", "attributes": {"style": "Heading1", "text": "Summary"}}
|
||||
]
|
||||
```
|
||||
|
||||
**오류**는 구조화된 오류 객체를 반환합니다. 오류 코드, 수정 제안, 사용 가능한 값을 포함:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"error": "Slide 50 not found (total: 8)",
|
||||
"code": "not_found",
|
||||
"suggestion": "Valid Slide index range: 1-8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
오류 코드: `not_found`, `invalid_value`, `unsupported_property`, `invalid_path`, `unsupported_type`, `missing_property`, `file_not_found`, `file_locked`, `invalid_selector`. 속성 이름은 자동 교정 지원 -- 속성 이름 오타 시 가장 근접한 매칭을 제안합니다.
|
||||
|
||||
**오류 복구** -- 에이전트가 사용 가능한 요소를 확인하여 자체 수정:
|
||||
|
||||
```bash
|
||||
# 에이전트가 잘못된 경로 시도
|
||||
officecli get report.docx /body/p[99] --json
|
||||
# 반환: {"success": false, "error": {"error": "...", "code": "not_found", "suggestion": "..."}}
|
||||
|
||||
# 에이전트가 사용 가능한 요소를 확인하여 자체 수정
|
||||
officecli get report.docx /body --depth 1 --json
|
||||
# 사용 가능한 하위 요소 목록 반환, 에이전트가 올바른 경로 선택
|
||||
```
|
||||
|
||||
**변경 확인** (`set`, `add`, `remove`, `move`, `create`에서 `--json` 사용 시):
|
||||
|
||||
```json
|
||||
{"success": true, "path": "/slide[1]/shape[1]"}
|
||||
```
|
||||
|
||||
`officecli --help`로 종료 코드와 오류 형식의 전체 설명 확인.
|
||||
|
||||
## 비교
|
||||
|
||||
| | OfficeCLI | Microsoft Office | LibreOffice | python-docx / openpyxl |
|
||||
|---|---|---|---|---|
|
||||
| 오픈소스 & 무료 | ✓ (Apache 2.0) | ✗ (유료 라이선스) | ✓ | ✓ |
|
||||
| AI 네이티브 CLI + JSON | ✓ | ✗ | ✗ | ✗ |
|
||||
| 제로 설치 (단일 바이너리) | ✓ | ✗ | ✗ | ✗ (Python + pip 필요) |
|
||||
| 모든 언어에서 호출 | ✓ (CLI) | ✗ (COM/Add-in) | ✗ (UNO API) | Python만 |
|
||||
| 경로 기반 요소 접근 | ✓ | ✗ | ✗ | ✗ |
|
||||
| 원시 XML 폴백 | ✓ | ✗ | ✗ | 부분 지원 |
|
||||
| 내장 에이전트 친화적 렌더링 엔진 | ✓ | ✗ | ✗ | ✗ |
|
||||
| 헤드리스 HTML/PNG 출력 | ✓ | ✗ | 부분 지원 | ✗ |
|
||||
| 크로스 포맷 템플릿 병합 (`{{key}}`) | ✓ | ✗ | ✗ | ✗ |
|
||||
| Dump → batch JSON 라운드트립 | ✓ | ✗ | ✗ | ✗ |
|
||||
| 라이브 미리보기 (편집 후 자동 새로고침) | ✓ | ✗ | ✗ | ✗ |
|
||||
| 헤드리스 / CI | ✓ | ✗ | 부분 지원 | ✓ |
|
||||
| 크로스 플랫폼 | ✓ | Windows/Mac | ✓ | ✓ |
|
||||
| Word + Excel + PowerPoint | ✓ | ✓ | ✓ | 여러 라이브러리 필요 |
|
||||
|
||||
## 명령어 참조
|
||||
|
||||
| 명령어 | 설명 |
|
||||
|--------|------|
|
||||
| [`create`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-create) | 빈 .docx, .xlsx, .pptx 생성 (확장자로 유형 결정) |
|
||||
| [`view`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-view) | 콘텐츠 보기 (모드: `outline`, `text`, `annotated`, `stats`, `issues`, `html`) |
|
||||
| [`get`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-get) | 요소와 하위 요소 가져오기 (`--depth N`, `--json`) |
|
||||
| [`query`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-query) | CSS 스타일 쿼리 (`[attr=value]`, `:contains()`, `:has()` 등) |
|
||||
| [`set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-set) | 요소 속성 수정 |
|
||||
| [`add`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-add) | 요소 추가 (또는 `--from <path>`로 복제) |
|
||||
| [`remove`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-remove) | 요소 삭제 |
|
||||
| [`move`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-move) | 요소 이동 (`--to <parent>`, `--index N`, `--after <path>`, `--before <path>`) |
|
||||
| [`swap`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-swap) | 두 요소 교체 |
|
||||
| [`validate`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-validate) | OpenXML 스키마 검증 |
|
||||
| [`batch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-batch) | 한 번의 open/save 사이클에서 여러 작업 실행 (stdin, `--input`, 또는 `--commands`; 기본적으로 첫 오류에서 중지, `--force`로 계속) |
|
||||
| [`merge`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-merge) | 템플릿 병합 — `{{key}}` 플레이스홀더를 JSON 데이터로 교체 |
|
||||
| [`watch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-watch) | 브라우저에서 라이브 HTML 미리보기, 자동 새로고침 |
|
||||
| [`mcp`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-mcp) | AI 도구 통합용 MCP 서버 시작 |
|
||||
| [`raw`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | 문서 파트의 원시 XML 보기 |
|
||||
| [`raw-set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | XPath로 원시 XML 수정 |
|
||||
| `add-part` | 새 문서 파트 추가 (머리글, 차트 등) |
|
||||
| [`open`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-open) | 레지던트 모드 시작 (문서를 메모리에 유지) |
|
||||
| `close` | 저장하고 레지던트 모드 종료 |
|
||||
| [`install`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-install) | 바이너리 + 스킬 + MCP 설치 (`all`, `claude`, `cursor` 등) |
|
||||
| `config` | 설정 가져오기 또는 변경 |
|
||||
| `<format> <command>` | [내장 도움말](https://github.com/iOfficeAI/OfficeCLI/wiki/command-reference) (예: `officecli pptx set shape`) |
|
||||
|
||||
## 엔드투엔드 워크플로우 예시
|
||||
|
||||
전형적인 에이전트 자가 치유 워크플로우: 프레젠테이션 생성, 콘텐츠 입력, 검증, 문제 수정 -- 모두 사람의 개입 없이.
|
||||
|
||||
```bash
|
||||
# 1. 생성
|
||||
officecli create report.pptx
|
||||
|
||||
# 2. 콘텐츠 추가
|
||||
officecli add report.pptx / --type slide --prop title="Q4 Results"
|
||||
officecli add report.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue: $4.2M" --prop x=2cm --prop y=5cm --prop size=28
|
||||
officecli add report.pptx / --type slide --prop title="Details"
|
||||
officecli add report.pptx '/slide[2]' --type shape \
|
||||
--prop text="Growth driven by new markets" --prop x=2cm --prop y=5cm
|
||||
|
||||
# 3. 검증
|
||||
officecli view report.pptx outline
|
||||
officecli validate report.pptx
|
||||
|
||||
# 4. 문제 수정
|
||||
officecli view report.pptx issues --json
|
||||
# 출력에 따라 문제 수정:
|
||||
officecli set report.pptx '/slide[1]/shape[1]' --prop font=Arial
|
||||
```
|
||||
|
||||
### 단위와 색상
|
||||
|
||||
모든 치수 및 색상 속성은 유연한 입력 형식을 지원:
|
||||
|
||||
| 유형 | 지원 형식 | 예시 |
|
||||
|------|----------|------|
|
||||
| **치수** | cm, in, pt, px 또는 원시 EMU | `2cm`, `1in`, `72pt`, `96px`, `914400` |
|
||||
| **색상** | 16진수, 색상 이름, RGB, 테마 색상 | `#FF0000`, `FF0000`, `red`, `rgb(255,0,0)`, `accent1` |
|
||||
| **글꼴 크기** | 숫자만 또는 pt 접미사 | `14`, `14pt`, `10.5pt` |
|
||||
| **간격** | pt, cm, in 또는 배율 | `12pt`, `0.5cm`, `1.5x`, `150%` |
|
||||
|
||||
## 자주 사용하는 패턴
|
||||
|
||||
```bash
|
||||
# Word 문서의 모든 Heading1 텍스트 교체
|
||||
officecli query report.docx "paragraph[style=Heading1]" --json | ...
|
||||
officecli set report.docx /body/p[1]/r[1] --prop text="New Title"
|
||||
|
||||
# 모든 슬라이드 콘텐츠를 JSON으로 내보내기
|
||||
officecli get deck.pptx / --depth 2 --json
|
||||
|
||||
# Excel 셀 일괄 업데이트
|
||||
officecli batch budget.xlsx --input updates.json --json
|
||||
|
||||
# CSV 데이터를 Excel 시트로 가져오기
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q1 Data" --prop csv=sales.csv
|
||||
|
||||
# 템플릿 병합으로 보고서 일괄 생성
|
||||
officecli merge invoice-template.docx invoice-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
|
||||
# 납품 전 문서 품질 확인
|
||||
officecli validate report.docx && officecli view report.docx issues --json
|
||||
```
|
||||
|
||||
**Python에서 호출** — 한 번 래핑하면 모든 호출이 파싱된 JSON을 반환합니다:
|
||||
|
||||
```python
|
||||
import json, subprocess
|
||||
|
||||
def cli(*args):
|
||||
return json.loads(subprocess.check_output(["officecli", *args, "--json"], text=True))
|
||||
|
||||
cli("create", "deck.pptx")
|
||||
cli("add", "deck.pptx", "/", "--type", "slide", "--prop", "title=Q4 보고서")
|
||||
slide = cli("get", "deck.pptx", "/slide[1]")
|
||||
print(slide["attributes"]["text"])
|
||||
```
|
||||
|
||||
## 문서
|
||||
|
||||
[Wiki](https://github.com/iOfficeAI/OfficeCLI/wiki)에서 모든 명령어, 요소 유형, 속성의 상세 가이드를 확인하세요:
|
||||
|
||||
- **형식별:** [Word](https://github.com/iOfficeAI/OfficeCLI/wiki/word-reference) | [Excel](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-reference) | [PowerPoint](https://github.com/iOfficeAI/OfficeCLI/wiki/powerpoint-reference)
|
||||
- **워크플로우:** [엔드투엔드 예시](https://github.com/iOfficeAI/OfficeCLI/wiki/workflows) -- Word 보고서, Excel 대시보드, PPT 프레젠테이션, 일괄 수정, 레지던트 모드
|
||||
- **문제 해결:** [자주 발생하는 오류와 해결책](https://github.com/iOfficeAI/OfficeCLI/wiki/troubleshooting)
|
||||
- **AI 에이전트 가이드:** [Wiki 내비게이션 결정 트리](https://github.com/iOfficeAI/OfficeCLI/wiki/agent-guide)
|
||||
|
||||
## 소스에서 빌드
|
||||
|
||||
컴파일에는 [.NET 10 SDK](https://dotnet.microsoft.com/download)가 필요합니다. 출력은 자체 완결형 네이티브 바이너리 -- .NET이 내장되어 있어 실행 시 설치 불필요.
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
## 라이선스
|
||||
|
||||
[Apache License 2.0](LICENSE)
|
||||
|
||||
버그 리포트와 기여는 [GitHub Issues](https://github.com/iOfficeAI/OfficeCLI/issues)로 환영합니다.
|
||||
|
||||
---
|
||||
|
||||
OfficeCLI가 유용하다면 [GitHub에서 스타를 눌러주세요](https://github.com/iOfficeAI/OfficeCLI) — 더 많은 사람들이 프로젝트를 발견하는 데 도움이 됩니다.
|
||||
|
||||
[OfficeCLI.AI](https://OfficeCLI.AI) | [GitHub](https://github.com/iOfficeAI/OfficeCLI)
|
||||
|
||||
<!--
|
||||
yaml-frontmatter:
|
||||
name: officecli
|
||||
version: see GitHub Releases
|
||||
description: The standard CLI for AI agents to create, read, and modify Office documents
|
||||
formats: [docx, xlsx, pptx]
|
||||
platforms: [macos, linux, windows]
|
||||
license: Apache-2.0
|
||||
keywords: [office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint]
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
|
||||
<!-- LLM/agent discovery metadata
|
||||
tool: officecli
|
||||
type: cli
|
||||
canonical: the standard CLI for AI-driven Office document manipulation
|
||||
formats: docx, xlsx, pptx
|
||||
capabilities: create, read, modify, validate, batch, resident-mode, mcp-server, live-preview, auto-install
|
||||
output: json, text, annotated
|
||||
platforms: macos, linux, windows
|
||||
license: Apache-2.0
|
||||
keywords: office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint, ai-tools, command-line, structured-output
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
alternatives: python-docx, openpyxl, python-pptx, libreoffice --headless
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
@@ -0,0 +1,669 @@
|
||||
# OfficeCLI
|
||||
|
||||
> **OfficeCLI 是全球首个、也是最好的专为 AI 智能体设计的 Office 套件。**
|
||||
|
||||
**让任何 AI 智能体完全掌控 Word、Excel 和 PowerPoint——只需一行代码。**
|
||||
|
||||
开源免费。单一可执行文件。无需安装 Office。零依赖。全平台运行。
|
||||
|
||||
**OfficeCLI 的内置 HTML 渲染引擎,高度还原文档原貌 —— 这正是让 AI 拥有"眼睛"的关键。** 它把 `.docx` / `.xlsx` / `.pptx` 渲染为 HTML 或 PNG,闭合"渲染 → 看 → 改"的循环。
|
||||
|
||||
[](https://github.com/iOfficeAI/OfficeCLI/releases)
|
||||
[](LICENSE)
|
||||
|
||||
[English](README.md) | **中文** | [日本語](README_ja.md) | [한국어](README_ko.md)
|
||||
|
||||
<p align="center">
|
||||
<strong>🌐 官方网站:</strong> <a href="https://officecli.ai" target="_blank">officecli.ai</a> | <strong>💬 社区:</strong> <a href="https://discord.gg/2QAwJn7Egx" target="_blank">Discord</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/ppt-process.webp" alt="在 AionUi 上使用 OfficeCLI 的 PPT 制作过程" width="100%">
|
||||
</p>
|
||||
|
||||
<p align="center"><em>在 <a href="https://github.com/iOfficeAI/AionUi">AionUi</a> 上使用 OfficeCLI 的 PPT 制作过程</em></p>
|
||||
|
||||
<p align="center"><strong>PowerPoint 演示文稿</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/designwhatmovesyou.gif" alt="OfficeCLI 设计演示 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/horizon.gif" alt="OfficeCLI 商务演示 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/efforless.gif" alt="OfficeCLI 科技演示 (PowerPoint)"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/blackhole.gif" alt="OfficeCLI 太空演示 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/first-ppt-aionui.gif" alt="OfficeCLI 游戏演示 (PowerPoint)"></td>
|
||||
<td width="33%"><img src="assets/shiba.gif" alt="OfficeCLI 创意演示 (PowerPoint)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Word 文档</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/word1.gif" alt="OfficeCLI 学术论文 (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word2.gif" alt="OfficeCLI 项目建议书 (Word)"></td>
|
||||
<td width="33%"><img src="assets/showcase/word3.gif" alt="OfficeCLI 年度报告 (Word)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">—</p>
|
||||
<p align="center"><strong>Excel 电子表格</strong></p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%"><img src="assets/showcase/excel1.gif" alt="OfficeCLI 预算跟踪 (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel2.gif" alt="OfficeCLI 成绩管理 (Excel)"></td>
|
||||
<td width="33%"><img src="assets/showcase/excel3.gif" alt="OfficeCLI 销售仪表盘 (Excel)"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center"><em>以上所有文档均由 AI 智能体使用 OfficeCLI 全自动创建 — 无模板、无人工编辑。</em></p>
|
||||
|
||||
## AI 智能体 — 一行搞定
|
||||
|
||||
把这行粘贴到你的 AI 智能体对话框 — 它会自动读取技能文件并完成安装:
|
||||
|
||||
```
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
就这一步。技能文件会教智能体如何安装二进制文件并使用所有命令。
|
||||
|
||||
## 普通用户
|
||||
|
||||
**方式 A — 图形界面:** 安装 [**AionUi**](https://github.com/iOfficeAI/AionUi) — 一款桌面应用,用自然语言就能创建和编辑 Office 文档,底层由 OfficeCLI 驱动。只需描述你想要什么,AionUi 帮你搞定。
|
||||
|
||||
**方式 B — 命令行:** 从 [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases) 下载对应平台的二进制文件,然后运行:
|
||||
|
||||
```bash
|
||||
officecli install
|
||||
```
|
||||
|
||||
该命令会将二进制文件复制到 PATH,并自动将 **officecli 技能文件**安装到检测到的所有 AI 编程助手 — Claude Code、Cursor、Windsurf、GitHub Copilot 等。您的智能体可以立即创建、读取和编辑 Office 文档,无需额外配置。
|
||||
|
||||
## 开发者 — 30 秒亲眼看到效果
|
||||
|
||||
```bash
|
||||
# 1. 安装(macOS / Linux)— 也可以:brew install officecli / npm install -g @officecli/officecli
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
# Windows (PowerShell): irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
|
||||
# 2. 创建一个空白 PowerPoint
|
||||
officecli create deck.pptx
|
||||
|
||||
# 3. 启动实时预览 — 浏览器自动打开 http://localhost:26315
|
||||
officecli watch deck.pptx
|
||||
|
||||
# 4. 打开另一个终端,添加一页幻灯片 — 浏览器即时刷新
|
||||
officecli add deck.pptx / --type slide --prop title="Hello, World!"
|
||||
```
|
||||
|
||||
就这么简单。你执行的每一条 `add`、`set`、`remove` 命令都会实时刷新预览。继续尝试吧 — 浏览器就是你的实时反馈窗口。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 创建演示文稿并添加内容
|
||||
officecli create deck.pptx
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report" --prop background=1A1A2E
|
||||
officecli add deck.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue grew 25%" --prop x=2cm --prop y=5cm \
|
||||
--prop font=Arial --prop size=24 --prop color=FFFFFF
|
||||
|
||||
# 查看大纲
|
||||
officecli view deck.pptx outline
|
||||
# → Slide 1: Q4 Report
|
||||
# → Shape 1 [TextBox]: Revenue grew 25%
|
||||
|
||||
# 查看 HTML — 在浏览器中打开渲染预览,无需启动服务器
|
||||
officecli view deck.pptx html
|
||||
|
||||
# 获取任意元素的结构化 JSON
|
||||
officecli get deck.pptx '/slide[1]/shape[1]' --json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"tag": "shape",
|
||||
"path": "/slide[1]/shape[1]",
|
||||
"attributes": {
|
||||
"name": "TextBox 1",
|
||||
"text": "Revenue grew 25%",
|
||||
"x": "720000",
|
||||
"y": "1800000"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 为什么选择 OfficeCLI?
|
||||
|
||||
以前需要 50 行 Python 和 3 个独立库:
|
||||
|
||||
```python
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches, Pt
|
||||
prs = Presentation()
|
||||
slide = prs.slides.add_slide(prs.slide_layouts[0])
|
||||
title = slide.shapes.title
|
||||
title.text = "Q4 Report"
|
||||
# ... 还有 45 行 ...
|
||||
prs.save('deck.pptx')
|
||||
```
|
||||
|
||||
现在只需一条命令:
|
||||
|
||||
```bash
|
||||
officecli add deck.pptx / --type slide --prop title="Q4 Report"
|
||||
```
|
||||
|
||||
**OfficeCLI 能做什么:**
|
||||
|
||||
- **创建** 文档 -- 空白文档或带内容的文档
|
||||
- **读取** 文本、结构、样式、公式 -- 纯文本或结构化 JSON
|
||||
- **分析** 格式问题、样式不一致和结构缺陷
|
||||
- **修改** 任意元素 -- 文本、字体、颜色、布局、公式、图表、图片
|
||||
- **重组** 内容 -- 添加、删除、移动、复制跨文档元素
|
||||
|
||||
| 格式 | 读取 | 修改 | 创建 |
|
||||
|------|------|------|------|
|
||||
| Word (.docx) | ✅ | ✅ | ✅ |
|
||||
| Excel (.xlsx) | ✅ | ✅ | ✅ |
|
||||
| PowerPoint (.pptx) | ✅ | ✅ | ✅ |
|
||||
|
||||
**Word** — 完整的 [i18n 与 RTL 支持](https://github.com/iOfficeAI/OfficeCLI/wiki/i18n)(按脚本字体槽位、按脚本 BCP-47 语言标签 `lang.latin/ea/cs`、复杂脚本粗体/斜体/字号、`direction=rtl` 在段落/文本片段/节/表格/样式/页眉/页脚/docDefaults 间级联、`rtlGutter` + `pgBorders` 简写、印地语/阿拉伯语/泰语/中日韩本地化页码)、[段落](https://github.com/iOfficeAI/OfficeCLI/wiki/word-paragraph)、[文本片段](https://github.com/iOfficeAI/OfficeCLI/wiki/word-run)、[表格](https://github.com/iOfficeAI/OfficeCLI/wiki/word-table)、[样式](https://github.com/iOfficeAI/OfficeCLI/wiki/word-style)、[页眉/页脚](https://github.com/iOfficeAI/OfficeCLI/wiki/word-header-footer)、[图片](https://github.com/iOfficeAI/OfficeCLI/wiki/word-picture)(PNG/JPG/GIF/SVG)、[公式](https://github.com/iOfficeAI/OfficeCLI/wiki/word-equation)、[批注](https://github.com/iOfficeAI/OfficeCLI/wiki/word-comment)、[脚注](https://github.com/iOfficeAI/OfficeCLI/wiki/word-footnote)、[水印](https://github.com/iOfficeAI/OfficeCLI/wiki/word-watermark)、[书签](https://github.com/iOfficeAI/OfficeCLI/wiki/word-bookmark)、[目录](https://github.com/iOfficeAI/OfficeCLI/wiki/word-toc)、[图表](https://github.com/iOfficeAI/OfficeCLI/wiki/word-chart)、[超链接](https://github.com/iOfficeAI/OfficeCLI/wiki/word-hyperlink)、[节](https://github.com/iOfficeAI/OfficeCLI/wiki/word-section)、[表单域](https://github.com/iOfficeAI/OfficeCLI/wiki/word-formfield)、[内容控件 (SDT)](https://github.com/iOfficeAI/OfficeCLI/wiki/word-sdt)、[域](https://github.com/iOfficeAI/OfficeCLI/wiki/word-field)(22 种零参数 + MERGEFIELD / REF / PAGEREF / SEQ / STYLEREF / DOCPROPERTY / IF)、[OLE 对象](https://github.com/iOfficeAI/OfficeCLI/wiki/word-ole)、[文档属性](https://github.com/iOfficeAI/OfficeCLI/wiki/word-document)
|
||||
|
||||
**Excel** — [单元格](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-cell)(添加时支持音标/振假名)、公式(内置 350+ 函数自动求值,可溢出的动态数组自动加 `_xlfn.` 前缀,含财务/债券与统计函数族)、[工作表](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sheet)(visible/hidden/veryHidden、打印边距、printTitleRows/Cols、RTL `sheetView`、级联感知的工作表重命名)、[表格](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-table)、[排序](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sort)(工作表/区域、多键、附属感知)、[条件格式](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-conditionalformatting)、[图表](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart)(含箱线图、[帕累托图](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-chart-add) 自动排序 + 累计百分比、对数轴)、[数据透视表](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-pivottable)(多字段、日期分组、showDataAs、排序、总计、分类汇总、紧凑/大纲/表格布局、重复项目标签、空白行、计算字段)、[切片器](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-slicer)、[命名范围](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-namedrange)、[数据验证](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-validation)、[图片](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-picture)(PNG/JPG/GIF/SVG,双重表示回退)、[迷你图](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-sparkline)、[批注](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-comment)(RTL)、[自动筛选](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-autofilter)、[形状](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-shape)、[OLE 对象](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-ole)、CSV/TSV 导入、`$Sheet:A1` 单元格寻址
|
||||
|
||||
**PowerPoint** — [幻灯片](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-slide)(页眉/页脚/日期/页码切换、隐藏)、[形状](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-shape)(图案填充、模糊效果、超链接提示 + 跳转幻灯片链接)、[图片](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-picture)(PNG/JPG/GIF/SVG,填充模式:stretch/contain/cover/tile,亮度/对比度/发光/阴影)、[表格](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-table)、[图表](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-chart)、[动画](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-slide)、[morph 过渡](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-morph-check)、[3D 模型(.glb)](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-3dmodel)、[幻灯片缩放](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-zoom)、[公式](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-equation)、[主题](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-theme)、[连接线](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-connector)、[视频/音频](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-video)、[组合](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-group)、[备注](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-notes)(RTL、lang)、[批注](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-comment)(RTL)、[OLE 对象](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-ole)、[占位符](https://github.com/iOfficeAI/OfficeCLI/wiki/ppt-placeholder)(按 phType 添加/设置)
|
||||
|
||||
## 使用场景
|
||||
|
||||
**开发者:**
|
||||
- 从数据库或 API 自动生成报告
|
||||
- 批量处理文档(批量查找/替换、样式更新)
|
||||
- 在 CI/CD 环境中构建文档流水线(从测试结果生成文档)
|
||||
- Docker/容器化环境中的无头 Office 自动化
|
||||
|
||||
**AI 智能体:**
|
||||
- 根据用户提示生成演示文稿(见上方示例)
|
||||
- 从文档提取结构化数据到 JSON
|
||||
- 交付前验证和检查文档质量
|
||||
|
||||
**团队:**
|
||||
- 克隆文档模板并填充数据
|
||||
- CI/CD 流水线中的自动化文档验证
|
||||
|
||||
## 安装
|
||||
|
||||
单一自包含可执行文件,.NET 运行时已内嵌 -- 无需安装任何依赖,无需管理运行时。
|
||||
|
||||
**一键安装:**
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
**或通过包管理器安装:**
|
||||
|
||||
```bash
|
||||
# Homebrew(macOS / Linux)
|
||||
brew install officecli
|
||||
|
||||
# Scoop(Windows)
|
||||
scoop install officecli
|
||||
|
||||
# npm(全平台 — 安装时自动拉取对应平台的原生二进制)
|
||||
npm install -g @officecli/officecli
|
||||
```
|
||||
|
||||
**或手动下载** [GitHub Releases](https://github.com/iOfficeAI/OfficeCLI/releases):
|
||||
|
||||
| 平台 | 文件名 |
|
||||
|------|--------|
|
||||
| macOS Apple Silicon | `officecli-mac-arm64` |
|
||||
| macOS Intel | `officecli-mac-x64` |
|
||||
| Linux x64 | `officecli-linux-x64` |
|
||||
| Linux ARM64 | `officecli-linux-arm64` |
|
||||
| Windows x64 | `officecli-win-x64.exe` |
|
||||
| Windows ARM64 | `officecli-win-arm64.exe` |
|
||||
|
||||
验证安装:`officecli --version`
|
||||
|
||||
**或从已下载的二进制文件自安装(直接运行 `officecli` 也会触发安装):**
|
||||
|
||||
```bash
|
||||
officecli install # 显式安装
|
||||
officecli # 直接运行也会触发安装
|
||||
```
|
||||
|
||||
OfficeCLI 会在后台自动检查更新。通过 `officecli config autoUpdate false` 关闭,或通过 `OFFICECLI_SKIP_UPDATE=1` 跳过单次检查。配置文件位于 `~/.officecli/config.json`。
|
||||
|
||||
## 核心功能
|
||||
|
||||
### 内置引擎与生成原语
|
||||
|
||||
OfficeCLI 是自包含的。下列能力全部内置在二进制中——**无需 Office**。
|
||||
|
||||
#### 渲染引擎 —— 高保真、内置
|
||||
|
||||
OfficeCLI 的基石:一个从零实现、高保真的 HTML 渲染引擎,让 AI 智能体能"看见"渲染后的文档,而不是凭 DOM 瞎猜。它覆盖形状、图表(趋势线、误差线、瀑布、K 线、sparkline)、公式(OMML → LaTeX,KaTeX 渲染)、通过 Three.js 渲染的 3D `.glb` 模型、morph 过渡、幻灯片缩放、形状效果。按页 PNG 截图是把渲染出的 HTML 通过无头浏览器截出来的。三种模式:
|
||||
|
||||
- **`view html`** —— 独立 HTML 文件,资源内联。任何浏览器打开即可看。
|
||||
- **`view screenshot`** —— 按页 PNG,供多模态智能体读图检查。
|
||||
- **`watch`** —— 本地 HTTP 服务 + 自动刷新预览;每次 `add` / `set` / `remove` 立即更新浏览器。Excel watch 还支持单元格内联编辑、图表拖动定位。
|
||||
|
||||
```bash
|
||||
officecli view deck.pptx html -o /tmp/deck.html
|
||||
officecli view deck.pptx screenshot -o /tmp/deck.png # 多页用 --page 1-N
|
||||
officecli watch deck.pptx # http://localhost:26315
|
||||
```
|
||||
|
||||
> 没有可视化,生成 PPT 的智能体就是在盲跑——它能读 DOM,但分辨不出标题溢出、两个形状重叠。因为渲染引擎内置在二进制里,"渲染 → 看 → 改"循环在 CI、Docker、无显示器的服务器——只要二进制能跑的地方都能用。
|
||||
|
||||
#### 公式与透视引擎
|
||||
|
||||
350+ Excel 函数写入即自动求值——写 `=SUM(A1:A2)`,`get` 单元格,值已经在那。不需要回到 Office 重算。覆盖可溢出的动态数组(`FILTER` / `SORT` / `UNIQUE` / `SEQUENCE` / `LET` / `LAMBDA` / `MAP`,`_xlfn.` 自动加前缀)、`VLOOKUP` / `XLOOKUP` / `INDEX` / `MATCH`、财务与债券函数(`XIRR` / `PRICE` / `YIELD` / `DURATION` / `COUPNUM`)、统计分布/检验/回归(`NORM.DIST` / `T.TEST` / `LINEST`)、日期与文本函数等。
|
||||
|
||||
外加从源数据范围一条命令生成原生 OOXML 数据透视表——多字段行/列/筛选器、10 种聚合方式、`showDataAs` 多种模式、日期分组、计算字段、Top-N、布局选项。透视表缓存和定义都写入 OOXML,Excel 打开即看到聚合后的结果:
|
||||
|
||||
```bash
|
||||
officecli add sales.xlsx '/Sheet1' --type pivottable \
|
||||
--prop source='Data!A1:E10000' --prop rows='Region,Category' \
|
||||
--prop cols=Quarter --prop values='Revenue:sum,Units:avg' \
|
||||
--prop showDataAs=percentOfTotal
|
||||
```
|
||||
|
||||
#### 模板合并 —— 设计一次,填充 N 次
|
||||
|
||||
`merge` 把任意 `.docx` / `.xlsx` / `.pptx` 中的 `{{key}}` 占位符替换为 JSON 数据——段落、表格单元格、形状、页眉页脚、图表标题都支持。智能体一次性设计版式(昂贵),生产代码填充 N 次(廉价、确定、零 token 成本)。避免了"每份报告都从头重生成、产出 N 份版式不一致"的失败模式。
|
||||
|
||||
```bash
|
||||
officecli merge invoice-template.docx out-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
officecli merge q4-template.pptx q4-acme.pptx data.json
|
||||
```
|
||||
|
||||
#### Dump 往返 —— 从现有文档学习
|
||||
|
||||
`dump` 把任意 `.docx`、`.pptx`、`.xlsx` —— 整个文档**或任意子树**(单段、单表、单页幻灯片、单个工作表、styles、numbering、theme、settings)——序列化为可重放的 batch JSON,`batch` 重放回去。给一份用户想模仿的范本,智能体读结构化规格而不是原始 OOXML XML,修改后重放。打通"我有一份现成模板"和"给我生成 100 份变体"之间的链路。
|
||||
|
||||
```bash
|
||||
officecli dump existing.docx -o blueprint.json # 整个文档
|
||||
officecli dump existing.docx /body/tbl[1] -o table.json # 任意子树
|
||||
officecli dump existing.xlsx /Sheet1 -o sheet.json # 单个工作表
|
||||
officecli batch new.docx --input blueprint.json
|
||||
```
|
||||
|
||||
### 驻留模式与批量执行
|
||||
|
||||
驻留模式将文档保持在内存中,批量模式在一次打开/保存周期内执行多条命令。
|
||||
|
||||
```bash
|
||||
# 驻留模式 — 通过命名管道通信,延迟接近零
|
||||
officecli open report.docx
|
||||
officecli set report.docx /body/p[1]/r[1] --prop bold=true
|
||||
officecli set report.docx /body/p[2]/r[1] --prop color=FF0000
|
||||
officecli close report.docx
|
||||
|
||||
# 批量模式 — 原子化多命令执行(默认遇到第一个错误即停止)
|
||||
echo '[{"command":"set","path":"/slide[1]/shape[1]","props":{"text":"Hello"}},
|
||||
{"command":"set","path":"/slide[1]/shape[2]","props":{"fill":"FF0000"}}]' \
|
||||
| officecli batch deck.pptx --json
|
||||
|
||||
# 内联 batch,无需标准输入
|
||||
officecli batch deck.pptx --commands '[{"op":"set","path":"/slide[1]/shape[1]","props":{"text":"Hi"}}]'
|
||||
|
||||
# 使用 --force 跳过错误继续执行
|
||||
officecli batch deck.pptx --input updates.json --force --json
|
||||
```
|
||||
|
||||
> **要用其他工具读这个文件?先落盘。**
|
||||
> OfficeCLI 自己的读(`get`/`query`/`view`)永远能看到最新改动,所以在 officecli 内部无需操心保存。但常驻进程会延迟写盘,因此**在非-officecli 程序读取该文件之前**——python-docx/openpyxl、Microsoft Word、渲染器、交付/上传——先落盘:
|
||||
> ```bash
|
||||
> officecli set report.docx /body/p[1] --prop bold=true
|
||||
> officecli save report.docx # 落盘, 保留常驻进程(或 `close` = 落盘 + 释放)
|
||||
> python my_reader.py report.docx # 此时才能看到改动
|
||||
> ```
|
||||
> 常驻进程闲置约 10s 后也会自动落盘一次。完整落盘模型(auto-save / auto-close / save / close、环境变量调节):[wiki → open / close](https://github.com/iOfficeAI/OfficeCLI/wiki/command-open#when-the-file-on-disk-is-refreshed)。
|
||||
|
||||
### 三层架构
|
||||
|
||||
从简单开始,仅在需要时深入。
|
||||
|
||||
| 层 | 用途 | 命令 |
|
||||
|----|------|------|
|
||||
| **L1:读取** | 内容的语义视图 | `view`(text、annotated、outline、stats、issues、html、svg、screenshot) |
|
||||
| **L2:DOM** | 结构化元素操作 | `get`、`query`、`set`、`add`、`remove`、`move`、`swap` |
|
||||
| **L3:原始 XML** | XPath 直接访问 — 通用兜底 | `raw`、`raw-set`、`add-part`、`validate` |
|
||||
|
||||
```bash
|
||||
# L1 — 高级视图
|
||||
officecli view report.docx annotated
|
||||
officecli view budget.xlsx text --cols A,B,C --max-lines 50
|
||||
|
||||
# L2 — 元素级操作
|
||||
officecli query report.docx "run:contains(TODO)"
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q2 Report"
|
||||
officecli move report.docx /body/p[5] --to /body --index 1
|
||||
|
||||
# L3 — L2 不够时用原始 XML
|
||||
officecli raw deck.pptx '/slide[1]'
|
||||
officecli raw-set report.docx document \
|
||||
--xpath "//w:p[1]" --action append \
|
||||
--xml '<w:r><w:t>Injected text</w:t></w:r>'
|
||||
```
|
||||
|
||||
## AI 集成
|
||||
|
||||
### MCP 服务器
|
||||
|
||||
内置 [MCP](https://modelcontextprotocol.io) 服务器 — 一条命令注册:
|
||||
|
||||
```bash
|
||||
officecli mcp claude # Claude Code
|
||||
officecli mcp cursor # Cursor
|
||||
officecli mcp vscode # VS Code / Copilot
|
||||
officecli mcp lmstudio # LM Studio
|
||||
officecli mcp list # 查看注册状态
|
||||
```
|
||||
|
||||
通过 JSON-RPC 暴露所有文档操作 — 无需 shell 访问。
|
||||
|
||||
### 直接 CLI 集成
|
||||
|
||||
两步将 OfficeCLI 集成到任何 AI 智能体:
|
||||
|
||||
1. **安装二进制文件** -- 一条命令(见[安装](#安装))
|
||||
2. **完成。** OfficeCLI 自动检测您的 AI 工具(Claude Code、GitHub Copilot、Codex),通过检查已知配置目录并安装技能文件。您的智能体可以立即创建、读取和修改任何 Office 文档。
|
||||
|
||||
<details>
|
||||
<summary><strong>手动配置(可选)</strong></summary>
|
||||
|
||||
如果自动安装未覆盖您的环境,可以手动安装技能文件:
|
||||
|
||||
**直接将 SKILL.md 提供给智能体:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md
|
||||
```
|
||||
|
||||
**安装为 Claude Code 本地技能:**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://officecli.ai/SKILL.md -o ~/.claude/skills/officecli.md
|
||||
```
|
||||
|
||||
**其他智能体:** 将 `SKILL.md` 的内容添加到智能体的系统提示词或工具描述中。
|
||||
|
||||
</details>
|
||||
|
||||
### 智能体为什么在 OfficeCLI 上如鱼得水
|
||||
|
||||
- **确定性 JSON 输出** —— 每条命令都支持 `--json`,schema 一致。无需正则解析、无需抓 stdout。
|
||||
- **基于路径的寻址** —— 每个元素都有稳定路径(`/slide[1]/shape[2]`)。智能体无需理解 XML 命名空间即可导航文档。(OfficeCLI 自己的语法:1-based 索引、元素本地名——不是 XPath。)
|
||||
- **渐进式复杂度(L1 → L2 → L3)** —— 智能体从只读视图入手,升级到 DOM 操作,仅在必要时降到 raw XML。最大限度节省 token。
|
||||
- **自愈式工作流** —— `validate`、`view issues`、以及结构化错误码(`not_found`、`invalid_value`、`unsupported_property`)会返回 suggestion 和有效范围。智能体无需人工介入即可自纠错。
|
||||
- **内置 agent 友好渲染引擎** —— `view html` / `view screenshot` / `watch` 原生输出 HTML 和 PNG。无需 Office。智能体能"看见"自己的产出,并在 CI / Docker / 无头环境里修复排版问题。
|
||||
- **内置公式与透视引擎** —— 350+ Excel 函数写入即自动求值(含可溢出动态数组、财务/债券与统计函数族);从源数据范围一条命令生成原生 OOXML 数据透视表。智能体立刻读到计算值和聚合结果,不需要回到 Office 重算。
|
||||
- **模板合并** —— 智能体一次性设计版式,下游代码把 `{{key}}` 占位符填充 N 次。避免每份报告都烧 token 重生成。
|
||||
- **Dump 往返** —— `dump` 把任意 `.docx`、`.pptx`、`.xlsx` 转成可重放的 batch JSON。智能体通过读结构化规格学习人类范本,而不是从原始 OOXML XML 反推。
|
||||
- **内置帮助** —— 属性名或取值格式不确定时,智能体跑 `officecli <format> set <element>`,不靠猜。
|
||||
- **自动安装** —— OfficeCLI 自动识别您的 AI 工具(Claude Code、Cursor、VS Code…)并完成配置。无需手动放 skill 文件。
|
||||
|
||||
### 内置帮助
|
||||
|
||||
不确定属性名时,用分层帮助查询:
|
||||
|
||||
```bash
|
||||
officecli pptx set # 全部可设置元素与属性
|
||||
officecli pptx set shape # 某一类元素的详细说明
|
||||
officecli pptx set shape.fill # 单个属性格式与示例
|
||||
officecli docx query # 选择器说明:属性匹配、:contains、:has() 等
|
||||
```
|
||||
|
||||
将 `pptx` 换成 `docx` 或 `xlsx`;动词包括 `view`、`get`、`query`、`set`、`add`、`raw`。
|
||||
|
||||
运行 `officecli --help` 查看完整概览。
|
||||
|
||||
### JSON 输出格式
|
||||
|
||||
所有命令均支持 `--json`。常见响应格式:
|
||||
|
||||
**单个元素**(`get --json`):
|
||||
|
||||
```json
|
||||
{"tag": "shape", "path": "/slide[1]/shape[1]", "attributes": {"name": "TextBox 1", "text": "Hello"}}
|
||||
```
|
||||
|
||||
**元素列表**(`query --json`):
|
||||
|
||||
```json
|
||||
[
|
||||
{"tag": "paragraph", "path": "/body/p[1]", "attributes": {"style": "Heading1", "text": "Title"}},
|
||||
{"tag": "paragraph", "path": "/body/p[5]", "attributes": {"style": "Heading1", "text": "Summary"}}
|
||||
]
|
||||
```
|
||||
|
||||
**错误** 返回结构化错误对象,包含错误码、建议修正和可用值:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"error": "Slide 50 not found (total: 8)",
|
||||
"code": "not_found",
|
||||
"suggestion": "Valid Slide index range: 1-8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
错误码:`not_found`、`invalid_value`、`unsupported_property`、`invalid_path`、`unsupported_type`、`missing_property`、`file_not_found`、`file_locked`、`invalid_selector`。属性名支持自动纠错 -- 拼错属性名时会返回最接近的匹配建议。
|
||||
|
||||
**错误恢复** -- 智能体通过检查可用元素自行修正:
|
||||
|
||||
```bash
|
||||
# 智能体尝试无效路径
|
||||
officecli get report.docx /body/p[99] --json
|
||||
# 返回: {"success": false, "error": {"error": "...", "code": "not_found", "suggestion": "..."}}
|
||||
|
||||
# 智能体通过查看可用元素自行修正
|
||||
officecli get report.docx /body --depth 1 --json
|
||||
# 返回可用子元素列表,智能体选择正确路径
|
||||
```
|
||||
|
||||
**变更确认**(`set`、`add`、`remove`、`move`、`create` 使用 `--json`):
|
||||
|
||||
```json
|
||||
{"success": true, "path": "/slide[1]/shape[1]"}
|
||||
```
|
||||
|
||||
运行 `officecli --help` 查看退出码和错误格式的完整说明。
|
||||
|
||||
## 对比
|
||||
|
||||
| | OfficeCLI | Microsoft Office | LibreOffice | python-docx / openpyxl |
|
||||
|---|---|---|---|---|
|
||||
| 开源免费 | ✓ (Apache 2.0) | ✗(付费授权) | ✓ | ✓ |
|
||||
| AI 原生 CLI + JSON | ✓ | ✗ | ✗ | ✗ |
|
||||
| 零安装(单一可执行文件) | ✓ | ✗ | ✗ | ✗(需 Python + pip) |
|
||||
| 任意语言调用 | ✓ (CLI) | ✗ (COM/Add-in) | ✗ (UNO API) | 仅 Python |
|
||||
| 基于路径的元素访问 | ✓ | ✗ | ✗ | ✗ |
|
||||
| 原始 XML 兜底 | ✓ | ✗ | ✗ | 部分支持 |
|
||||
| 内置 agent 友好渲染引擎 | ✓ | ✗ | ✗ | ✗ |
|
||||
| 无头 HTML/PNG 输出 | ✓ | ✗ | 部分支持 | ✗ |
|
||||
| 跨格式模板合并(`{{key}}`)| ✓ | ✗ | ✗ | ✗ |
|
||||
| Dump → batch JSON 往返 | ✓ | ✗ | ✗ | ✗ |
|
||||
| 实时预览(编辑后自动刷新) | ✓ | ✗ | ✗ | ✗ |
|
||||
| 无头 / CI 环境 | ✓ | ✗ | 部分支持 | ✓ |
|
||||
| 跨平台 | ✓ | Windows/Mac | ✓ | ✓ |
|
||||
| Word + Excel + PowerPoint | ✓ | ✓ | ✓ | 需要多个库 |
|
||||
|
||||
## 命令参考
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| [`create`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-create) | 创建空白 .docx、.xlsx 或 .pptx(根据扩展名判断类型) |
|
||||
| [`view`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-view) | 查看内容(模式:`outline`、`text`、`annotated`、`stats`、`issues`、`html`) |
|
||||
| [`get`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-get) | 获取元素及子元素(`--depth N`、`--json`) |
|
||||
| [`query`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-query) | CSS 风格查询(`[attr=value]`、`:contains()`、`:has()` 等) |
|
||||
| [`set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-set) | 修改元素属性 |
|
||||
| [`add`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-add) | 添加元素(或通过 `--from <path>` 克隆) |
|
||||
| [`remove`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-remove) | 删除元素 |
|
||||
| [`move`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-move) | 移动元素(`--to <parent>`、`--index N`、`--after <path>`、`--before <path>`) |
|
||||
| [`swap`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-swap) | 交换两个元素 |
|
||||
| [`validate`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-validate) | OpenXML 模式校验 |
|
||||
| [`batch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-batch) | 单次打开/保存周期内执行多条操作(stdin、`--input` 或 `--commands`;默认遇到第一个错误停止,`--force` 跳过错误继续) |
|
||||
| [`merge`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-merge) | 模板合并 — 用 JSON 数据替换 `{{key}}` 占位符 |
|
||||
| [`watch`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-watch) | 在浏览器中实时 HTML 预览,自动刷新 |
|
||||
| [`mcp`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-mcp) | 启动 MCP 服务器,用于 AI 工具集成 |
|
||||
| [`raw`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | 查看文档部件的原始 XML |
|
||||
| [`raw-set`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-raw) | 通过 XPath 修改原始 XML |
|
||||
| `add-part` | 添加新的文档部件(页眉、图表等) |
|
||||
| [`open`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-open) | 启动驻留模式(文档保持在内存中) |
|
||||
| `close` | 保存并关闭驻留模式 |
|
||||
| [`install`](https://github.com/iOfficeAI/OfficeCLI/wiki/command-install) | 安装二进制文件 + 技能文件 + MCP(`all`、`claude`、`cursor` 等) |
|
||||
| `config` | 获取或设置配置 |
|
||||
| `<format> <command>` | [内置帮助](https://github.com/iOfficeAI/OfficeCLI/wiki/command-reference)(如 `officecli pptx set shape`) |
|
||||
|
||||
## 端到端工作流示例
|
||||
|
||||
典型的智能体自愈式工作流:创建演示文稿、填充内容、验证并修复问题 -- 全程无需人工干预。
|
||||
|
||||
```bash
|
||||
# 1. 创建
|
||||
officecli create report.pptx
|
||||
|
||||
# 2. 添加内容
|
||||
officecli add report.pptx / --type slide --prop title="Q4 Results"
|
||||
officecli add report.pptx '/slide[1]' --type shape \
|
||||
--prop text="Revenue: $4.2M" --prop x=2cm --prop y=5cm --prop size=28
|
||||
officecli add report.pptx / --type slide --prop title="Details"
|
||||
officecli add report.pptx '/slide[2]' --type shape \
|
||||
--prop text="Growth driven by new markets" --prop x=2cm --prop y=5cm
|
||||
|
||||
# 3. 验证
|
||||
officecli view report.pptx outline
|
||||
officecli validate report.pptx
|
||||
|
||||
# 4. 修复发现的问题
|
||||
officecli view report.pptx issues --json
|
||||
# 根据输出修复问题,例如:
|
||||
officecli set report.pptx '/slide[1]/shape[1]' --prop font=Arial
|
||||
```
|
||||
|
||||
### 单位与颜色
|
||||
|
||||
所有尺寸和颜色属性均接受灵活的输入格式:
|
||||
|
||||
| 类型 | 支持的格式 | 示例 |
|
||||
|------|-----------|------|
|
||||
| **尺寸** | cm、in、pt、px 或原始 EMU | `2cm`、`1in`、`72pt`、`96px`、`914400` |
|
||||
| **颜色** | 十六进制、命名色、RGB、主题色 | `#FF0000`、`FF0000`、`red`、`rgb(255,0,0)`、`accent1` |
|
||||
| **字号** | 纯数字或带 pt 后缀 | `14`、`14pt`、`10.5pt` |
|
||||
| **间距** | pt、cm、in 或倍数 | `12pt`、`0.5cm`、`1.5x`、`150%` |
|
||||
|
||||
## 常用模式
|
||||
|
||||
```bash
|
||||
# 替换 Word 文档中所有 Heading1 文本
|
||||
officecli query report.docx "paragraph[style=Heading1]" --json | ...
|
||||
officecli set report.docx /body/p[1]/r[1] --prop text="New Title"
|
||||
|
||||
# 将所有幻灯片内容导出为 JSON
|
||||
officecli get deck.pptx / --depth 2 --json
|
||||
|
||||
# 批量更新 Excel 单元格
|
||||
officecli batch budget.xlsx --input updates.json --json
|
||||
|
||||
# 导入 CSV 数据到 Excel 工作表
|
||||
officecli add budget.xlsx / --type sheet --prop name="Q1 Data" --prop csv=sales.csv
|
||||
|
||||
# 模板合并批量生成报告
|
||||
officecli merge invoice-template.docx invoice-001.docx '{"client":"Acme","total":"$5,200"}'
|
||||
|
||||
# 交付前检查文档质量
|
||||
officecli validate report.docx && officecli view report.docx issues --json
|
||||
```
|
||||
|
||||
**Python 调用** —— 包装一次,每次调用都返回解析好的 JSON:
|
||||
|
||||
```python
|
||||
import json, subprocess
|
||||
|
||||
def cli(*args):
|
||||
return json.loads(subprocess.check_output(["officecli", *args, "--json"], text=True))
|
||||
|
||||
cli("create", "deck.pptx")
|
||||
cli("add", "deck.pptx", "/", "--type", "slide", "--prop", "title=Q4 报告")
|
||||
slide = cli("get", "deck.pptx", "/slide[1]")
|
||||
print(slide["attributes"]["text"])
|
||||
```
|
||||
|
||||
## 文档
|
||||
|
||||
[Wiki](https://github.com/iOfficeAI/OfficeCLI/wiki) 提供了每个命令、元素类型和属性的详细指南:
|
||||
|
||||
- **按格式查看:**[Word](https://github.com/iOfficeAI/OfficeCLI/wiki/word-reference) | [Excel](https://github.com/iOfficeAI/OfficeCLI/wiki/excel-reference) | [PowerPoint](https://github.com/iOfficeAI/OfficeCLI/wiki/powerpoint-reference)
|
||||
- **工作流:**[端到端示例](https://github.com/iOfficeAI/OfficeCLI/wiki/workflows) -- Word 报告、Excel 数据表、PPT 演示、批量修改、驻留模式
|
||||
- **故障排除:**[常见错误与解决方案](https://github.com/iOfficeAI/OfficeCLI/wiki/troubleshooting)
|
||||
- **AI 智能体指南:**[Wiki 导航决策树](https://github.com/iOfficeAI/OfficeCLI/wiki/agent-guide)
|
||||
|
||||
## 从源码构建
|
||||
|
||||
编译需要 [.NET 10 SDK](https://dotnet.microsoft.com/download)。输出为自包含的原生二进制文件 -- .NET 已内嵌,运行时无需安装。
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
## 许可证
|
||||
|
||||
[Apache License 2.0](LICENSE)
|
||||
|
||||
欢迎通过 [GitHub Issues](https://github.com/iOfficeAI/OfficeCLI/issues) 提交 Bug 报告和贡献代码。
|
||||
|
||||
---
|
||||
|
||||
如果觉得 OfficeCLI 好用,请在 [GitHub 上点个 Star](https://github.com/iOfficeAI/OfficeCLI) — 帮助更多人发现这个项目。
|
||||
|
||||
[OfficeCLI.AI](https://OfficeCLI.AI) | [GitHub](https://github.com/iOfficeAI/OfficeCLI)
|
||||
|
||||
<!--
|
||||
yaml-frontmatter:
|
||||
name: officecli
|
||||
version: see GitHub Releases
|
||||
description: The standard CLI for AI agents to create, read, and modify Office documents
|
||||
formats: [docx, xlsx, pptx]
|
||||
platforms: [macos, linux, windows]
|
||||
license: Apache-2.0
|
||||
keywords: [office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint]
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
|
||||
<!-- LLM/agent discovery metadata
|
||||
tool: officecli
|
||||
type: cli
|
||||
canonical: the standard CLI for AI-driven Office document manipulation
|
||||
formats: docx, xlsx, pptx
|
||||
capabilities: create, read, modify, validate, batch, resident-mode, mcp-server, live-preview, auto-install
|
||||
output: json, text, annotated
|
||||
platforms: macos, linux, windows
|
||||
license: Apache-2.0
|
||||
keywords: office, cli, ai-agent, automation, docx, xlsx, pptx, openxml, document-generation, mcp-server, word, excel, powerpoint, ai-tools, command-line, structured-output
|
||||
ai-agent-compatible: true
|
||||
mcp-server: true
|
||||
skill-file: SKILL.md
|
||||
alternatives: python-docx, openpyxl, python-pptx, libreoffice --headless
|
||||
install-command-unix: curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
|
||||
install-command-windows: irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
|
||||
-->
|
||||
@@ -0,0 +1,25 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
OfficeCLI reads and writes `.docx`, `.xlsx`, and `.pptx` files, which may come
|
||||
from untrusted sources. If you discover a security vulnerability, please report
|
||||
it privately — **do not open a public issue**.
|
||||
|
||||
Preferred channel: use GitHub's private vulnerability reporting on this
|
||||
repository (the **Security → Report a vulnerability** tab). This keeps the
|
||||
report confidential until a fix is available.
|
||||
|
||||
Please include:
|
||||
|
||||
- A description of the issue and its impact
|
||||
- Steps to reproduce (a minimal sample file is ideal)
|
||||
- The OfficeCLI version (`officecli --version`) and your OS
|
||||
|
||||
We aim to acknowledge reports within a reasonable timeframe and will coordinate
|
||||
a fix and disclosure with you.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security fixes are applied to the latest released version. Please upgrade to the
|
||||
latest version before reporting.
|
||||
@@ -0,0 +1,417 @@
|
||||
---
|
||||
name: officecli
|
||||
description: Create, analyze, proofread, and modify Office documents (.docx, .xlsx, .pptx) using the officecli CLI tool. Use when the user wants to create, inspect, check formatting, find issues, add charts, or modify Office documents.
|
||||
---
|
||||
|
||||
# officecli
|
||||
|
||||
AI-friendly CLI for .docx, .xlsx, .pptx. Single binary, no dependencies, no Office installation needed.
|
||||
|
||||
## Install
|
||||
|
||||
If `officecli` is not installed:
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
curl -fsSL https://d.officecli.ai/install.sh | bash
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://d.officecli.ai/install.ps1 | iex
|
||||
```
|
||||
|
||||
Verify with `officecli --version`. If still not found after install, open a new terminal.
|
||||
|
||||
---
|
||||
|
||||
## Strategy
|
||||
|
||||
**L1 (read) → L2 (DOM edit) → L3 (raw XML)**. Always prefer higher layers. Add `--json` for structured output.
|
||||
|
||||
**Before doc work, check Specialized Skills** (bottom of this file). Fundraising decks, academic papers, financial models, dashboards, and Morph animations need their own skill loaded first — `load_skill` once, then proceed.
|
||||
|
||||
---
|
||||
|
||||
## Help System (IMPORTANT)
|
||||
|
||||
**When unsure about property names, value formats, or command syntax, ALWAYS run help instead of guessing.** One help query beats guess-fail-retry loops.
|
||||
|
||||
`officecli help` ≡ `officecli --help`, and `officecli <cmd> --help` ≡ `officecli help <cmd>` — same content.
|
||||
|
||||
```bash
|
||||
officecli help # All commands + global options + schema entry points
|
||||
officecli help docx # List all docx elements
|
||||
officecli help docx paragraph # Full schema: properties, aliases, examples, readbacks
|
||||
officecli help docx set paragraph # Verb-filtered: only props usable with `set`
|
||||
officecli help docx paragraph --json # Structured schema (machine-readable)
|
||||
```
|
||||
|
||||
Format aliases: `word`→`docx`, `excel`→`xlsx`, `ppt`/`powerpoint`→`pptx`. Verbs: `add`, `set`, `get`, `query`, `remove`. MCP exposes the same schema via the single `command` string param: `{"command":"help docx paragraph"}` (not a structured `{"format":...,"type":...}` object — the MCP tool has exactly one param, `command`, and passes it through to the CLI verbatim).
|
||||
|
||||
---
|
||||
|
||||
## Performance: Resident Mode
|
||||
|
||||
**Every command auto-starts a resident on first access** (60s idle timeout) — file-lock conflicts are automatically avoided. Explicit `open`/`close` is still recommended for longer sessions (12min idle):
|
||||
```bash
|
||||
officecli open report.docx # explicitly keep in memory
|
||||
officecli set report.docx ... # no file I/O overhead
|
||||
officecli close report.docx # save and release
|
||||
```
|
||||
|
||||
Opt out of auto-start: `OFFICECLI_NO_AUTO_RESIDENT=1`.
|
||||
|
||||
**Flush only at the non-officecli boundary.** officecli's own reads (`get`/`query`/`view`/`dump`) always see your latest edits, so you never need to save mid-workflow. Run `save` (keeps the resident) or `close` (flush + release) only **before a non-officecli program reads the file** — python-docx/openpyxl, Word, a renderer, delivery/upload. (Idle sessions auto-flush within seconds; `OFFICECLI_RESIDENT_FLUSH=each` makes every mutation flush before returning.)
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
**PPT:**
|
||||
```bash
|
||||
officecli create slides.pptx
|
||||
officecli add slides.pptx / --type slide --prop title="Q4 Report" --prop background=1A1A2E
|
||||
officecli add slides.pptx '/slide[1]' --type shape --prop text="Revenue grew 25%" --prop x=2cm --prop y=5cm --prop font=Arial --prop size=24 --prop color=FFFFFF
|
||||
```
|
||||
|
||||
**Word:**
|
||||
```bash
|
||||
officecli create report.docx
|
||||
officecli add report.docx /body --type paragraph --prop text="Executive Summary" --prop style=Heading1
|
||||
officecli add report.docx /body --type paragraph --prop text="Revenue increased by 25% year-over-year."
|
||||
```
|
||||
|
||||
**Excel:**
|
||||
```bash
|
||||
officecli create data.xlsx
|
||||
officecli set data.xlsx /Sheet1/A1 --prop value="Name" --prop bold=true
|
||||
officecli set data.xlsx /Sheet1/A2 --prop value="Alice"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## L1: Create, Read & Inspect
|
||||
|
||||
```bash
|
||||
officecli create <file> # Create blank .docx/.xlsx/.pptx (type from extension)
|
||||
officecli view <file> <mode> # outline | stats | issues | text | annotated | html
|
||||
officecli get <file> <path> --depth N # Get a node and its children [--json]
|
||||
officecli query <file> <selector> # CSS-like query
|
||||
officecli validate <file> # Validate against OpenXML schema
|
||||
```
|
||||
|
||||
### view modes
|
||||
|
||||
| Mode | Description | Useful flags |
|
||||
|------|-------------|-------------|
|
||||
| `outline` | Document structure | |
|
||||
| `stats` | Statistics (pages, words, shapes) | |
|
||||
| `issues` | Formatting/content/structure problems | `--type format\|content\|structure`, `--limit N` |
|
||||
| `text` | Plain text extraction | `--start N --end N`, `--max-lines N` |
|
||||
| `annotated` | Text with formatting annotations | |
|
||||
| `html` | Static HTML snapshot — same renderer as `watch`, no server needed | `--browser`, `--page N` (docx), `--start N --end N` (pptx) |
|
||||
| `screenshot` / `svg` / `pdf` / `forms` | PNG via headless browser / SVG (pptx slide) / PDF via exporter plugin / form-fields JSON via format-handler plugin | `-o`, `--screenshot-width/-height`, pptx `--grid N` |
|
||||
|
||||
Use `view html` for one-shot snapshots (CI artifacts, archival, diffing); use `watch` when you need live refresh or browser-side click-to-select.
|
||||
|
||||
### get
|
||||
|
||||
Any XML path via element localName. Use `--depth N` to expand children. Add `--json` for structured output. Default text output is grep-friendly: `path (type) "text" key=val key=val ...`
|
||||
|
||||
```bash
|
||||
officecli get report.docx '/body/p[3]' --depth 2 --json
|
||||
officecli get slides.pptx '/slide[1]' --depth 1 # list all shapes on slide 1
|
||||
officecli get data.xlsx '/Sheet1/B2' --json
|
||||
```
|
||||
|
||||
### Stable ID Addressing
|
||||
|
||||
Elements with stable IDs return `@attr=value` paths instead of positional indices. Prefer these in multi-step workflows — positional indices shift on insert/delete, stable IDs do not.
|
||||
|
||||
```
|
||||
/slide[1]/shape[@id=550950021] # PPT shape
|
||||
/slide[1]/table[@id=1388430425]/tr[1]/tc[2] # PPT table
|
||||
/body/p[@paraId=1A2B3C4D] # Word paragraph
|
||||
/comments/comment[@commentId=1] # Word comment
|
||||
```
|
||||
|
||||
PPT also accepts `@name=` (e.g. `shape[@name=Title 1]`), with morph `!!` prefix awareness. Elements without stable IDs (slide, run, tr/tc, row) fall back to positional indices.
|
||||
|
||||
### query
|
||||
|
||||
CSS-like selectors: `[attr=value]`, `[attr!=value]`, `[attr~=text]`, `[attr>=value]`, `[attr<=value]`, `:contains("text")`, `:empty`, `:has(formula)`, `:no-alt`. Boolean `and`/`or` supported across `query`/`set`/`remove`: `cell[value>5000 or value<100]`, `cell[(type=Number or type=Date) and value>0]`. Excel row-by-column-name: `Sheet1!row[Salary>5000]`. `set` accepts selectors and Excel-native paths (parity with `get`/`query`). Bare unscoped selectors rejected on `set`/`remove`.
|
||||
|
||||
```bash
|
||||
officecli query report.docx 'paragraph[style=Normal] > run[font!=Arial]'
|
||||
officecli query slides.pptx 'shape[fill=FF0000]'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Watch & Interactive Selection
|
||||
|
||||
Live HTML preview that auto-refreshes on every file change. Browsers can click / shift-click / box-drag to select shapes; the CLI can read the current browser selection and act on it.
|
||||
|
||||
```bash
|
||||
officecli watch <file> [--port N] # Start preview server (default port 26315)
|
||||
officecli unwatch <file> # Stop
|
||||
officecli goto <file> <path> # Scroll watching browser(s) to element (docx: p / table / tr / tc)
|
||||
```
|
||||
|
||||
Open the printed `http://localhost:N` URL. Click to select; shift/cmd/ctrl+click to multi-select; drag from empty space to box-select. PPT/Word use blue outline; Excel uses native-style green selection (double-click cell to edit inline; drag a chart to reposition).
|
||||
|
||||
### `get <file> selected` — read what the user clicked
|
||||
|
||||
```bash
|
||||
officecli get <file> selected [--json]
|
||||
```
|
||||
|
||||
Returns DocumentNodes for whatever is currently selected. Empty result if nothing selected. Exit code != 0 if no watch is running.
|
||||
|
||||
```bash
|
||||
# User clicks shapes in the browser, then asks "make these red"
|
||||
PATHS=$(officecli get deck.pptx selected --json | jq -r '.data.Results[].path')
|
||||
for p in $PATHS; do officecli set deck.pptx "$p" --prop fill=FF0000; done
|
||||
```
|
||||
|
||||
### Key properties
|
||||
|
||||
- **Selection survives file edits.** Paths use stable `@id=` form.
|
||||
- **All connected browsers share one selection.** Last-write-wins.
|
||||
- **Same-file single-watch.** A given file can have only one watch process at a time.
|
||||
- **Group shapes select as a whole.** Drilling into individual children of a group is not supported in v1.
|
||||
- **Coverage:** `.pptx` shapes/pictures/tables/charts/connectors/groups; `.docx` top-level paragraphs and tables. Inherited layout/master decorations and Word nested elements (table cells, run-level) are not addressable. **`.xlsx` does not emit `data-path`** — `mark`/`selection` on xlsx always resolve `stale=true` (v2 candidate).
|
||||
|
||||
### Marks — edit proposals waiting for review
|
||||
|
||||
Use `mark` when changes need human review BEFORE they hit the file. Marks live in the watch process only; a separate `set` pipeline applies accepted ones. For one-shot changes use `set` directly; for permanent file annotations use `add --type comment` (Word native).
|
||||
|
||||
```bash
|
||||
officecli mark <file> <path> [--prop find=... color=... note=... tofix=... regex=true] [--json]
|
||||
officecli unmark <file> [--path <p> | --all] [--json]
|
||||
officecli get-marks <file> [--json]
|
||||
```
|
||||
|
||||
Props: `find` (literal or regex when `regex=true`; raw form `find='r"[abc]"'`), `color` (hex / `rgb(...)` / 22 named whitelist), `note`, `tofix` (drives apply pipeline). **Path** must be `data-path` format from watch HTML — see subskills for full pipeline.
|
||||
|
||||
---
|
||||
|
||||
## L2: DOM Operations
|
||||
|
||||
### set — modify properties
|
||||
|
||||
```bash
|
||||
officecli set <file> <path> --prop key=value [--prop ...]
|
||||
```
|
||||
|
||||
**Any XML attribute is settable** via element path (found via `get --depth N`) — even attributes not currently present. Without `find=`, `set` applies format to the entire element.
|
||||
|
||||
**Value formats:**
|
||||
|
||||
| Type | Format | Examples |
|
||||
|------|--------|---------|
|
||||
| Colors | Hex (with/without `#`), named, RGB, theme | `FF0000`, `#FF0000`, `red`, `rgb(255,0,0)`, `accent1`..`accent6` |
|
||||
| Spacing | Unit-qualified | `12pt`, `0.5cm`, `1.5x`, `150%` |
|
||||
| Dimensions | EMU or suffixed | `914400`, `2.54cm`, `1in`, `72pt`, `96px` |
|
||||
|
||||
**Dotted-attr aliases** — `font.<attr>` forms accepted on shape/run/paragraph/table/row/cell/section/styles, e.g. `--prop font.color=red --prop font.bold=true --prop font.size=14pt`. Run `officecli help <fmt> <element>` for the full list.
|
||||
|
||||
### find — format or replace matched text
|
||||
|
||||
Use top-level `--find` / `--replace` on `set` (and `--find` on `query`). Legacy `--prop find=X` still works but emits a hint.
|
||||
|
||||
```bash
|
||||
# Format matched text (auto-splits runs)
|
||||
officecli set doc.docx '/body/p[1]' --find weather --prop bold=true --prop color=red
|
||||
|
||||
# Regex matching (regex= still a prop flag)
|
||||
officecli set doc.docx '/body/p[1]' --find '\d+%' --prop regex=true --prop color=red
|
||||
|
||||
# Replace text (use `/` for whole-document scope)
|
||||
officecli set doc.docx / --find draft --replace final
|
||||
|
||||
# docx: tracked Find&Replace
|
||||
officecli set doc.docx / --find draft --replace final --prop revision.author=Alice
|
||||
|
||||
# PPT — same syntax, different paths
|
||||
officecli set slides.pptx / --find draft --replace final
|
||||
```
|
||||
|
||||
**Path controls search scope:** `/` = whole document, `/body/p[1]` or `/slide[N]/shape[M]` = specific element, `/header[1]` / `/footer[1]` = headers/footers.
|
||||
|
||||
**Notes:**
|
||||
- Case-sensitive by default. Case-insensitive: `--prop 'find=(?i)error' --prop regex=true`
|
||||
- Matches work across run boundaries
|
||||
- No match = silent success. `--json` includes `"matched": N`
|
||||
- **Excel:** only `find` + `replace` supported (no find + format props)
|
||||
|
||||
### add — add elements or clone
|
||||
|
||||
```bash
|
||||
officecli add <file> <parent> --type <type> [--prop ...]
|
||||
officecli add <file> <parent> --type <type> --after <path> [--prop ...] # insert after anchor
|
||||
officecli add <file> <parent> --type <type> --before <path> [--prop ...] # insert before anchor
|
||||
officecli add <file> <parent> --type <type> --index N [--prop ...] # 0-based position (legacy)
|
||||
officecli add <file> <parent> --from <path> # clone existing element
|
||||
```
|
||||
|
||||
`--after`, `--before`, `--index` are mutually exclusive. No position flag = append to end.
|
||||
|
||||
**Element types (with aliases):**
|
||||
|
||||
| Format | Types |
|
||||
|--------|-------|
|
||||
| **pptx** | slide (incl. hidden), shape (font.latin/ea/cs, direction=rtl, underline.color, highlight=COLOR (Add/Set/Get/HTML preview), effective.X+effective.X.src; arrow alias for rightArrow; slideMaster/slideLayout typed add/set/remove), picture (SVG, brightness/contrast/glow/shadow, rotation, link, tooltip), chart (direction=rtl, pieOfPie, barOfPie, axisLine/gridline per-attr setters, animation+chartBuild=byCategory|bySeries, line dropLines/hiLowLines/upDownBars, anchor=x,y,w,h shorthand), table (cell direction=rtl, fill/background, built-in PowerPoint style catalogue, /col[C] get + swap/copyFrom, row/col Move/CopyFrom), row (tr), connector (from/to accept full-path `@name=`/`@id=` forms — bare `@name=Foo` is rejected, must be `/slide[N]/shape[@name=Foo]` — startshape/endshape SetByPath; edge-to-edge anchoring by default, fromSide/toSide to force an edge, fromIdx/toIdx for raw cxn index), group (link, tooltip, deep walk by get/query/add/remove, ungroup=true dissolves back to slide-absolute), align/distribute (targets= accepts shape[@id=N] paths, not just positional), video/audio (loop, autoStart alias), equation, notes (direction=rtl, lang), comment (legacy + modern p188 threaded round-trip), animation (15 emphasis + 16 exit presets, multi-effect chains, motion-path presets, repeat/restart/autoReverse, chart animations), transition (12 p15 presets + morph/p14), paragraph (para), run, zoom, ole (preview=, full dump round-trip via add-part+raw-set), placeholder (phType=...), model3d (rotation=ax,ay,az; full dump round-trip), smartart (dump round-trip via add-part), diagram (add-only mermaid → native shapes or rendered image, `--type diagram`/`flowchart`). |
|
||||
| **docx** | paragraph (direction/font.latin/ea/cs, bold.cs/italic.cs/size.cs, lang.latin/ea/cs, wordWrap, framePr.\*, tabs shorthand), run (lang slots, direction, underline.color, position half-pts, **revision.type=ins\|del\|format\|moveFrom\|moveTo + revision.action=accept\|reject** with .author/.date — bare `@author=`/`@type=` selector on `set /revision[...]` for filtered accept/reject, but `query 'revision[...]'` needs the dotted `revision.author=`/`revision.type=` form; move+revision is run-level paths only, not paragraph-level; **range=START:END** on a paragraph/shape path formats a char span by explicit 0-based half-open offset instead of addressing a run — the offset sibling of find=), table (direction=rtl, hMerge, cantSplit on row/nowrap on cell (both add+set), **virtual column ops**: add/remove/move/copyfrom on /body/tbl[N]/col), row (tr), cell (td), image, header/footer (direction), section (pageNumFmt full enum, direction=rtl, rtlGutter, pgBorders=box), bookmark, comment, footnote, endnote, formfield, sdt, chart, equation, field (28 types), hyperlink, style (direction, indents, pbdr, lineSpacing on Add/Set), toc, watermark, break, ole, **num/abstractNum/lvl**, **tab**, **textbox/shape** (add-mostly — Get returns raw XML preview only, no structured readback; Set is limited to width/height/geometry/fill/line.\*; position is `anchor.x`/`anchor.y` not bare x/y; **textbox-only** `textDirection`/rotation/gradient/shadow — docx shape itself has neither rotation nor gradient), embedded **OLE round-trip on dump→batch**, **diagram** (add-only mermaid → native shapes or rendered image, `--type diagram`/`flowchart`, no x/y at add-time — reposition via `set /body/group[N]`). docDefaults.rtl, autoHyphenation, `get /` exposes locale + /comments /footnotes /endnotes. `create --minimal` for raw OOXML scaffolding. |
|
||||
| **xlsx** | sheet (visible/hidden/veryHidden, print margins, printTitleRows/Cols, rightToLeft sheetView, cascade-aware rename), row (c{N}= cell-content shorthand; add accepts --from /Sheet/col[L]; formula-ref rewrite on insert), col (formula-ref rewrite, named-range follow on move), cell (type=richtext+runs, merge=range/sweep, direction=rtl, phonetic; **--shift left\|up on remove, shift=right\|down on add** — Excel UI dialog parity; formula auto-detect; OFFSET/INDIRECT in calc), chart (per-axis RTL/title, anchor=x,y,w,h, pareto), image (SVG), comment (direction=rtl), table (listobject), namedrange (definedname, volatile, `[@name=X]`; formula-body inlined at parse), pivottable (cache CoW + cross-pivot sharing, labelFilter=field:type:value add-time-only, topN=integer add-time-only, fillDownLabels is an alias of repeatLabels not a separate feature, calculatedField), sparkline, validation, autofilter, shape, textbox, CF (databar/colorscale/iconset/formulacf/cellIs/topN/aboveAverage), ole, csv. Query supports `merge`/`mergedrange`. Workbook: password. Shape selector enumerates leaves inside grpSp. |
|
||||
|
||||
### Pivot tables (xlsx)
|
||||
|
||||
```bash
|
||||
officecli add data.xlsx /Sheet1 --type pivottable \
|
||||
--prop source="Sheet1!A1:E100" --prop rows=Region,Category \
|
||||
--prop cols=Year --prop values="Sales:sum,Qty:count" \
|
||||
--prop grandTotals=rows --prop subtotals=off --prop sort=asc
|
||||
```
|
||||
|
||||
Key props: `rows`, `cols`, `values` (Field:func[:showDataAs]), `filters`, `source`, `position`, `layout` (compact/outline/tabular), `repeatLabels`, `blankRows`, `aggregate`, `showDataAs` (percent_of_total/row/col, running_total), `grandTotals`, `subtotals`, `sort`. Aggregators: sum, count, average, max, min, product, stdDev, stdDevp, var, varp, countNums. Date columns auto-group. Run `officecli help xlsx pivottable` for full schema.
|
||||
|
||||
### Document-level properties (all formats)
|
||||
|
||||
```bash
|
||||
officecli set doc.docx / --prop docDefaults.font=Arial --prop docDefaults.fontSize=11pt
|
||||
officecli set doc.docx / --prop protection=forms --prop evenAndOddHeaders=true
|
||||
officecli set data.xlsx / --prop calc.mode=manual --prop calc.refMode=r1c1
|
||||
officecli set slides.pptx / --prop defaultFont=Arial --prop show.loop=true --prop print.what=handouts
|
||||
```
|
||||
|
||||
Run `officecli help <format> /` for all document-level properties (docDefaults, docGrid, CJK spacing, calc, print, show, theme, extended).
|
||||
|
||||
### Sort (xlsx)
|
||||
|
||||
```bash
|
||||
officecli set data.xlsx /Sheet1 --prop sort="C desc" --prop sortHeader=true
|
||||
officecli set data.xlsx '/Sheet1/A1:D100' --prop sort="A asc" --prop sortHeader=true
|
||||
```
|
||||
|
||||
Format: `COL DIR[, COL DIR ...]`. Rejects ranges with merged cells or formulas. Sidecar metadata (hyperlinks, comments, conditional formatting, drawings) follows rows automatically.
|
||||
|
||||
### Text-anchored insert (`--after find:X` / `--before find:X`)
|
||||
|
||||
Locate an insertion point by text match within a paragraph. Inline types (run, picture, hyperlink) insert within the paragraph; block types (table, paragraph) auto-split it. PPT only supports inline.
|
||||
|
||||
```bash
|
||||
# Word: inline run after matched text
|
||||
officecli add doc.docx '/body/p[1]' --type run --after find:weather --prop text=" (sunny)"
|
||||
|
||||
# Word: block table after matched text (auto-splits paragraph)
|
||||
officecli add doc.docx '/body/p[1]' --type table --after "find:First sentence." --prop rows=2 --prop cols=2
|
||||
```
|
||||
|
||||
### Clone
|
||||
|
||||
`officecli add <file> / --from '/slide[1]'` — copies with all cross-part relationships.
|
||||
|
||||
### move, swap, remove
|
||||
|
||||
```bash
|
||||
officecli move <file> <path> [--to <parent>] [--index N] [--after <path>] [--before <path>]
|
||||
officecli swap <file> <path1> <path2>
|
||||
officecli remove <file> '/body/p[4]'
|
||||
```
|
||||
|
||||
When using `--after` or `--before`, `--to` can be omitted — the target container is inferred from the anchor.
|
||||
|
||||
### batch — multiple operations in one save cycle
|
||||
|
||||
Continues on error by default (returns exit 1 if any item fails). Use `--stop-on-error` to abort on the first failure. `--force` is the docx-protection bypass.
|
||||
|
||||
`officecli dump <file> [<path>]` emits a replayable batch JSON for round-trip — `.docx` (full coverage), `.pptx` (text/tables/pictures/charts/notes/theme + OLE/3D/video/audio/SmartArt/morph/p15 transitions via raw-set passthrough), and `.xlsx` (cells/formulas/styles + tables, conditional formatting, validations, comments, charts, sparklines, pictures, shapes, pivot tables; slicers/chartEx/OLE via verbatim carrier). Path defaults to `/` (whole document); pass a subtree path (docx: `/body`, `/body/p[N]`, `/body/tbl[N]`, `/theme`, `/settings`, `/numbering`, `/styles`; xlsx: `/SheetName`, `/sheet[N]`) to scope the dump. `officecli refresh <file.docx>` recalculates TOC page numbers / PAGE / cross-references after replay (Word backend on Windows; headless-HTML fallback elsewhere). `officecli plugins list` extends support to `.doc`, `.hwpx`, `.pdf` export.
|
||||
|
||||
```bash
|
||||
echo '[
|
||||
{"command":"set","path":"/Sheet1/A1","props":{"value":"Name","bold":"true"}},
|
||||
{"command":"set","path":"/Sheet1/B1","props":{"value":"Score","bold":"true"}}
|
||||
]' | officecli batch data.xlsx --json
|
||||
|
||||
officecli batch data.xlsx --commands '[{"op":"set","path":"/Sheet1/A1","props":{"value":"Done"}}]' --json
|
||||
officecli batch data.xlsx --input updates.json --force --json
|
||||
```
|
||||
|
||||
Supports: `add`, `set`, `get`, `query`, `remove`, `move`, `swap`, `view`, `raw`, `raw-set`, `validate`. Fields: `command` (or `op`), `path`, `parent`, `type`, `from`, `to`, `index`, `after`, `before`, `props`, `selector`, `mode`, `depth`, `part`, `xpath`, `action`, `xml`.
|
||||
|
||||
---
|
||||
|
||||
## L3: Raw XML
|
||||
|
||||
Use when L2 cannot express what you need. No xmlns declarations needed — prefixes auto-registered.
|
||||
|
||||
```bash
|
||||
officecli raw <file> <part> # view raw XML
|
||||
officecli raw-set <file> <part> --xpath "..." --action replace --xml '<w:p>...</w:p>'
|
||||
officecli add-part <file> <parent> # create new document part (returns rId)
|
||||
```
|
||||
|
||||
`raw-set` actions: `append`, `prepend`, `insertbefore`, `insertafter`, `replace`, `remove`, `setattr`. Run `officecli help <format> raw` for available parts.
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
| Pitfall | Correct Approach |
|
||||
|---------|-----------------|
|
||||
| `--name "foo"` | Use `--prop name="foo"` — all attributes go through `--prop` |
|
||||
| Unquoted `[N]` paths in zsh/bash | Always quote: `'/slide[1]'` or `"/slide[1]"` (shell glob-expands brackets) |
|
||||
| PPT `shape[1]` for content | `shape[1]` is typically the title placeholder. Use `shape[2]+` for content shapes |
|
||||
| `/shape[myname]` | Name indexing not supported. Use numeric index or `@name=` (PPT only) |
|
||||
| Guessing property names | Run `officecli help <format> <element>` to see exact names |
|
||||
| Modifying an open file | Close the file in PowerPoint/WPS first |
|
||||
| `\n` in shell strings | Use `\\n` for newlines in `--prop text="..."` |
|
||||
| `$` in shell text | `--prop text="$15M"` strips `$15`. Use single quotes: `--prop text='$15M'`, or heredoc batch |
|
||||
|
||||
---
|
||||
|
||||
## Specialized Skills
|
||||
|
||||
`officecli load_skill <name>` — output is a SKILL.md, follow its rules.
|
||||
|
||||
**Loading rule**:
|
||||
- Pick the most specific match in "When to use"; if none fits, load the format default (`word` / `pptx` / `excel`).
|
||||
- Scenes already contain the format default's rules — load **one** skill per artifact, never stack.
|
||||
- Loaded rules persist across turns; don't re-load each reply.
|
||||
- Two distinct artifacts → two separate loads.
|
||||
|
||||
### Word (.docx)
|
||||
|
||||
| Name | When to use |
|
||||
|------|-------------|
|
||||
| `word` | Reports, letters, memos, proposals, generic documents |
|
||||
| `academic-paper` | Journal / conference / thesis: APA / Chicago / IEEE / MLA citations, equations, SEQ + PAGEREF cross-refs, multi-column journal layout, bibliography. NOT for business reports or letters (route those to `word`) |
|
||||
|
||||
### PowerPoint (.pptx)
|
||||
|
||||
| Name | When to use |
|
||||
|------|-------------|
|
||||
| `pptx` | Generic decks: board reviews, sales decks, all-hands, product launches |
|
||||
| `pitch-deck` | **Fundraising only** — seed / Series A-C / SAFE / convertible / strategic raise. NOT for sales / product / board decks (route those to `pptx`) |
|
||||
| `morph-ppt` | Cinematic Morph-animated presentations. NOT for static decks (route those to `pptx`) |
|
||||
| `morph-ppt-3d` | 3D Morph: GLB models, camera moves, depth. NOT for 2D-only Morph (route those to `morph-ppt`) |
|
||||
|
||||
### Excel (.xlsx)
|
||||
|
||||
| Name | When to use |
|
||||
|------|-------------|
|
||||
| `excel` | Generic workbooks, formulas, pivots, trackers |
|
||||
| `financial-model` | Financial models, scenarios, projections. NOT for general data analysis (route those to `excel`) |
|
||||
| `data-dashboard` | CSV/tabular data → KPI / analytics / executive dashboards with charts and sparklines. NOT for raw data tracking (route those to `excel`) |
|
||||
|
||||
Example: a fundraising deck task → `officecli load_skill pitch-deck` → use the printed rules.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Paths are **1-based** (XPath convention): `'/body/p[3]'` = third paragraph
|
||||
- `--index` is **0-based** (array convention): `--index 0` = first position
|
||||
- **Excel exception**: for `add --type row` and `add --type col`, `--index N` is **1-based** (matches OOXML RowIndex / column letter index). `--index 5` inserts at row 5 / column 5.
|
||||
- After modifications, verify with `validate` and/or `view issues`
|
||||
- **When unsure**, run `officecli help <format> <element>` instead of guessing
|
||||
@@ -0,0 +1,49 @@
|
||||
OfficeCLI — Third-Party Notices
|
||||
|
||||
This product bundles third-party components. Their copyright notices and
|
||||
license terms are reproduced below as required by their respective licenses.
|
||||
|
||||
================================================================================
|
||||
DocumentFormat.OpenXml (3.4.1)
|
||||
https://github.com/dotnet/Open-XML-SDK
|
||||
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
Licensed under the MIT License. See "MIT License" below.
|
||||
|
||||
================================================================================
|
||||
System.CommandLine (3.0.0-preview.2.26159.112)
|
||||
https://github.com/dotnet/command-line-api
|
||||
|
||||
Copyright (c) .NET Foundation and Contributors
|
||||
|
||||
Licensed under the MIT License. See "MIT License" below.
|
||||
|
||||
================================================================================
|
||||
.NET Runtime (bundled by self-contained publish)
|
||||
https://github.com/dotnet/runtime
|
||||
|
||||
Copyright (c) .NET Foundation and Contributors
|
||||
|
||||
Licensed under the MIT License. See "MIT License" below.
|
||||
|
||||
================================================================================
|
||||
MIT License
|
||||
--------------------------------------------------------------------------------
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 331 KiB |
|
After Width: | Height: | Size: 588 KiB |
|
After Width: | Height: | Size: 597 KiB |
|
After Width: | Height: | Size: 563 KiB |
|
After Width: | Height: | Size: 331 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 590 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 387 KiB |
|
After Width: | Height: | Size: 655 KiB |
|
After Width: | Height: | Size: 6.5 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 2.3 MiB |
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
PROJECT="src/officecli/officecli.csproj"
|
||||
ALL_TARGETS="osx-arm64:officecli-mac-arm64 osx-x64:officecli-mac-x64 linux-x64:officecli-linux-x64 linux-arm64:officecli-linux-arm64 linux-musl-x64:officecli-linux-alpine-x64 linux-musl-arm64:officecli-linux-alpine-arm64 win-x64:officecli-win-x64.exe win-arm64:officecli-win-arm64.exe"
|
||||
|
||||
# Detect current platform RID
|
||||
detect_local_rid() {
|
||||
local OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
local ARCH=$(uname -m)
|
||||
local LIBC="gnu"
|
||||
if [ "$OS" = "linux" ]; then
|
||||
if command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then
|
||||
LIBC="musl"
|
||||
elif [ -f /etc/alpine-release ]; then
|
||||
LIBC="musl"
|
||||
fi
|
||||
fi
|
||||
case "$OS" in
|
||||
darwin)
|
||||
case "$ARCH" in
|
||||
arm64) echo "osx-arm64" ;;
|
||||
x86_64) echo "osx-x64" ;;
|
||||
esac ;;
|
||||
linux)
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
if [ "$LIBC" = "musl" ]; then echo "linux-musl-x64"; else echo "linux-x64"; fi ;;
|
||||
aarch64|arm64)
|
||||
if [ "$LIBC" = "musl" ]; then echo "linux-musl-arm64"; else echo "linux-arm64"; fi ;;
|
||||
esac ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Find target entry by RID
|
||||
find_target() {
|
||||
local RID="$1"
|
||||
for target in $ALL_TARGETS; do
|
||||
if [ "${target%%:*}" = "$RID" ]; then
|
||||
echo "$target"
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
build_config() {
|
||||
local CONFIG="$1"
|
||||
local TARGETS="$2"
|
||||
local OUTPUT="bin/$(echo "$CONFIG" | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
rm -rf "$OUTPUT"
|
||||
mkdir -p "$OUTPUT"
|
||||
|
||||
for target in $TARGETS; do
|
||||
RID="${target%%:*}"
|
||||
NAME="${target##*:}"
|
||||
TMPDIR=$(mktemp -d)
|
||||
|
||||
echo "[$CONFIG] Building $RID -> $NAME"
|
||||
dotnet publish "$PROJECT" -c "$CONFIG" -r "$RID" -o "$TMPDIR" --nologo -v quiet
|
||||
|
||||
# Atomic replace: stage as .new alongside the target, sign there, then rename.
|
||||
# Overwriting the binary in place would trash the text segment of any
|
||||
# running officecli process that happens to be mmap'd on this path
|
||||
# (macOS does not block ETXTBSY), leaving it stuck in uninterruptible
|
||||
# `UE` state on the next code page fault.
|
||||
if [ -f "$TMPDIR/officecli.exe" ]; then
|
||||
cp "$TMPDIR/officecli.exe" "$OUTPUT/$NAME.new"
|
||||
else
|
||||
cp "$TMPDIR/officecli" "$OUTPUT/$NAME.new"
|
||||
fi
|
||||
|
||||
# Ad-hoc codesign on macOS (required by AppleSystemPolicy).
|
||||
# Done on the staged .new copy so the live binary is never mutated in place.
|
||||
if [ "$(uname -s)" = "Darwin" ] && [[ "$RID" == osx-* ]]; then
|
||||
codesign -s - -f "$OUTPUT/$NAME.new" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
mv -f "$OUTPUT/$NAME.new" "$OUTPUT/$NAME"
|
||||
cp "$TMPDIR/officecli.pdb" "$OUTPUT/${NAME%.*}.pdb"
|
||||
|
||||
rm -rf "$TMPDIR"
|
||||
done
|
||||
|
||||
rm -rf src/officecli/bin src/officecli/obj
|
||||
|
||||
echo ""
|
||||
echo "$CONFIG build complete:"
|
||||
ls -lh "$OUTPUT"
|
||||
}
|
||||
|
||||
CONFIG="${1:-release}"
|
||||
|
||||
case "$CONFIG" in
|
||||
release|Release)
|
||||
LOCAL_RID=$(detect_local_rid)
|
||||
TARGET=$(find_target "$LOCAL_RID")
|
||||
if [ -z "$TARGET" ]; then
|
||||
echo "Unsupported platform: $(uname -s) $(uname -m)"
|
||||
exit 1
|
||||
fi
|
||||
build_config "Release" "$TARGET"
|
||||
;;
|
||||
debug|Debug)
|
||||
LOCAL_RID=$(detect_local_rid)
|
||||
TARGET=$(find_target "$LOCAL_RID")
|
||||
if [ -z "$TARGET" ]; then
|
||||
echo "Unsupported platform: $(uname -s) $(uname -m)"
|
||||
exit 1
|
||||
fi
|
||||
build_config "Debug" "$TARGET"
|
||||
;;
|
||||
all)
|
||||
build_config "Release" "$ALL_TARGETS"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ./build.sh [release|debug|all]"
|
||||
echo " release - Build Release for current platform (default)"
|
||||
echo " debug - Build Debug for current platform"
|
||||
echo " all - Build Release for all platforms"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT="$SCRIPT_DIR/src/officecli/officecli.csproj"
|
||||
BINARY_NAME="officecli"
|
||||
|
||||
# Detect platform
|
||||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH=$(uname -m)
|
||||
|
||||
case "$OS" in
|
||||
darwin)
|
||||
case "$ARCH" in
|
||||
arm64) RID="osx-arm64" ;;
|
||||
x86_64) RID="osx-x64" ;;
|
||||
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
linux)
|
||||
# Detect musl libc (Alpine, etc.)
|
||||
LIBC="gnu"
|
||||
if command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then
|
||||
LIBC="musl"
|
||||
elif [ -f /etc/alpine-release ]; then
|
||||
LIBC="musl"
|
||||
fi
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
if [ "$LIBC" = "musl" ]; then RID="linux-musl-x64"; else RID="linux-x64"; fi ;;
|
||||
aarch64|arm64)
|
||||
if [ "$LIBC" = "musl" ]; then RID="linux-musl-arm64"; else RID="linux-arm64"; fi ;;
|
||||
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported OS: $OS"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Build
|
||||
echo "Building officecli ($RID)..."
|
||||
TMPDIR=$(mktemp -d)
|
||||
dotnet publish "$PROJECT" -c Release -r "$RID" -o "$TMPDIR" --nologo -v quiet
|
||||
echo "Build complete."
|
||||
|
||||
# Install
|
||||
EXISTING=$(command -v "$BINARY_NAME" 2>/dev/null || true)
|
||||
if [ -n "$EXISTING" ]; then
|
||||
INSTALL_DIR=$(dirname "$EXISTING")
|
||||
echo "Found existing installation at $EXISTING, upgrading..."
|
||||
else
|
||||
INSTALL_DIR="$HOME/.local/bin"
|
||||
fi
|
||||
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
# Atomic replace: stage as .new alongside the target, sign there, then rename.
|
||||
# Overwriting the binary in place would trash the text segment of any
|
||||
# running officecli process (macOS does not block ETXTBSY), leaving it
|
||||
# stuck in uninterruptible `UE` state on the next code page fault.
|
||||
cp "$TMPDIR/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME.new"
|
||||
chmod +x "$INSTALL_DIR/$BINARY_NAME.new"
|
||||
rm -rf "$TMPDIR"
|
||||
|
||||
# macOS: remove quarantine flag and ad-hoc codesign (required by AppleSystemPolicy)
|
||||
# Done on the staged .new copy so the live binary is never mutated in place.
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
xattr -d com.apple.quarantine "$INSTALL_DIR/$BINARY_NAME.new" 2>/dev/null || true
|
||||
codesign -s - -f "$INSTALL_DIR/$BINARY_NAME.new" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
mv -f "$INSTALL_DIR/$BINARY_NAME.new" "$INSTALL_DIR/$BINARY_NAME"
|
||||
|
||||
# Hint if not in PATH
|
||||
case ":$PATH:" in
|
||||
*":$INSTALL_DIR:"*) ;;
|
||||
*) echo "Add to PATH: export PATH=\"$INSTALL_DIR:\$PATH\""
|
||||
echo "Or add the line above to your ~/.zshrc or ~/.bashrc" ;;
|
||||
esac
|
||||
|
||||
echo "OfficeCLI installed successfully!"
|
||||
echo "Run 'officecli --help' to get started."
|
||||
@@ -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).
|
||||
@@ -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"
|
||||
```
|
||||
@@ -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}")
|
||||
@@ -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"
|
||||
@@ -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]'
|
||||
```
|
||||
@@ -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}")
|
||||
@@ -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"
|
||||
@@ -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]"
|
||||
```
|
||||
@@ -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)")
|
||||
@@ -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"
|
||||
@@ -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]"
|
||||
```
|
||||
@@ -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)")
|
||||
@@ -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"
|
||||
@@ -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, -90–90 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]"
|
||||
```
|
||||
@@ -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 (50–250)",
|
||||
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)")
|
||||
@@ -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 (50–250)" \
|
||||
--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"
|
||||
@@ -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]"
|
||||
```
|
||||
@@ -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)")
|
||||
@@ -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)"
|
||||
@@ -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]"
|
||||
```
|
||||
@@ -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)")
|
||||
@@ -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"
|
||||
@@ -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]"
|
||||
```
|
||||
@@ -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")
|
||||
@@ -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"
|
||||
@@ -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]"
|
||||
```
|
||||
@@ -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)")
|
||||
@@ -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"
|
||||
@@ -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]"
|
||||
```
|
||||
@@ -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")
|
||||