import os
import sys
import copy
try:
from pptx import Presentation
from pptx.util import Inches
except ImportError:
pass
def load_template(template_path):
if not os.path.isfile(template_path):
print(f"ERROR: template not found: {template_path!r}", file=sys.stderr)
return None
try:
return Presentation(template_path)
except Exception as e:
print(f"ERROR: failed to load template: {e}", file=sys.stderr)
return None
def fill_text_placeholders(slide, values):
for shape in slide.shapes:
if shape.has_text_frame:
for paragraph in shape.text_frame.paragraphs:
for run in paragraph.runs:
for key, val in values.items():
placeholder = f"{{{{{key}}}}}"
if placeholder in run.text:
run.text = run.text.replace(placeholder, str(val))
def fill_image_placeholders(slide, image_map):
for shape in slide.shapes:
if shape.name in image_map:
img_path = image_map[shape.name]
if os.path.isfile(img_path):
try:
left = shape.left
top = shape.top
width = shape.width
height = shape.height
sp = shape._element
sp.getparent().remove(sp)
slide.shapes.add_picture(img_path, left, top, width, height)
except Exception as e:
print(f"WARNING: failed to replace image '{shape.name}': {e}",
file=sys.stderr)
def build_from_template(template_path, slide_fills, output_path=None):
prs = load_template(template_path)
if prs is None:
return None
new_prs = Presentation()
new_prs.slide_width = prs.slide_width
new_prs.slide_height = prs.slide_height
for fill in slide_fills:
slide_idx = fill.get("slide_index", 0)
clone_count = fill.get("clone_count", 1)
text_vals = fill.get("text", {})
img_map = fill.get("images", {})
if slide_idx >= len(prs.slides):
print(f"WARNING: slide_index {slide_idx} out of range", file=sys.stderr)
continue
for _ in range(clone_count):
template_slide = prs.slides[slide_idx]
new_slide = new_prs.slides.add_slide(new_prs.slide_layouts[6])
_copy_shapes(template_slide, new_slide)
fill_text_placeholders(new_slide, text_vals)
fill_image_placeholders(new_slide, img_map)
if output_path:
new_prs.save(output_path)
return new_prs
def _copy_shapes(source_slide, dest_slide):
import copy
from lxml import etree
bg = source_slide.background
if bg.fill.type is not None:
dest_slide.background.fill.solid()
try:
dest_slide.background.fill.fore_color.rgb = bg.fill.fore_color.rgb
except Exception:
pass
for shape in source_slide.shapes:
el = copy.deepcopy(shape._element)
dest_slide.shapes._spTree.append(el)
def make_template_data(slide_fills):
return {"template": True, "slide_fills": slide_fills}