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

212 lines
9.9 KiB
Python

#!/usr/bin/env python3
"""
pictures.py — embed and lay out images in a Word document (.docx).
SDK twin of pictures.sh (officecli CLI). Both produce an equivalent
pictures.docx. This one drives the **officecli Python SDK**
(`pip install officecli-sdk`): one resident is started and every paragraph and
picture is shipped over the named pipe. Each item is the same
`{"command","parent","type","props"}` dict you'd put in an `officecli batch`
list; `add(...)` ships one and returns its envelope.
In docx a picture is a run inside a paragraph, so every picture is added to a
paragraph path (/body/p[N]); floating layout (wrap / behindText / hAlign /
vAlign / hPosition / vPosition) requires props anchor=true, while inline
pictures sit in the text flow like a big character.
This script:
1. Synthesizes two sample PNGs (a square logo + a wide banner) in-dir
2. Builds a document demoing docx picture properties:
- 1: inline picture (in the text flow, width/height sizing)
- 2: cropped picture (crop=L,T,R,B percent per edge)
- 3: alt text (accessibility / screen readers)
- 4: behind-text watermark (anchor + wrap=none + behindText, centered)
- 5: square text wrap (text flows around a right-aligned float)
- 6: absolute position (anchor + wrap=tight + hPosition/vPosition)
- 7: clickable picture (link= external URL)
- 8: decorative picture (decorative=true — screen readers skip it)
Requirements:
pip install Pillow officecli-sdk # plus the `officecli` binary on PATH
Usage:
python3 pictures.py
"""
import os
import sys
try:
from PIL import Image, ImageDraw
except ImportError:
print("ERROR: Pillow not installed. Run: pip install Pillow")
sys.exit(1)
# --- 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
HERE = os.path.dirname(os.path.abspath(__file__))
FILE = os.path.join(HERE, "pictures.docx")
LOGO = os.path.join(HERE, "pictures-logo.png")
BANNER = os.path.join(HERE, "pictures-banner.png")
def make_logo(path, w=300, h=300):
img = Image.new("RGB", (w, h), (245, 245, 220))
d = ImageDraw.Draw(img)
d.ellipse((40, 40, 260, 260), fill=(52, 152, 219), outline=(0, 0, 0), width=4)
d.polygon([(150, 80), (210, 220), (90, 220)], fill=(241, 196, 15), outline=(0, 0, 0))
d.text((110, 140), "LOGO", fill=(0, 0, 0))
img.save(path)
def make_banner(path, w=800, h=200, c1=(231, 76, 60), c2=(142, 68, 173)):
img = Image.new("RGB", (w, h))
pix = img.load()
for x in range(w):
t = x / (w - 1)
col = tuple(int(c1[i] * (1 - t) + c2[i] * t) for i in range(3))
for y in range(h):
pix[x, y] = col
ImageDraw.Draw(img).text((20, 20), "banner.png", fill=(255, 255, 255))
img.save(path)
def add(doc, parent, typ, **props):
"""Ship one `add` item over the pipe; return the parsed envelope."""
return doc.send({"command": "add", "parent": parent, "type": typ, "props": props})
def para(doc, text="", **props):
add(doc, "/body", "paragraph", text=text, **props)
def main():
if os.path.exists(FILE):
os.remove(FILE)
make_logo(LOGO)
make_banner(BANNER)
print(f"Building {FILE} ...")
with officecli.create(FILE, "--force") as doc:
# ── 1. Inline picture — sits in the text flow like a large character ──
para(doc, "1. Inline Picture", style="Heading1")
para(doc, "An inline picture flows with the paragraph text — no anchor, "
"no wrap; it occupies its own line box like an oversized glyph:")
para(doc, "")
# Features: inline picture (default, no anchor); width / height sizing
add(doc, "/body/p[3]", "picture",
src=LOGO, width="3cm", height="3cm")
# ── 2. Cropped picture — trim edges via crop=L,T,R,B (percent) ────────
para(doc, "2. Cropped Picture", style="Heading1")
para(doc, "crop=L,T,R,B trims each edge by a percentage of the source. "
"Here 10% left, 5% top, 15% right, 8% bottom (per-edge "
"cropLeft/cropTop/cropRight/cropBottom also accepted on add):")
para(doc, "")
# Features: crop=L,T,R,B four-value form (percent of original per edge)
add(doc, "/body/p[6]", "picture",
src=BANNER, crop="10,5,15,8", width="10cm", height="2.5cm")
# ── 3. Picture with alt text — accessibility / screen readers ─────────
para(doc, "3. Alt Text (Accessibility)", style="Heading1")
para(doc, "alt= writes the DocProperties description read aloud by "
"screen readers. Aliases: altText, description.")
para(doc, "")
# Features: alt (alternative text for accessibility)
add(doc, "/body/p[9]", "picture",
src=LOGO, width="3cm", height="3cm",
alt="Company logo: a blue circle enclosing a yellow triangle")
# ── 4. Behind-text watermark — floating picture behind the text ───────
para(doc, "4. Behind-Text Watermark", style="Heading1")
para(doc, "A floating picture with anchor=true, wrap=none and "
"behindText=true sits behind the text like a watermark. It is "
"centered on the page margins via hAlign=center + vAlign=center. "
"This paragraph text should render on top of the faint image "
"behind it, demonstrating the behind-text z-order stacking that "
"a plain inline picture cannot achieve.")
# Features: anchor=true (floating), wrap=none + behindText=true, hAlign/vAlign=center
add(doc, "/body/p[11]", "picture",
src=BANNER, anchor="true", wrap="none", behindText="true",
hAlign="center", vAlign="center",
hRelative="margin", vRelative="margin",
width="12cm", height="3cm",
alt="Decorative watermark banner")
# ── 5. Square text-wrap — body text flows around a floating picture ───
para(doc, "5. Square Text Wrap", style="Heading1")
para(doc, "With anchor=true and wrap=square, the surrounding paragraph "
"text flows around the picture's bounding box. The picture "
"below is right-aligned to the margin, so this long paragraph "
"wraps down its left side. Keep reading to see the text reflow "
"around the floated image on the right — square wrap uses a "
"rectangular boundary regardless of the image's own shape, so "
"text keeps a clean vertical edge against the picture. The "
"remaining lines continue underneath once the text clears the "
"bottom of the anchored picture's bounding rectangle.")
# Features: wrap=square (text flows around bounding box), hAlign=right rel. margin
add(doc, "/body/p[13]", "picture",
src=LOGO, anchor="true", wrap="square",
hAlign="right", hRelative="margin", vRelative="paragraph",
width="3.5cm", height="3.5cm",
alt="Logo floated right with square wrap")
# ── 6. Tight wrap + absolute position — hPosition / vPosition ─────────
para(doc, "6. Absolute Position (hPosition / vPosition)", style="Heading1")
para(doc, "Instead of relative alignment, a floating picture can be "
"pinned to an absolute offset from its reference frame. Here "
"hPosition=2cm and vPosition=1cm place the picture 2cm from the "
"left margin and 1cm down, with wrap=tight so text hugs the "
"boundary. This paragraph provides enough text for the wrap to "
"be visible against the absolutely-positioned image.")
# Features: anchor=true, wrap=tight, hPosition/vPosition (absolute, unit-qualified)
add(doc, "/body/p[15]", "picture",
src=LOGO, anchor="true", wrap="tight",
hPosition="2cm", vPosition="1cm",
hRelative="margin", vRelative="paragraph",
width="3cm", height="3cm",
alt="Logo at absolute 2cm,1cm offset with tight wrap")
# ── 7. Clickable picture — link= makes the image a hyperlink ──────────
para(doc, "7. Clickable Picture (link)", style="Heading1")
para(doc, "link= wraps the picture in a click hyperlink. An absolute URL "
"round-trips as an external relationship; a #anchor or bookmark "
"name becomes an internal jump.")
para(doc, "")
# Features: link (external URL hyperlink on the image); alt on a clickable image
add(doc, "/body/p[18]", "picture",
src=BANNER, width="10cm", height="2.5cm",
link="https://example.com",
alt="Banner linking to example.com")
# ── 8. Decorative picture — mark as decorative for accessibility ──────
para(doc, "8. Decorative Picture (accessibility)", style="Heading1")
para(doc, "decorative=true marks the image as decorative: screen readers "
"skip it entirely (no alt text is announced). Stored as an "
"adec:decorative extension under the picture's docPr. Use it for "
"purely ornamental images that carry no information.")
para(doc, "")
# Features: decorative=true (accessibility — screen readers skip the image)
add(doc, "/body/p[21]", "picture",
src=BANNER, width="10cm", height="2.5cm",
decorative="true")
doc.send({"command": "save"})
# context exit closes the resident, flushing the document to disk.
print(f"Created: {FILE}")
if __name__ == "__main__":
main()