Files
aiturk-hermes-ide/skills/productivity/powerpoint/scripts/pptx_from_template.py
T

89 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""Build a deck from a .pptx template (brand deck) and fill placeholders.
Two modes:
1) Token fill (default): open TEMPLATE, replace every {{token}} across
slides, tables, and notes using --values JSON ({"token": "value"}),
save to OUTPUT. Formatting of the token's run is preserved.
2) --add-slides SPEC.json: additionally append slides built from the
template's own layouts (referenced by layout name or index), so new
slides inherit the brand master. Spec:
{"slides": [{"layout": "Title and Content", "title": "New",
"bullets": ["a", {"text": "b", "level": 1}],
"notes": "presenter text"}]}
"""
import argparse
import json
import sys
from pptx import Presentation
def fill_tokens(prs, values):
from pptx_edit import replace_text # same scripts/ directory
total = 0
for token, value in values.items():
total += replace_text(prs, "{{%s}}" % token, str(value))
return total
def find_layout(prs, ref):
if isinstance(ref, int):
return prs.slide_layouts[ref]
for layout in prs.slide_layouts:
if layout.name == ref:
return layout
raise SystemExit(f"layout {ref!r} not found; available: "
f"{[la.name for la in prs.slide_layouts]}")
def add_slides(prs, spec):
from pptx_create import add_bullets
for slide_spec in spec.get("slides", []):
layout = find_layout(prs, slide_spec.get("layout", 1))
slide = prs.slides.add_slide(layout)
if slide_spec.get("title") is not None and slide.shapes.title:
slide.shapes.title.text = slide_spec["title"]
if slide_spec.get("bullets"):
body = next((ph for ph in slide.placeholders
if ph.placeholder_format.idx != 0), None)
if body is not None:
add_bullets(body.text_frame, slide_spec["bullets"])
if slide_spec.get("notes"):
slide.notes_slide.notes_text_frame.text = slide_spec["notes"]
def main(argv=None):
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
parser = argparse.ArgumentParser(
description="Fill {{tokens}} in a .pptx template and optionally "
"append slides using the template's own layouts.",
epilog=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("template", help="path to the template .pptx")
parser.add_argument("output", help="output .pptx path")
parser.add_argument("--values", metavar="JSON",
help="JSON file mapping token -> replacement value")
parser.add_argument("--add-slides", metavar="SPEC_JSON",
help="JSON spec of slides to append")
args = parser.parse_args(argv)
prs = Presentation(args.template)
filled = 0
if args.values:
with open(args.values, encoding="utf-8") as fh:
filled = fill_tokens(prs, json.load(fh))
if args.add_slides:
with open(args.add_slides, encoding="utf-8") as fh:
add_slides(prs, json.load(fh))
prs.save(args.output)
print(json.dumps({"ok": True, "output": args.output,
"tokens_filled": filled}))
return 0
if __name__ == "__main__":
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
sys.exit(main())