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

234 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Shape effects and meta — generates shapes-effects.pptx exercising the pptx shape
props NOT touched by shapes-basic / shapes-connectors / textboxes-basic:
autoFit, flipH/flipV (mirror), image= (picture as shape fill / blipFill), 3D
(bevel / bevelBottom / depth / lighting / material), softEdge, link + tooltip,
name override, and zorder stacking.
SDK twin of shapes-effects.sh (officecli CLI). Both produce an equivalent
shapes-effects.pptx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every slide / shape /
textbox 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.
A tiny 64x64 magenta+yellow checker PNG is synthesized on the fly (pure stdlib,
no document command) for the image-fill demo, exactly as the .sh does.
Usage:
pip install officecli-sdk # plus the `officecli` binary on PATH
python3 shapes-effects.py
"""
import os
import struct
import sys
import zlib
# --- 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-effects.pptx")
LONGTEXT = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do "
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
"enim ad minim veniam, quis nostrud exercitation ullamco laboris.")
def make_sample_png(path):
"""Write a 64x64 magenta+yellow 16px checker PNG (pure stdlib, no deps)."""
W = H = 64
rows = []
for y in range(H):
row = b"\x00"
for x in range(W):
cell = (x // 16 + y // 16) & 1
row += (b"\xE6\x39\x46" if cell else b"\xFF\xE6\x6D")
rows.append(row)
raw = b"".join(rows)
def chunk(t, d):
return struct.pack(">I", len(d)) + t + d + struct.pack(
">I", zlib.crc32(t + d) & 0xffffffff)
png = b"\x89PNG\r\n\x1a\n"
png += chunk(b"IHDR", struct.pack(">IIBBBBB", W, H, 8, 2, 0, 0, 0))
png += chunk(b"IDAT", zlib.compress(raw))
png += chunk(b"IEND", b"")
with open(path, "wb") as f:
f.write(png)
def slide():
"""One `add slide` item in batch-shape."""
return {"command": "add", "parent": "/", "type": "slide", "props": {}}
def textbox(sl, **props):
"""One `add textbox` item on slide `sl` in batch-shape."""
return {"command": "add", "parent": f"/slide[{sl}]", "type": "textbox", "props": props}
def shape(sl, **props):
"""One `add shape` item on slide `sl` in batch-shape."""
return {"command": "add", "parent": f"/slide[{sl}]", "type": "shape", "props": props}
SAMPLE_PNG = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".shapes-effects-fill.png")
make_sample_png(SAMPLE_PNG)
print(f"Building {FILE} ...")
try:
with officecli.create(FILE, "--force") as doc:
items = [
# ============================================================
# Slide 1 — autoFit (text overflow behavior)
# ============================================================
slide(),
textbox(1, text="autoFit — text overflow behavior", size="28", bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in"),
# 'none' — text just overflows the box
textbox(1, x="0.5in", y="1.5in", width="4in", height="1.5in",
fill="F1FAEE", size="18", text=LONGTEXT, autoFit="none"),
textbox(1, text="autoFit=none (overflows)", size="12", italic="true",
x="0.5in", y="3.2in", width="4in", height="0.4in"),
# 'normal' — shrinks text to fit
textbox(1, x="5in", y="1.5in", width="4in", height="1.5in",
fill="A8DADC", size="18", text=LONGTEXT, autoFit="normal"),
textbox(1, text="autoFit=normal (text shrinks)", size="12", italic="true",
x="5in", y="3.2in", width="4in", height="0.4in"),
# 'shape' — box resizes to fit text
textbox(1, x="9.5in", y="1.5in", width="4in", height="1.5in",
fill="F4A261", size="18", text=LONGTEXT, autoFit="shape"),
textbox(1, text="autoFit=shape (box grows)", size="12", italic="true",
x="9.5in", y="4.5in", width="4in", height="0.4in"),
# ============================================================
# Slide 2 — flipH / flipV (mirror)
# ============================================================
slide(),
textbox(2, text="flipH / flipV — mirror", size="28", bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in"),
# Original
shape(2, geometry="rightArrow", x="0.5in", y="2in", width="2.8in", height="1.5in",
fill="4472C4", color="FFFFFF", bold="true", text="original"),
# flipH
shape(2, geometry="rightArrow", x="4in", y="2in", width="2.8in", height="1.5in",
fill="E63946", color="FFFFFF", bold="true", text="flipH=true", flipH="true"),
# flipV
shape(2, geometry="rightArrow", x="7.5in", y="2in", width="2.8in", height="1.5in",
fill="2A9D8F", color="FFFFFF", bold="true", text="flipV=true", flipV="true"),
# flipH + flipV
shape(2, geometry="rightArrow", x="11in", y="2in", width="2.8in", height="1.5in",
fill="F4A261", color="000000", bold="true", text="flipH + flipV",
flipH="true", flipV="true"),
textbox(2, text=("Aliases: flipHorizontal, flipVertical. Flip flags are stored "
"independently of rotation, so flipH + rotate=90 chains predictably."),
size="14", italic="true", color="666666",
x="0.5in", y="4in", width="13in", height="0.6in"),
# ============================================================
# Slide 3 — image fill on a shape (blipFill, NOT --type picture)
# ============================================================
slide(),
textbox(3, text="image= — picture as shape fill (blipFill)", size="28", bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in"),
# The image fills the shape interior; the geometry preset clips the image.
shape(3, geometry="ellipse", x="0.5in", y="1.5in", width="3.5in", height="3.5in",
image=SAMPLE_PNG, lineColor="1D3557", lineWidth="3pt"),
shape(3, geometry="star5", x="4.5in", y="1.5in", width="3.5in", height="3.5in",
image=SAMPLE_PNG),
shape(3, geometry="diamond", x="8.5in", y="1.5in", width="3.5in", height="3.5in",
image=SAMPLE_PNG, lineColor="1D3557", lineWidth="3pt"),
textbox(3, text=('image="/path/to/photo.png" turns the shape into a clipped picture '
"— different element from --type picture, which embeds the bitmap "
"with its native bounding box."),
size="14", italic="true", color="666666",
x="0.5in", y="5.5in", width="13in", height="1in"),
# ============================================================
# Slide 4 — 3D effects (bevel, bevelBottom, depth, lighting, material)
# ============================================================
slide(),
textbox(4, text="3D — bevel / depth / lighting / material", size="28", bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in"),
# Bevel top, default size
shape(4, geometry="roundRect", x="0.5in", y="1.4in", width="3in", height="1.8in",
fill="4472C4", color="FFFFFF", bold="true", size="14",
text="bevel=circle", bevel="circle"),
# Bevel top + bottom with explicit widths
shape(4, geometry="roundRect", x="4in", y="1.4in", width="3in", height="1.8in",
fill="E63946", color="FFFFFF", bold="true", size="14",
text="bevel=angle-8-4 + bevelBottom=circle-4-4",
bevel="angle-8-4", bevelBottom="circle-4-4"),
# Extrusion depth
shape(4, geometry="roundRect", x="7.5in", y="1.4in", width="3in", height="1.8in",
fill="2A9D8F", color="FFFFFF", bold="true", size="14",
text="depth=14pt + bevel=softRound", depth="14pt", bevel="softRound"),
# Lighting + material combos
shape(4, geometry="ellipse", x="0.5in", y="3.7in", width="3in", height="1.8in",
fill="F4A261", color="000000", bold="true", size="12",
text="bevel=circle-8 depth=10 lighting=threePt material=metal",
bevel="circle-8", depth="10", lighting="threePt", material="metal"),
shape(4, geometry="ellipse", x="4in", y="3.7in", width="3in", height="1.8in",
fill="A8DADC", color="000000", bold="true", size="12",
text="lighting=balanced material=plastic",
bevel="circle-6", depth="8", lighting="balanced", material="plastic"),
shape(4, geometry="ellipse", x="7.5in", y="3.7in", width="3in", height="1.8in",
fill="FFD700", color="000000", bold="true", size="12",
text="lighting=harsh material=warmMatte",
bevel="circle-6", depth="8", lighting="harsh", material="warmMatte"),
# ============================================================
# Slide 5 — softEdge + link + tooltip + name + zorder
# ============================================================
slide(),
textbox(5, text="softEdge / link / name / zorder", size="28", bold="true",
x="0.5in", y="0.3in", width="12in", height="0.6in"),
# softEdge — feathered/blurred edge in points
shape(5, geometry="ellipse", x="0.5in", y="1.5in", width="3in", height="2in",
fill="E63946", color="FFFFFF", bold="true",
text="softEdge=0 (sharp)", softEdge="0"),
shape(5, geometry="ellipse", x="4in", y="1.5in", width="3in", height="2in",
fill="E63946", color="FFFFFF", bold="true",
text="softEdge=8pt", softEdge="8pt"),
shape(5, geometry="ellipse", x="7.5in", y="1.5in", width="3in", height="2in",
fill="E63946", color="FFFFFF", bold="true",
text="softEdge=20pt (heavy feather)", softEdge="20pt"),
# link + tooltip on a shape — entire shape becomes clickable
shape(5, geometry="roundRect", x="0.5in", y="4in", width="4in", height="1in",
fill="2A9D8F", color="FFFFFF", bold="true", size="16",
text="Click me → example.com",
link="https://example.com", tooltip="Open example.com", name="cta-button"),
textbox(5, text=('link=https://example.com tooltip="Open example.com" name="cta-button"'),
size="12", italic="true", color="666666",
x="0.5in", y="5.1in", width="6in", height="0.4in"),
# zorder — three overlapping shapes with explicit stack order
shape(5, geometry="rect", x="8in", y="4in", width="2.5in", height="2.5in",
fill="4472C4", name="back", zorder="1",
color="FFFFFF", bold="true", text="back (zorder=1)"),
shape(5, geometry="rect", x="9in", y="4.5in", width="2.5in", height="2.5in",
fill="E63946", name="middle", zorder="2",
color="FFFFFF", bold="true", text="middle (zorder=2)"),
shape(5, geometry="rect", x="10in", y="5in", width="2.5in", height="2.5in",
fill="F4A261", name="front", zorder="3",
color="000000", bold="true", text="front (zorder=3)"),
]
doc.batch(items)
print(f" added {len(items)} slides/shapes/textboxes")
doc.send({"command": "save"})
# context exit closes the resident, flushing the deck to disk.
finally:
if os.path.exists(SAMPLE_PNG):
os.remove(SAMPLE_PNG)
print(f"Generated: {FILE}")