slidemaker is a Python library for generating slide decks from PowerPoint
templates. Built on python-pptx, it exposes one high-level class
(SlideBuilder) that handles template loading, slide cloning, placeholder
replacement, content layout, style resolution, and final deck assembly.
pip install slidemakerOr for development:
pip install -e .Dependencies:
- Python
>=3.10 python-pptx >= 1
from slidemaker import SlideBuilder
sb = SlideBuilder("my_template.pptx", template_default_page=5)
# Replace placeholders in a template slide
sb.add_slide(
template_page=1,
content={
"title": "LESSON 7.2",
"subtitle": "ETL Pipelines for Analytics",
},
)
# Create new shapes on a cloned slide
sb.add_slide(
content={"title": "Extract Step"},
items=["Use pd.to_datetime for start date", "Use $gte/$lt for interval"],
code='query = {\n "createdAt": {"$gte": start, "$lt": end}\n}',
)
# Bullets-only slide using default template page
sb.add_slide(
content={"title": "Key Takeaways"},
items=["ETL separates concerns", "Classes bundle logic"],
)
sb.save("output.pptx")Place {{placeholder}} text in your PowerPoint template slides. When
add_slide clones a template page, shapes containing {{key}} are found and
replaced with the matching value from the content dict.
Placeholder matching is case-insensitive: {{TITLE}}, {{Title}}, and
{{title}} all match content={"title": "..."}.
str— replaces the shape text.list[str]— replaces the shape with a bullet list at the same position and size.None— clears the shape text.
Pass items, markdown, code, table, image, flow_boxes, or callout
to create new shapes on top of the cloned slide. These are laid out
automatically:
| Combination | Layout |
|---|---|
flow_boxes |
Flow diagram at content top |
items + code |
Bullets on top, code block below |
markdown + code |
Markdown on top, code block below |
items + table |
Bullets on top, table below |
markdown + table |
Markdown on top, table below |
code + table |
Code block on top, table below |
items + image |
Bullets on top, image below |
markdown + image |
Markdown on top, image below |
code + image |
Code block on top, image below |
items only |
Full content area |
markdown only |
Full content area |
code only |
Full content area |
table only |
Full content area |
image only |
Full content area |
callout |
Placed below other content |
Both modes (placeholder replacement and new shapes) can be used together on the same slide.
table can be combined with either items or code, but not both, and it
cannot be combined with flow_boxes. image can be combined with either
items or code, but not both, and it cannot be combined with table or
flow_boxes. markdown can be combined with code, table, or image, but
not with items or flow_boxes.
from slidemaker import SlideBuilderSlideBuilder(
template: str | Path,
style: dict[str, dict] | None = None,
template_default_page: int = 5,
image_provider=None,
media_cache_dir: str | Path | None = None,
)template: path to the.pptxtemplate (required).style: global style definitions.template_default_page: 1-based index of the default template slide to clone whentemplate_pageis not specified.image_provider: optional prompt-based image provider object or config dict. Dict form currently supports OpenAI settings such asmodel,api_key, andapi_key_env.media_cache_dir: optional cache directory for downloaded/generated images. Defaults to.slidemaker-cachenext to the template.
sb.add_slide(
content={"title": "Slide Title", "body": ["Point A", "Point B"]},
items=None,
markdown=None,
code=None,
table=None,
image=None,
flow_boxes=None,
callout=None,
notes="",
style=None,
template_page=None,
)content: dict of placeholder replacements ({{key}}in template).items: bullet points (creates new shapes).markdown: free-form markdown block (creates new shape).code: source code block (creates new shape).table: generated table definition (creates new shape).image: local image path or image spec, including URL or prompt-backed images (creates new shape).flow_boxes: flow diagram boxes (creates new shapes).callout: bold callout text below other content.notes: speaker notes.style: per-slide style override (string name, flat dict, or structured dict).template_page: 1-based template page to clone (defaults totemplate_default_page).
sb.add_style({
"dense": {"font-size": 26, "spacing": 10},
"highlight": {"font-color": "#193952"},
"#title": {"font-size": 51, "bold": True},
})sb.save("output.pptx")Removes all original template slides and writes the final deck.
.slide— base style for all new text shapes and placeholder fallback..code— code block style..table— base style for generated tables..table-header— header-row overrides for generated tables..table-cell— body-cell overrides for generated tables.#placeholder— style for a specific{{placeholder}}. Falls back to.slide.- Any other name — a named preset (e.g.
"dense") that can be applied viastyle="dense"orstyle={"use": "dense"}.
- Global:
SlideBuilder(style={...}) - Named presets:
add_style({"dense": {...}}), then use by name. - Per-slide:
style=...inadd_slide.
# Named preset
style="dense"
# Flat dict (applies to .slide)
style={"font-size": 26, "font-color": "#193952"}
# Structured dict with placeholder overrides
style={
"use": "dense",
".slide": {"font-size": 24},
".code": {"line-numbers": True},
".table": {"font-size": 20, "padding": "6pt"},
".table-header": {"fill-color": "#193952", "font-color": "#FFFFFF"},
".table-cell": {"font-color": "#0B1F33"},
"#title": {"font-size": 48, "letter-spacing": 90},
}font-name: stringfont-size: number (points) or string like"24pt"font-color:"#RRGGBB",(r, g, b), orRGBColorbold: boolitalic: boolalignment/align:left|center|right|justifyuppercase: boolline-spacing/line-height: multiplier,"1.2x","120%", or"36pt"letter-spacing: tracking units or"0.9pt"padding,padding-x,padding-y,padding-left,padding-right,padding-top,padding-bottom
spacing/space-afterspace-beforebullet-charbold-prefixes
These keys also apply to bullet paragraphs inside markdown blocks.
bg-color(orfill-color)line-numbers: bool
columns: optional header-row cell valuesrows: body rows aslist[list[str | None]]column_widths: optional column widths; numeric values are inchesrow_heights: optional row heights; numeric values are inchesbanded_rows: boolstyle: per-table override merged into.tableheader_style: per-table override merged into.table-headercell_style: per-table override merged into.table-cell
path/src: local image file pathurl: remote image URL to download into the media cacheprompt: prompt to generate an image via the configured image providercaption: optional caption below the imagefit:"contain"(default) or"stretch"caption_style: optional caption text override merged into.slidesize: optional prompt-generation size such as"1024x1024"quality: optional prompt-generation quality passed to OpenAIbackground: optional prompt-generation background passed to OpenAIoutput_format: optional prompt-generation output format, defaults topngoutput_compression: optional prompt-generation compression integer
Exactly one of path / src, url, or prompt must be provided.
fill-colorline-colorline-width
flow_boxes=[
{
"label": "EXTRACT",
"desc": "Get raw data",
"style": {
"fill-color": "#2E86AB",
"font-color": "#FFFFFF",
},
},
]table={
"columns": ["Field", "Type", "Notes"],
"rows": [
["_id", "ObjectId", "Primary key"],
["createdAt", "datetime", "UTC timestamp"],
["tags", "list[str]", "Optional labels"],
],
"column_widths": [2.2, 2.0, 5.8],
"banded_rows": True,
}image={
"path": "artifacts/confusion_matrix.png",
"caption": "Validation confusion matrix",
"fit": "contain",
}image={
"url": "https://example.com/assets/confusion-matrix.png",
"caption": "Validation confusion matrix",
}from slidemaker import OpenAIImageProvider, SlideBuilder
sb = SlideBuilder(
"my_template.pptx",
image_provider=OpenAIImageProvider(
model="gpt-image-1",
api_key_env="OPENAI_API_KEY",
),
)
sb.add_slide(
content={"title": "Generated Visual"},
image={
"prompt": "A clean isometric illustration of a machine learning pipeline",
"size": "1024x1024",
"caption": "Generated with OpenAI",
},
)markdown = """# Why This Matters
This pipeline keeps **data movement** and *transformation* clear.
- Reproducible steps
- Easier debugging
"""Supported markdown subset:
- paragraphs separated by blank lines
#,##,###headings- unordered bullets with
-or*rendered as real PowerPoint bullets - nested bullets using two leading spaces per level
- inline
**bold**,*italic*, and`code`
Bullet items support inline bold with **...**:
items=[
"Use **len(obs) // 2** for midpoint",
"**First half** -> control",
]Every add_slide call accepts notes="..." for speaker notes.
slidemaker.core provides lower-level helpers:
- text:
add_textbox,set_textbox_text,add_bullet_list,add_markdown_textbox,add_code_block - media:
add_image - tables:
add_table - layout:
layout_content_shapes - placeholders:
replace_placeholders - shapes/flow:
add_shape_rect,add_flow_boxes - notes:
set_notes - slide structure:
clone_slide,delete_slide,move_slide - shape lookup:
find_group_textbox,find_textbox_by_name