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

225 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Connectors and groups — generates shapes-connectors.pptx exercising the pptx
`connector` element (straight / elbow / curve presets, head/tail arrowheads,
lineJoin / miterLimit) and the `group` element (comma-separated shape indices).
SDK twin of shapes-connectors.sh (officecli CLI). Both produce an equivalent
shapes-connectors.pptx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every command is
shipped over the named pipe. Each item is the same
`{"command","parent","type","props"}` dict you'd put in an `officecli batch`
list.
Connectors attach to shapes via from=/to= shape *paths*, and a group is built
from a comma-separated list of shape paths — so the shapes those refer to must
be added first and their returned `@id` path captured. `add_shape` does exactly
that: it `send`s one add and parses the path out of the response envelope
("Added shape at /slide[N]/shape[@id=M]" → the last whitespace-delimited token).
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 shapes-connectors.py
"""
import os
import sys
# --- locate the SDK: prefer an installed `officecli-sdk`, else the in-repo copy
try:
import officecli # pip install officecli-sdk
except ImportError:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "sdk", "python"))
import officecli
FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "shapes-connectors.pptx")
def _path_from_add(resp):
"""officecli prints "Added shape at /slide[N]/shape[@id=M]"; the path is the
last whitespace-delimited token of the response's data/message string."""
msg = ""
if isinstance(resp, dict):
msg = resp.get("data") or resp.get("message") or ""
else:
msg = str(resp)
return msg.split()[-1] if msg else ""
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
def add(parent, type_, **props):
"""Send one `add` and return the parsed envelope."""
return doc.send({"command": "add", "parent": parent, "type": type_,
"props": {k: str(v) for k, v in props.items()}})
def add_shape(parent, **props):
"""Add a shape and return its captured @id path (for from=/to=/shapes=)."""
return _path_from_add(add(parent, "shape", **props))
# ─────────────────────────────────────────────────────────────────────────
# Slide 1 — Connector geometry presets (straight / elbow / curve)
# ─────────────────────────────────────────────────────────────────────────
add("/", "slide")
add("/slide[1]", "shape", text="Connector Presets", size=28, bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in")
A1 = add_shape("/slide[1]", geometry="ellipse",
x="0.5in", y="1.5in", width="2in", height="1.2in",
fill="4472C4", color="FFFFFF", bold="true", text="A")
B1 = add_shape("/slide[1]", geometry="ellipse",
x="4.5in", y="1.5in", width="2in", height="1.2in",
fill="E63946", color="FFFFFF", bold="true", text="B")
add("/slide[1]", "connector", shape="straight", **{"from": A1}, to=B1,
color="1D3557", lineWidth="2pt", tailEnd="triangle")
add("/slide[1]", "shape", text="straight (default)", size=12,
x="0.5in", y="2.8in", width="6in", height="0.4in")
A2 = add_shape("/slide[1]", geometry="ellipse",
x="0.5in", y="3.6in", width="2in", height="1.2in",
fill="4472C4", color="FFFFFF", bold="true", text="A")
B2 = add_shape("/slide[1]", geometry="ellipse",
x="4.5in", y="5in", width="2in", height="1.2in",
fill="E63946", color="FFFFFF", bold="true", text="B")
add("/slide[1]", "connector", shape="elbow", **{"from": A2}, to=B2,
color="1D3557", lineWidth="2pt", tailEnd="triangle")
add("/slide[1]", "shape", text="elbow (bent, 90° turns)", size=12,
x="0.5in", y="6.3in", width="6in", height="0.4in")
A3 = add_shape("/slide[1]", geometry="ellipse",
x="8in", y="1.5in", width="2in", height="1.2in",
fill="4472C4", color="FFFFFF", bold="true", text="A")
B3 = add_shape("/slide[1]", geometry="ellipse",
x="11.5in", y="4.5in", width="2in", height="1.2in",
fill="E63946", color="FFFFFF", bold="true", text="B")
add("/slide[1]", "connector", shape="curve", **{"from": A3}, to=B3,
color="2A9D8F", lineWidth="3pt", tailEnd="arrow")
add("/slide[1]", "shape", text="curve (smooth Bezier)", size=12,
x="7.5in", y="6in", width="6in", height="0.4in")
# ─────────────────────────────────────────────────────────────────────────
# Slide 2 — Mini flowchart with attached connectors
# ─────────────────────────────────────────────────────────────────────────
add("/", "slide")
add("/slide[2]", "shape", text="Flowchart with Attached Connectors",
size=28, bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in")
P1 = add_shape("/slide[2]", geometry="roundRect",
x="0.8in", y="2.5in", width="2.2in", height="1.2in",
fill="2A9D8F", color="FFFFFF", bold="true", size=16, text="Start")
P2 = add_shape("/slide[2]", geometry="diamond",
x="4.5in", y="2.3in", width="2.8in", height="1.6in",
fill="F4A261", color="000000", bold="true", size=14, text="Valid?")
P3 = add_shape("/slide[2]", geometry="roundRect",
x="9in", y="2.5in", width="2.2in", height="1.2in",
fill="E63946", color="FFFFFF", bold="true", size=16, text="End")
P4 = add_shape("/slide[2]", geometry="roundRect",
x="4.7in", y="5in", width="2.4in", height="1in",
fill="A8DADC", color="000000", bold="true", size=14, text="Retry")
# Connect Start → Valid? → End, plus the loopback Valid? → Retry → Start
add("/slide[2]", "connector", shape="straight", **{"from": P1}, to=P2,
color="1D3557", lineWidth="2pt", tailEnd="triangle")
add("/slide[2]", "connector", shape="straight", **{"from": P2}, to=P3,
color="1D3557", lineWidth="2pt", tailEnd="triangle")
add("/slide[2]", "connector", shape="elbow", **{"from": P2}, to=P4,
color="E63946", lineWidth="2pt", lineDash="dash", tailEnd="triangle")
add("/slide[2]", "connector", shape="elbow", **{"from": P4}, to=P1,
color="E63946", lineWidth="2pt", lineDash="dash", tailEnd="triangle")
# Branch labels (textbox-style; no fill, transparent outline)
add("/slide[2]", "textbox", x="7.4in", y="2.7in", width="1.3in", height="0.5in",
text="yes", size=12, bold="true", color="2A9D8F")
add("/slide[2]", "textbox", x="6in", y="4in", width="1.3in", height="0.5in",
text="no", size=12, bold="true", color="E63946")
# ─────────────────────────────────────────────────────────────────────────
# Slide 3 — Grouping shapes
# ─────────────────────────────────────────────────────────────────────────
add("/", "slide")
add("/slide[3]", "shape", text="Grouping Shapes", size=28, bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in")
# Three logo-like shapes that we'll group together.
G1 = add_shape("/slide[3]", geometry="ellipse",
x="1.5in", y="2in", width="1.4in", height="1.4in", fill="E63946")
G2 = add_shape("/slide[3]", geometry="ellipse",
x="2.4in", y="2in", width="1.4in", height="1.4in",
fill="F4A261", opacity="0.75")
G3 = add_shape("/slide[3]", geometry="ellipse",
x="3.3in", y="2in", width="1.4in", height="1.4in",
fill="2A9D8F", opacity="0.75")
# Group them by passing the captured shape paths (comma-separated)
add("/slide[3]", "group", shapes=f"{G1},{G2},{G3}", name="Logo")
add("/slide[3]", "textbox",
text=f"Three ellipses grouped (shapes={G1},{G2},{G3}).", size=12,
x="0.5in", y="4in", width="12in", height="0.5in")
# Three independent boxes for comparison
add("/slide[3]", "shape", geometry="rect",
x="8in", y="2in", width="1.4in", height="1.4in", fill="4472C4")
add("/slide[3]", "shape", geometry="rect",
x="9.5in", y="2in", width="1.4in", height="1.4in", fill="4472C4")
add("/slide[3]", "shape", geometry="rect",
x="11in", y="2in", width="1.4in", height="1.4in", fill="4472C4")
add("/slide[3]", "textbox",
text="Three independent boxes (no group — each addressed separately).",
size=12, x="7in", y="4in", width="6in", height="0.5in")
# ─────────────────────────────────────────────────────────────────────────
# Slide 4 — headEnd / lineJoin / miterLimit on connectors
# ─────────────────────────────────────────────────────────────────────────
add("/", "slide")
add("/slide[4]", "shape",
text="headEnd / lineJoin / miterLimit on Connectors",
size=24, bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in")
# headEnd — arrowhead at the START of the connector (the 'head' = from-side)
# tailEnd — arrowhead at the END of the connector (the 'tail' = to-side)
add("/slide[4]", "textbox", text="headEnd + tailEnd arrowhead combinations:",
size=14, bold="true",
x="0.5in", y="1.1in", width="12in", height="0.4in")
y = 1.8
for hv, tv in [("triangle", "oval"), ("diamond", "arrow"), ("arrow", "arrow")]:
add("/slide[4]", "connector", shape="straight",
x="0.5in", y=f"{y}in", width="5in", height="0in",
color="1D3557", lineWidth="2pt", headEnd=hv, tailEnd=tv)
add("/slide[4]", "textbox", text=f"headEnd={hv} tailEnd={tv}", size=12,
x="5.8in", y=f"{y}in", width="6in", height="0.4in")
y += 0.8
# lineJoin — connector joins: round / bevel / miter, visible at the elbow bend.
add("/slide[4]", "textbox", text="lineJoin on elbow connectors:",
size=14, bold="true",
x="0.5in", y="4.6in", width="12in", height="0.4in")
# Three elbow connectors, one per column. The third uses the compound
# lineJoin=miter:<lim> form (limit in 1/1000ths of a percent) to also
# exercise miterLimit.
def add_elbow(x_in, color, line_join, label):
add("/slide[4]", "connector", shape="elbow",
x=f"{x_in}in", y="5.2in", width="3.4in", height="1.6in",
color=color, lineWidth="5pt", lineJoin=line_join)
add("/slide[4]", "textbox", text=label, size=12,
x=f"{x_in}in", y="7.0in", width="4in", height="0.4in")
add_elbow(0.5, "E63946", "round", "lineJoin=round")
add_elbow(4.7, "E63946", "bevel", "lineJoin=bevel")
add_elbow(8.9, "2A9D8F", "miter:800000", "lineJoin=miter:800000 (800% limit)")
doc.send({"command": "save"})
print(f"Generated: {FILE}")