feat: sync 12 new skills into catalog (total 31 skills), update manifest and README
This commit is contained in:
112
skills/drawio-skill/scripts/drawio2pptx.py
Normal file
112
skills/drawio-skill/scripts/drawio2pptx.py
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Turn a .drawio into a PowerPoint deck — one slide per page.
|
||||
|
||||
Exports each page of a (multi-page) .drawio to a PNG via the draw.io CLI and
|
||||
lays them into a 16:9 .pptx, one page per slide, scaled to fit with the page
|
||||
name as the slide title. A C4 model (Context / Container / Component) becomes a
|
||||
ready-to-present deck; any multi-page diagram becomes a slide sequence.
|
||||
|
||||
python3 drawio2pptx.py c4.drawio -o c4.pptx
|
||||
python3 drawio2pptx.py architecture.drawio # -> architecture.pptx
|
||||
|
||||
Needs the draw.io CLI (for the PNG export) and the `python-pptx` package
|
||||
(`pip install python-pptx`) for writing the deck. Slides are 13.333in × 7.5in
|
||||
(16:9); each image is centred and scaled to fit inside a small margin.
|
||||
|
||||
Usage: python3 drawio2pptx.py <file.drawio> [-o out.pptx] [--scale N]
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def page_names(path):
|
||||
"""Names of the <diagram> pages, in order (None where unnamed)."""
|
||||
try:
|
||||
root = ET.parse(path).getroot()
|
||||
except (ET.ParseError, OSError) as exc:
|
||||
sys.exit(f"error: cannot parse {path}: {exc}")
|
||||
diagrams = root.findall("diagram")
|
||||
return [d.get("name") for d in diagrams] if diagrams else [None]
|
||||
|
||||
|
||||
def png_size(path):
|
||||
"""(width, height) in pixels from a PNG's IHDR header."""
|
||||
with open(path, "rb") as f:
|
||||
head = f.read(24)
|
||||
return struct.unpack(">II", head[16:24])
|
||||
|
||||
|
||||
def export_page(drawio_file, index, out_png, scale):
|
||||
"""Export one page (1-based index) to PNG via the draw.io CLI."""
|
||||
r = subprocess.run(["drawio", "-x", "-f", "png", "--page-index", str(index),
|
||||
"-s", str(scale), "-o", out_png, drawio_file],
|
||||
capture_output=True)
|
||||
return r.returncode == 0 and os.path.exists(out_png)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="Export a .drawio to a PowerPoint deck (one slide per page).")
|
||||
ap.add_argument("file")
|
||||
ap.add_argument("-o", "--output", help="output .pptx (default: alongside input)")
|
||||
ap.add_argument("--scale", type=float, default=2.0, help="PNG export scale (default 2)")
|
||||
args = ap.parse_args()
|
||||
|
||||
if not os.path.isfile(args.file):
|
||||
sys.exit(f"error: {args.file} not found")
|
||||
try:
|
||||
from pptx import Presentation
|
||||
from pptx.util import Emu, Pt
|
||||
except ImportError:
|
||||
sys.exit("error: python-pptx is required (pip install python-pptx)")
|
||||
|
||||
names = page_names(args.file)
|
||||
out = args.output or os.path.splitext(args.file)[0] + ".pptx"
|
||||
|
||||
prs = Presentation()
|
||||
prs.slide_width = Emu(12192000) # 13.333in — 16:9
|
||||
prs.slide_height = Emu(6858000) # 7.5in
|
||||
blank = prs.slide_layouts[6] # the built-in "Blank" layout
|
||||
sw, sh = int(prs.slide_width), int(prs.slide_height)
|
||||
margin = Emu(457200) # 0.5in
|
||||
title_h = Emu(500000)
|
||||
|
||||
made = 0
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
for i, name in enumerate(names, 1): # draw.io --page-index is 1-based
|
||||
png = os.path.join(tmp, f"page{i}.png")
|
||||
if not export_page(args.file, i, png, args.scale):
|
||||
sys.stderr.write(f"warning: page {i} export failed — skipped\n")
|
||||
continue
|
||||
slide = prs.slides.add_slide(blank)
|
||||
top_pad = margin
|
||||
if name:
|
||||
box = slide.shapes.add_textbox(margin, Emu(180000),
|
||||
Emu(sw - 2 * int(margin)), title_h)
|
||||
tf = box.text_frame
|
||||
tf.text = name
|
||||
tf.paragraphs[0].runs[0].font.size = Pt(20)
|
||||
tf.paragraphs[0].runs[0].font.bold = True
|
||||
top_pad = Emu(180000) + title_h
|
||||
|
||||
cw, ch = sw - 2 * int(margin), sh - int(top_pad) - int(margin)
|
||||
pw, ph = png_size(png)
|
||||
scale = min(cw / pw, ch / ph) # fit, preserve aspect
|
||||
iw, ih = int(pw * scale), int(ph * scale)
|
||||
left = Emu(int((sw - iw) / 2))
|
||||
top = Emu(int(top_pad) + int((ch - ih) / 2))
|
||||
slide.shapes.add_picture(png, left, top, width=Emu(iw), height=Emu(ih))
|
||||
made += 1
|
||||
|
||||
if not made:
|
||||
sys.exit("error: no pages exported (is the draw.io CLI installed?)")
|
||||
prs.save(out)
|
||||
sys.stderr.write(f"wrote {out} ({made} slide{'s' if made != 1 else ''})\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user