storyboard-to-slides

Assemble a polished PPTX from a storyboard CSV + image files using python-pptx .

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "storyboard-to-slides" with this command: npx skills add dp-archive/archive/dp-archive-archive-storyboard-to-slides

Storyboard to Slides

Assemble a polished PPTX from a storyboard CSV + image files using python-pptx .

Dependencies

pip install python-pptx Pillow

Input Format

Expects storyboard.csv with columns: slide_no, slide_type, title, bullet_points, image_prompt, speaker_notes, layout

And image files named slide_{no}.png for each row.

Workflow

  1. Read the Storyboard

import csv

with open("storyboard.csv", "r", encoding="utf-8") as f: reader = csv.DictReader(f) slides = list(reader)

  1. Initialize the Presentation

from pptx import Presentation from pptx.util import Inches, Pt, Emu from pptx.dml.color import RGBColor

prs = Presentation() prs.slide_width = Inches(13.333) # 16:9 prs.slide_height = Inches(7.5)

For 4:3 slides: Inches(10) x Inches(7.5) .

  1. Define Theme Constants

Customize per project

THEME = { "bg_color": RGBColor(0x1A, 0x1A, 0x2E), # dark navy "title_color": RGBColor(0xFF, 0xFF, 0xFF), "text_color": RGBColor(0xE0, 0xE0, 0xE0), "accent_color": RGBColor(0x64, 0xB5, 0xF6), "title_font": "Arial", "body_font": "Arial", "title_size": Pt(36), "body_size": Pt(18), }

Choose colors that contrast well with the generated images. For light images use dark text overlay with semi-transparent background; for dark images use white text.

  1. Layout Implementations

full_bg — Full-screen background image + overlay text

from pptx.util import Inches, Pt, Emu from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN, MSO_ANCHOR

def add_full_bg_slide(prs, img_path, title, subtitle="", theme=THEME): slide = prs.slides.add_slide(prs.slide_layouts[6]) # blank # Background image slide.shapes.add_picture(img_path, 0, 0, prs.slide_width, prs.slide_height) # Semi-transparent overlay overlay = slide.shapes.add_shape( 1, 0, 0, prs.slide_width, prs.slide_height # MSO_SHAPE.RECTANGLE = 1 ) overlay.fill.solid() overlay.fill.fore_color.rgb = RGBColor(0, 0, 0) overlay.shadow.inherit = False overlay.line.fill.background() # Set overlay transparency via XML (access the shape's XML element, not the fill object) from pptx.oxml.ns import qn solid = overlay._element.find(f'.//{qn("a:solidFill")}') if solid is not None: srgb = solid.find(qn("a:srgbClr")) if srgb is not None: alpha = srgb.makeelement(qn("a:alpha"), {"val": "40000"}) # 40% opacity srgb.append(alpha) # Title txBox = slide.shapes.add_textbox(Inches(1), Inches(2.5), Inches(11), Inches(2)) tf = txBox.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = title p.font.size = Pt(44) p.font.bold = True p.font.color.rgb = theme["title_color"] p.alignment = PP_ALIGN.CENTER if subtitle: p2 = tf.add_paragraph() p2.text = subtitle p2.font.size = Pt(24) p2.font.color.rgb = theme["text_color"] p2.alignment = PP_ALIGN.CENTER return slide

left_img_right_text — Image left, text right

def add_left_img_right_text(prs, img_path, title, bullets, theme=THEME): slide = prs.slides.add_slide(prs.slide_layouts[6]) # Solid background bg = slide.background fill = bg.fill fill.solid() fill.fore_color.rgb = theme["bg_color"] # Image on left (half width) slide.shapes.add_picture(img_path, 0, 0, Inches(6.5), prs.slide_height) # Title txBox = slide.shapes.add_textbox(Inches(7), Inches(0.5), Inches(5.8), Inches(1.2)) tf = txBox.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = title p.font.size = theme["title_size"] p.font.bold = True p.font.color.rgb = theme["title_color"] # Bullets txBox2 = slide.shapes.add_textbox(Inches(7), Inches(2), Inches(5.8), Inches(4.5)) tf2 = txBox2.text_frame tf2.word_wrap = True for i, line in enumerate(bullets.split("\n")): line = line.strip().lstrip("•-").strip() if not line: continue p = tf2.paragraphs[0] if i == 0 else tf2.add_paragraph() p.text = f" {line}" p.font.size = theme["body_size"] p.font.color.rgb = theme["text_color"] p.space_after = Pt(12) return slide

top_img_bottom_text — Image top, text bottom

def add_top_img_bottom_text(prs, img_path, title, bullets, theme=THEME): slide = prs.slides.add_slide(prs.slide_layouts[6]) bg = slide.background bg.fill.solid() bg.fill.fore_color.rgb = theme["bg_color"] # Image on top (60% height) slide.shapes.add_picture(img_path, 0, 0, prs.slide_width, Inches(4.5)) # Title txBox = slide.shapes.add_textbox(Inches(0.8), Inches(4.8), Inches(11.5), Inches(0.8)) tf = txBox.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = title p.font.size = Pt(28) p.font.bold = True p.font.color.rgb = theme["title_color"] # Bullets txBox2 = slide.shapes.add_textbox(Inches(0.8), Inches(5.7), Inches(11.5), Inches(1.5)) tf2 = txBox2.text_frame tf2.word_wrap = True for i, line in enumerate(bullets.split("\n")): line = line.strip().lstrip("•-").strip() if not line: continue p = tf2.paragraphs[0] if i == 0 else tf2.add_paragraph() p.text = f" {line}" p.font.size = Pt(16) p.font.color.rgb = theme["text_color"] return slide

center_text — Section divider / title-only

def add_center_text_slide(prs, img_path, title, subtitle="", theme=THEME): slide = prs.slides.add_slide(prs.slide_layouts[6]) if img_path and os.path.exists(img_path): slide.shapes.add_picture(img_path, 0, 0, prs.slide_width, prs.slide_height) # Add overlay same as full_bg else: bg = slide.background bg.fill.solid() bg.fill.fore_color.rgb = theme["bg_color"] txBox = slide.shapes.add_textbox(Inches(2), Inches(2.8), Inches(9), Inches(2)) tf = txBox.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.text = title p.font.size = Pt(40) p.font.bold = True p.font.color.rgb = theme["title_color"] p.alignment = PP_ALIGN.CENTER if subtitle: p2 = tf.add_paragraph() p2.text = subtitle p2.font.size = Pt(20) p2.font.color.rgb = theme["text_color"] p2.alignment = PP_ALIGN.CENTER return slide

two_column — Two-column text (for data slides)

def add_two_column_slide(prs, img_path, title, bullets, theme=THEME): slide = prs.slides.add_slide(prs.slide_layouts[6]) bg = slide.background bg.fill.solid() bg.fill.fore_color.rgb = theme["bg_color"] # Title txBox = slide.shapes.add_textbox(Inches(0.8), Inches(0.5), Inches(11.5), Inches(1)) tf = txBox.text_frame p = tf.paragraphs[0] p.text = title p.font.size = theme["title_size"] p.font.bold = True p.font.color.rgb = theme["title_color"] # Split bullets into two columns lines = [l.strip().lstrip("•-").strip() for l in bullets.split("\n") if l.strip()] mid = len(lines) // 2 left_lines, right_lines = lines[:mid], lines[mid:] for col_idx, (col_lines, left_pos) in enumerate([(left_lines, 0.8), (right_lines, 7.0)]): txBox = slide.shapes.add_textbox(Inches(left_pos), Inches(1.8), Inches(5.5), Inches(5)) tf = txBox.text_frame tf.word_wrap = True for i, line in enumerate(col_lines): p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() p.text = f" {line}" p.font.size = theme["body_size"] p.font.color.rgb = theme["text_color"] p.space_after = Pt(10) if img_path and os.path.exists(img_path): slide.shapes.add_picture(img_path, Inches(9.5), Inches(4.5), Inches(3.5), Inches(2.5)) return slide

  1. Assembly Loop

import os

LAYOUT_MAP = { "full_bg": add_full_bg_slide, "left_img_right_text": add_left_img_right_text, "top_img_bottom_text": add_top_img_bottom_text, "center_text": add_center_text_slide, "two_column": add_two_column_slide, }

for row in slides: no = row["slide_no"] layout = row.get("layout", "left_img_right_text") img_path = f"slide_{no}.png" title = row.get("title", "") bullets = row.get("bullet_points", "")

fn = LAYOUT_MAP.get(layout, add_left_img_right_text)

if layout in ("full_bg", "center_text"):
    fn(prs, img_path, title, subtitle=bullets, theme=THEME)
else:
    fn(prs, img_path, title, bullets, theme=THEME)

prs.save("presentation.pptx")

  1. Post-Assembly Checks

After saving, verify:

  • File size is reasonable (images embedded increase size)

  • Slide count matches storyboard row count

  • Report the output filename and size to the user

Font Notes

  • python-pptx embeds font name references, not font files

  • Safe cross-platform fonts: Arial, Calibri, Helvetica

  • For CJK content: specify "Microsoft YaHei", "Noto Sans CJK SC", or "PingFang SC"

  • If the user specifies a custom font, check availability first

Tips

  • Always use prs.slide_layouts[6] (blank layout) for full control

  • Images should match the slide aspect ratio to avoid stretching

  • For dark themes, use light text; for light themes, use dark text

  • Keep the overlay opacity between 30%–50% for readability over images

  • Speaker notes: slide.notes_slide.notes_text_frame.text = notes

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

code-to-diagram

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

biopython

No summary provided by upstream source.

Repository SourceNeeds Review
General

skill-updater

No summary provided by upstream source.

Repository SourceNeeds Review