scripts/pptx_render.py
scripts/pptx_render.pyBrowse 8 files
858 tokens
3,536 bytes
Token encoding: o200k_base
Snapshot 24fd22b
← Back to SKILL.md
1#!/usr/bin/env python32"""Render every slide of a .pptx to per-slide PNG images.3 4Pipeline: LibreOffice (soffice --headless --convert-to pdf) turns the deck5into a PDF, then poppler (pdftoppm, or pdftocairo as an alternate) splits6the PDF into one PNG per slide.7 8Output is JSON. When both tools are present:9 {"rendered": true, "files": ["render/slide-1.png", ...]}10When either tool is missing the script still exits 0 and reports:11 {"rendered": false, "missing": ["soffice"], "guidance": "..."}12so callers can degrade gracefully (fall back to pptx_read.py --outline).13"""14import argparse15import glob16import json17import os18import shutil19import subprocess20import sys21import tempfile22 23 24def find_tools():25 """Return (soffice, splitter, missing) using shutil.which."""26 soffice = shutil.which("soffice")27 splitter = shutil.which("pdftoppm") or shutil.which("pdftocairo")28 missing = []29 if not soffice:30 missing.append("soffice")31 if not splitter:32 missing.append("pdftoppm (or pdftocairo)")33 return soffice, splitter, missing34 35 36def render(pptx_path, out_dir, prefix, dpi):37 soffice, splitter, missing = find_tools()38 if missing:39 return {40 "rendered": False, "missing": missing,41 "guidance": "Install LibreOffice (soffice) and poppler-utils "42 "(pdftoppm/pdftocairo) to render slides. Without "43 "them, verify decks with pptx_read.py --outline "44 "instead.",45 }46 47 os.makedirs(out_dir, exist_ok=True)48 with tempfile.TemporaryDirectory() as tmp:49 proc = subprocess.run(50 [soffice, "--headless", "--convert-to", "pdf",51 "--outdir", tmp, pptx_path],52 capture_output=True, text=True, encoding="utf-8",53 errors="replace", timeout=300)54 pdfs = glob.glob(os.path.join(tmp, "*.pdf"))55 if proc.returncode != 0 or not pdfs:56 raise SystemExit(f"soffice PDF conversion failed: {proc.stderr}")57 pdf = pdfs[0]58 out_prefix = os.path.join(out_dir, prefix)59 proc = subprocess.run(60 [splitter, "-png", "-r", str(dpi), pdf, out_prefix],61 capture_output=True, text=True, encoding="utf-8",62 errors="replace", timeout=300)63 if proc.returncode != 0:64 raise SystemExit(f"{os.path.basename(splitter)} failed: "65 f"{proc.stderr}")66 67 files = sorted(glob.glob(out_prefix + "*.png"))68 return {"rendered": True, "files": files}69 70 71def main(argv=None):72 if hasattr(sys.stdout, "reconfigure"):73 sys.stdout.reconfigure(encoding="utf-8", errors="replace")74 parser = argparse.ArgumentParser(75 description="Render each slide of a .pptx to a PNG via "76 "soffice + pdftoppm/pdftocairo.",77 epilog=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)78 parser.add_argument("pptx", help="path to the .pptx file")79 parser.add_argument("--outdir", default="render",80 help="directory for PNGs (default: ./render)")81 parser.add_argument("--prefix", default="slide",82 help="PNG filename prefix (default: slide)")83 parser.add_argument("--dpi", type=int, default=100,84 help="render resolution (default: 100)")85 args = parser.parse_args(argv)86 87 result = render(args.pptx, args.outdir, args.prefix, args.dpi)88 print(json.dumps(result, indent=2))89 return 090 91 92if __name__ == "__main__":93 sys.exit(main())94