Skip to content

Commit 3c4f947

Browse files
committed
Initial workflows for final session
1 parent 72ecee9 commit 3c4f947

15 files changed

Lines changed: 5127 additions & 0 deletions
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
---
2+
name: pptx-from-template
3+
description: "Creates a new PowerPoint (.pptx) presentation based on an existing template PPTX, reusing its slide layouts, styles, fonts, colors, and backgrounds. Use this skill when the user asks to generate slides, create a new presentation from an existing one, or produce a PPTX with consistent branding from a template."
4+
argument-hint: "[template.pptx] [content description or plan]"
5+
---
6+
7+
# Create PPTX from Template
8+
9+
This skill creates a new PowerPoint presentation that inherits all styles, slide layouts, backgrounds, fonts, and color schemes from an existing PPTX template. It uses the `python-pptx` package.
10+
11+
## When to use
12+
13+
- User asks to create a new PPTX based on an existing presentation's style
14+
- User wants to generate slides with consistent branding from a template
15+
- User asks to produce a presentation using a previous session's look and feel
16+
- User has ASCII/markdown slide content and wants it turned into a styled PPTX
17+
18+
## Prerequisites
19+
20+
`python-pptx` is declared as an inline script dependency, so no manual install is needed when using `uv run`.
21+
22+
## Helper module
23+
24+
This skill includes a reusable Python module at `.github/skills/pptx-from-template/pptx_from_template.py`.
25+
26+
To inspect a template directly:
27+
```bash
28+
uv run .github/skills/pptx-from-template/pptx_from_template.py template.pptx
29+
```
30+
31+
Available functions:
32+
33+
| Function | Purpose |
34+
|---|---|
35+
| `inspect_template(path)` | Returns a dict with dimensions, layouts, placeholders, and existing slides |
36+
| `print_template_info(path)` | Prints a human-readable summary of the template |
37+
| `create_pptx_from_template(template, output, slides)` | Creates a new PPTX from a template with a list of slide content dicts |
38+
| `set_body_bullets(placeholder, lines)` | Sets placeholder text as bullet points |
39+
| `add_code_block(slide, code, ...)` | Adds a monospace code block with dark background |
40+
| `copy_slide_background(source, target)` | Copies background XML from one slide to another |
41+
42+
## How it works
43+
44+
The approach uses the template PPTX as the base file, which preserves:
45+
- **Slide master & layouts**: All layouts (title, content, section header, blank, etc.) with their formatting
46+
- **Theme**: Colors, fonts, effects
47+
- **Slide backgrounds**: Solid fills, gradient fills, and background images
48+
- **Placeholder styles**: Font sizes, positions, bullet styles
49+
50+
### Step 1: Inspect the template
51+
52+
Before generating slides, inspect the template to discover available slide layouts and their placeholders. Run this Python snippet to list them:
53+
54+
```python
55+
from pptx import Presentation
56+
57+
template = Presentation("template.pptx")
58+
59+
print(f"Slide dimensions: {template.slide_width} x {template.slide_height} EMUs")
60+
print(f" ({template.slide_width / 914400:.1f}\" x {template.slide_height / 914400:.1f}\")\n")
61+
62+
for i, layout in enumerate(template.slide_layouts):
63+
print(f"Layout [{i}]: '{layout.name}'")
64+
for ph in layout.placeholders:
65+
print(f" Placeholder idx={ph.placeholder_format.idx}, "
66+
f"name='{ph.name}', "
67+
f"type={ph.placeholder_format.type}, "
68+
f"size=({ph.left}, {ph.top}, {ph.width}, {ph.height})")
69+
print()
70+
```
71+
72+
Also inspect existing slides to see which layouts are used and how content is structured:
73+
74+
```python
75+
for i, slide in enumerate(template.slides):
76+
layout_name = slide.slide_layout.name
77+
print(f"Slide {i+1}: layout='{layout_name}'")
78+
for shape in slide.shapes:
79+
if shape.has_text_frame:
80+
text = shape.text_frame.text[:80]
81+
print(f" Shape '{shape.name}': \"{text}\"")
82+
elif shape.shape_type == 13: # Picture
83+
print(f" Picture '{shape.name}': {shape.width}x{shape.height}")
84+
print()
85+
```
86+
87+
### Step 2: Generate the new presentation
88+
89+
Use the template as the starting point. Delete existing slides (keeping the slide master/layouts), then add new slides using the discovered layouts.
90+
91+
```python
92+
from pptx import Presentation
93+
from pptx.util import Inches, Pt, Emu
94+
from pptx.enum.text import PP_ALIGN
95+
from lxml import etree
96+
import copy
97+
98+
def create_pptx_from_template(template_path, output_path, slides_content):
99+
"""Create a new PPTX from a template, preserving all styles.
100+
101+
Args:
102+
template_path: Path to the template .pptx file
103+
output_path: Path for the output .pptx file
104+
slides_content: List of dicts, each with:
105+
- layout_index (int): Index of the slide layout to use
106+
- title (str, optional): Title text
107+
- body (str, optional): Body/content text
108+
- notes (str, optional): Speaker notes
109+
"""
110+
prs = Presentation(template_path)
111+
112+
# Delete all existing slides (keep layouts/masters)
113+
while len(prs.slides) > 0:
114+
rId = prs.slides._sldIdLst[0].get('r:id')
115+
prs.part.drop_rel(rId)
116+
prs.slides._sldIdLst.remove(prs.slides._sldIdLst[0])
117+
118+
# Add new slides from content
119+
for slide_data in slides_content:
120+
layout_idx = slide_data.get("layout_index", 1)
121+
layout = prs.slide_layouts[layout_idx]
122+
slide = prs.slides.add_slide(layout)
123+
124+
# Set title if present and placeholder exists
125+
title_text = slide_data.get("title")
126+
if title_text and slide.shapes.title:
127+
slide.shapes.title.text = title_text
128+
129+
# Set body content if present
130+
body_text = slide_data.get("body")
131+
if body_text:
132+
for ph in slide.placeholders:
133+
if ph.placeholder_format.idx == 1: # Content placeholder
134+
ph.text = body_text
135+
break
136+
137+
# Add speaker notes if present
138+
notes_text = slide_data.get("notes")
139+
if notes_text:
140+
notes_slide = slide.notes_slide
141+
notes_slide.notes_text_frame.text = notes_text
142+
143+
prs.save(output_path)
144+
print(f"Created {output_path} with {len(slides_content)} slides")
145+
```
146+
147+
### Step 3: Advanced — Preserve a background from a template slide
148+
149+
If the template has slides with custom backgrounds (images, gradients) that you want to reuse on new slides, copy the background XML from the template slide:
150+
151+
```python
152+
from lxml import etree
153+
import copy
154+
155+
def copy_slide_background(source_slide, target_slide):
156+
"""Copy background properties from a source slide to a target slide."""
157+
source_bg = source_slide._element.find(
158+
'{http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing}bg',
159+
)
160+
if source_bg is None:
161+
# Try the presentation ML namespace
162+
source_bg = source_slide._element.find(
163+
'{http://schemas.openxmlformats.org/presentationml/2006/main}bg',
164+
)
165+
if source_bg is not None:
166+
# Remove existing background on target
167+
nsmap = {'p': 'http://schemas.openxmlformats.org/presentationml/2006/main'}
168+
existing_bg = target_slide._element.find('p:bg', nsmap)
169+
if existing_bg is not None:
170+
target_slide._element.remove(existing_bg)
171+
# Insert copy of source background
172+
new_bg = copy.deepcopy(source_bg)
173+
# Background should be the first child after cSld opening
174+
target_slide._element.insert(0, new_bg)
175+
```
176+
177+
### Step 4: Advanced — Multi-line body with bullet formatting
178+
179+
To add bullet points that inherit the template's bullet styling:
180+
181+
```python
182+
from pptx.util import Pt
183+
184+
def set_body_bullets(placeholder, lines):
185+
"""Set body text as bullet points, one per line.
186+
187+
Clears existing text and adds each line as a separate paragraph,
188+
inheriting the placeholder's default paragraph formatting.
189+
"""
190+
tf = placeholder.text_frame
191+
tf.clear()
192+
193+
for i, line in enumerate(lines):
194+
if i == 0:
195+
para = tf.paragraphs[0]
196+
else:
197+
para = tf.add_paragraph()
198+
para.text = line
199+
# Inherit level from template; set level 0 for top-level bullets
200+
para.level = 0
201+
```
202+
203+
### Step 5: Advanced — Add code blocks
204+
205+
For technical presentations, add code blocks as a text box with monospace font:
206+
207+
```python
208+
from pptx.util import Inches, Pt, Emu
209+
from pptx.dml.color import RGBColor
210+
211+
def add_code_block(slide, code_text, left=Inches(0.5), top=Inches(2),
212+
width=Inches(9), height=Inches(4.5),
213+
font_size=Pt(11), font_name="Consolas"):
214+
"""Add a code block as a text box with monospace font and dark background."""
215+
txBox = slide.shapes.add_textbox(left, top, width, height)
216+
tf = txBox.text_frame
217+
tf.word_wrap = True
218+
219+
# Set background fill on the shape
220+
fill = txBox.fill
221+
fill.solid()
222+
fill.fore_color.rgb = RGBColor(0x1E, 0x1E, 0x1E) # Dark background
223+
224+
para = tf.paragraphs[0]
225+
para.text = code_text
226+
run = para.runs[0]
227+
run.font.name = font_name
228+
run.font.size = font_size
229+
run.font.color.rgb = RGBColor(0xD4, 0xD4, 0xD4) # Light text
230+
```
231+
232+
## Typical workflow
233+
234+
1. User provides a template PPTX and slide content (ASCII plan, markdown, or structured data)
235+
2. **Inspect** the template to discover layouts and placeholders
236+
3. **Map** each piece of content to the appropriate layout
237+
4. **Generate** the PPTX using the template as the base
238+
5. **Review** the output and adjust as needed
239+
240+
## Layout mapping guide
241+
242+
Common layout conventions (indices vary per template — always inspect first):
243+
244+
| Layout name | Typical use | Key placeholders |
245+
|---|---|---|
246+
| Title Slide | First/last slide, section dividers | title (0), subtitle (1) |
247+
| Title and Content | Most content slides | title (0), body (1) |
248+
| Section Header | Topic transitions | title (0), subtitle (1) |
249+
| Two Content | Side-by-side comparisons | title (0), left (1), right (2) |
250+
| Blank | Diagrams, full-bleed images | none |
251+
| Title Only | Slides with custom content below title | title (0) |
252+
253+
## Important notes
254+
255+
- Always inspect the template first — layout indices and placeholder indices vary between templates
256+
- The template's slide master determines all default formatting; you rarely need to set fonts/colors explicitly
257+
- If a template slide has images or shapes you want to preserve, keep that slide and modify its text rather than deleting and recreating
258+
- Background images tied to the slide master (not individual slides) are automatically inherited by all new slides
259+
- For complex slides (diagrams, tables, charts), consider keeping the template slide and modifying specific shapes by name

0 commit comments

Comments
 (0)