# Ppt Slide Generator

> >

- **Type:** Skill
- **Install:** `agentstack add skill-crealwork-ai-marketing-kit-ppt-slide-generator`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [crealwork](https://agentstack.voostack.com/s/crealwork)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [crealwork](https://github.com/crealwork)
- **Source:** https://github.com/crealwork/ai-marketing-kit/tree/main/skills/ppt-slide-generator

## Install

```sh
agentstack add skill-crealwork-ai-marketing-kit-ppt-slide-generator
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# PPT Slide Generator (프레젠테이션 슬라이드 자동 생성기)

This skill creates professional HTML-based presentation slides (1920x1080, 16:9)
through a complete pipeline: theme selection, topic confirmation, deep research,
2-stage content review, AI image generation, and HTML generation with PDF download.

> **Output format fork — decide FIRST:**
> - Default deliverable (HTML → PDF): follow the steps below.
> - **Editable Google Slides deck** requested (구글 슬라이드, Google Slides,
>   collaborative deck, redesigning an existing Slides deck): read
>   `google-slides-export.md` in this skill folder and build with python-pptx
>   instead of HTML. Content/research/review steps (0-3.5) still apply;
>   Step 4 (HTML) and the download button do not.
> - Redesigning an existing deck from a PDF: skip research/outline gates —
>   extract content + page renders with PyMuPDF (`fitz`), keep copy 1:1,
>   redesign the template only.
> - Both at once (PDF redesign → Google Slides): PDF-redesign rules govern
>   content (skip gates, copy 1:1); Google Slides rules govern build/delivery.

---

## Step 0: Theme Selection + Topic Confirmation

First, check available themes:

1. Read the list of `.md` files in the `themes/` folder (relative to this SKILL.md)
2. Present the available themes to the user and ask them to select one
3. Read the selected theme file to load the design system

Then confirm:
1. **Topic** — What is the presentation about?
2. **Purpose** — Business presentation or educational/lecture material?
3. **Slide count preference** — Short (5-8) / Normal (10-15) / Deep (16-25)
4. **Presenter info** — Name, date, contact (for cover and ending slides)

Don't over-interview. Get the topic confirmed and move to research quickly.

---

## Step 1: Deep Research

Once topic is confirmed, do thorough web research. Research quality directly
determines whether the presentation is valuable or just looks nice.

### What to search for (5-10 searches)

- Key statistics and recent numbers
- Latest trends and market changes
- Comparable data points for before/after or vs. comparisons
- Visualizable data (percentages, growth rates, rankings)
- Real examples or quotable insights
- Source-backed facts (not vague claims)

### Organize your research

After researching, compile:

```
Topic summary (1 line)
Key data points (5-8)
Chart-ready data (numbers that can become visuals)
Memorable example or quote
Key takeaways (3-5)
```

---

## Step 1.5: Narrative Strategy

Design the presentation arc before writing the outline.

### Opening (slides 1-3) must:
1. **Hook the audience** — a bold claim, surprising statistic, or provocative question
2. **Establish the problem/opportunity** clearly
3. **Preview the value** — "by the end of this presentation, you will..."

### Middle (core slides):
- Build logical progression — each slide sets up the next
- Alternate between data/evidence slides and insight/implication slides
- Place the strongest data point at the climax, not the beginning

### Closing (last 2-3 slides):
- Summarize key takeaways (no new information)
- Clear call-to-action or next steps
- End with impact — not a whimper

---

## Step 2: Outline Review (Gate 1)

Present the slide outline to the user for review:

```
Slide 1 [Cover]: Title — "..."
Slide 2 [Agenda]: 3 sections
Slide 3 [Section Divider]: Section 1 — "..."
Slide 4 [Body-Text]: Key message — "..."
Slide 5 [Body-Data]: Chart — "..."
...
Slide N [Ending]: Thank you + contact
```

**Revision loop:** Wait for explicit approval before proceeding.
- If the user requests changes: apply revisions and re-present the updated outline
- Repeat until the user approves
- If the user wants to change the topic direction significantly, go back to Step 1

Only proceed to Gate 2 after explicit approval.

---

## Step 3: Detailed Content Review (Gate 2)

Present full text/data for each slide:

```
Slide 4 [Body-Text]
  Title: "AI 마케팅 도입률 현황"
  Bullet 1: "2026년 기준 글로벌 기업 72%가 ..."
  Bullet 2: "한국 시장은 ..."
  Source: McKinsey 2026 Report
```

Present ALL slides with their complete content. Wait for explicit approval.

**Revision loop:**
- If the user requests changes: apply revisions to specific slides and re-present only the changed slides
- Repeat until the user approves
- If structural changes are needed (add/remove/reorder slides), go back to Gate 1

Only proceed to image generation after explicit approval.

---

## Step 3.5: Image Generation (Optional)

After content approval, before HTML generation:

1. Analyze each slide and identify where images would enhance the presentation
2. Present image prompt proposals to the user:
   ```
   Slide 1 [Cover]: Background — "Professional abstract geometric pattern in navy and gold tones"
   Slide 6 [Quote]: Illustration — "Business team collaborating with AI dashboard"
   → Approve / Edit prompt / Skip all images
   ```
3. For approved prompts, generate images using Gemini API
4. Embed generated images as base64 `data:image/png;base64,...` in the HTML

### Gemini API Call

**Model:** `gemini-3.1-flash-image-preview`

**Authentication:** Uses `GOOGLE_AI_API_KEY` from global environment.

**API pattern:**

```python
from google import genai
from google.genai import types

client = genai.Client()

response = client.models.generate_content(
    model="gemini-3.1-flash-image-preview",
    contents="Create a professional presentation background: [prompt]",
    config=types.GenerateContentConfig(
        response_modalities=['TEXT', 'IMAGE'],
        image_config=types.ImageConfig(
            aspect_ratio="16:9",  # for slide backgrounds
            image_size="2K"
        )
    )
)

for part in response.parts:
    if part.inline_data is not None:
        image = part.as_image()
        image.save("slide-bg.png")
```

**Aspect ratios:**
- Slide backgrounds: `16:9`
- Inline illustrations: `4:3` or `1:1`

### Error Handling

All image generation failures are non-blocking:
- API timeout → skip image, log warning
- Rate limit → wait 5s, retry once, then skip
- Invalid response → skip, proceed without image
- User can skip image generation entirely

If any image fails, note which slides were intended to have images so the user
can retry later.

---

## Step 4: HTML Generation

Read the selected theme file and generate a single HTML file containing all slides.

### Critical Rules

**Rule 1: Static HTML only for slides.**
Every `` must exist directly in the ``. Never generate
slides with JavaScript. JS-generated slides won't render in preview.

**Rule 2: JavaScript is only for the download button and slide navigator.**
No JS for slide rendering.

**Rule 3: Use Pretendard font via CDN.**

```html

```

```css
:root {
  --font-main: 'Pretendard Variable', 'Pretendard', 'Apple SD Gothic Neo',
               'Malgun Gothic', 'NanumGothic', sans-serif;
}

* {
  word-break: keep-all;
  overflow-wrap: break-word;
}
```

`word-break: keep-all` is mandatory for Korean. Without it, the browser breaks lines
in the middle of words (character by character), which looks broken. `keep-all` forces
line breaks only at spaces, keeping Korean phrases intact.

**Rule 4: Deliver the HTML file directly.** One HTML file, no separate viewer.

**Rule 5: Photos and headshots — use `` with `object-fit` (vector-first path).**

The PRIMARY export path is now Playwright (vector PDF, see Rule 6). Playwright honors `object-fit: cover` perfectly. The html2canvas raster path is fallback-only.

Use `` for photos that need to fit a fixed-ratio container:

```html

```

```css
.speaker-photo { width:520px; height:680px; overflow:hidden; }
.headshot { width:100%; height:100%; object-fit:cover; object-position:center top; }
```

**Why not `background-image`:** Chrome's Ctrl+P "Background graphics" checkbox is OFF by default — `` with background-image disappears from the printed PDF. `` always renders. This was a real Dave Kim deck bug.

The old html2canvas-friendly background-image pattern is still acceptable as a fallback only if you can't move to Playwright export.

**Rule 6: Use Playwright for PDF export — html2canvas/Ctrl+P are fallbacks.**

Playwright `page.pdf({width:'1920px', height:'1080px'})` is the only method that guarantees exact 16:9 page size regardless of user environment. Chrome's Ctrl+P UI ignores `@page` CSS when user picks Letter/Legal manually. Always create `render-pdf.py` alongside the deck HTML:

```python
"""Render the deck to a 1920×1080 landscape PDF via Playwright."""
import asyncio
from pathlib import Path
from playwright.async_api import async_playwright

HERE = Path(__file__).parent
HTML = HERE / "deck.html"  # adjust to actual filename
OUT = Path.home() / "Downloads" / "deck.pdf"  # adjust

async def main():
    async with async_playwright() as pw:
        browser = await pw.chromium.launch()
        ctx = await browser.new_context(viewport={"width": 1920, "height": 1080})
        page = await ctx.new_page()
        await page.goto(HTML.as_uri(), wait_until="networkidle")
        await page.evaluate("document.fonts.ready")  # wait for web fonts (FOUT guard)
        await page.pdf(
            path=str(OUT), width="1920px", height="1080px",
            print_background=True,
            margin={"top":"0","right":"0","bottom":"0","left":"0"},
            prefer_css_page_size=False,
        )
        await browser.close()
    print(f"PDF written to {OUT} ({OUT.stat().st_size:,} bytes)")

asyncio.run(main())
```

**Auto-open after creation/modification (MANDATORY):**
After running render-pdf.py, always chain `start "" ""` so the user sees the result immediately. Same for the source HTML during iteration:

```bash
python render-pdf.py && start "" "%USERPROFILE%\Downloads\deck.pdf"
```

If the PDF is locked by an open viewer (PermissionError), fall back to V{N+1}.pdf naming (e.g., V4 → V5).

**Print CSS still required** for the rare Ctrl+P fallback path:

```css
@media print {
  @page { size: 1920px 1080px; margin: 0; }
  html, body {
    background: #fff !important; margin: 0; padding: 0;
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
  .slide-nav, .download-section { display: none !important; }
  .deck { padding: 0 !important; }
  .slide {
    margin: 0 !important; box-shadow: none !important;
    page-break-after: always; page-break-inside: avoid; break-after: page;
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
  .slide:last-child { page-break-after: auto; break-after: auto; }
}
```

`print-color-adjust:exact` prevents Chrome's ink-saver from stripping accent colors. The html2canvas download button stays as a tertiary fallback in the download section.

### Slide Dimensions

Each slide: `width: 1920px; height: 1080px` (16:9 ratio).

```css
.slide {
  width: 1920px;
  height: 1080px;
  padding: 80px 100px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  box-sizing: border-box;
  overflow: hidden;
  position: relative;
}
```

`position: relative` is required so that absolutely-positioned children (slide-number,
slide-footer) are positioned relative to the slide, not the viewport.

### Font Size Minimums

| Element | Size | Weight |
|---------|------|--------|
| Slide title | 48px ~ 64px | 800 |
| Subtitle / section heading | 36px ~ 44px | 700 |
| Body text | 28px ~ 34px | 400-500 |
| Number callouts | 72px ~ 120px | 900 |
| Caption / source | 20px ~ 24px | 400 |
| Slide number | 18px ~ 20px | 400 |

These are MINIMUM sizes. If a slide has less content, scale UP the font sizes
to fill the space rather than leaving empty areas.

### Heading Letter Spacing

All headings (h1, h2) must have `letter-spacing: 0.02em` to prevent tight kerning
at large sizes. Add this as a global CSS rule:

```css
h1, h2 { letter-spacing: 0.02em; }
```

### Spacing Rules

- Slide padding: `80px ~ 100px`
- Content block gap: `40px+`
- Bullet point spacing: `32px+`

### Data Visualization

For stable PNG rendering, do NOT use external chart libraries (Chart.js, etc.).

**Allowed methods:**
- Inline SVG
- HTML/CSS div-based charts (width percentages, flexbox bars, etc.)

**Recommended chart types (mix at least 2-3 per presentation):**
- Donut/pie charts (SVG)
- Bar charts (CSS divs)
- Comparison bars (side-by-side CSS)
- Progress bars
- Big number + label blocks
- Timeline/process flow (CSS flexbox)

**Checklist:**
- Numbers in the chart match the text
- Percentages add up correctly
- Sources are attributed
- Each data slide focuses on one insight

### Slide Numbering

All slides except Cover and Ending/CTA display a slide number.
Position: bottom-right. Format: `N / Total` (e.g., `3 / 15`).

### Slide Navigator (Browser Preview)

Since 1920x1080 slides are larger than typical browser viewports, add a navigation
helper at the top of the HTML:

```html

  Slide Navigator
  1 2 ...

```

Each slide gets `id="slide-N"`. The nav bar is fixed at top and is NOT inside
any `.slide` element, so it's automatically excluded from PNG capture.

```css
.slide-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background: #333;
  color: #fff;
  padding: 8px 16px;
  z-index: 100000;
  display: flex;
  gap: 8px;
  align-items: center;
  font-family: var(--font-main);
  font-size: 14px;
}
.slide-nav a {
  color: #fff;
  background: #555;
  padding: 4px 10px;
  border-radius: 4px;
  text-decoration: none;
}
.slide-nav a:hover { background: #777; }
```

### Year References

Always use the current year. Never hardcode a specific year.

### Download Button (PDF)

Add a download section at the bottom of the HTML (after all slides, before ``).

**Libraries (EXACT CDN URLs — copy exactly, do NOT change versions):**

```html

```

CRITICAL:
- Use html2canvas `@1.4.1` + jsPDF `@2.5.2`. Access jsPDF via `window.jspdf.jsPDF`.
- Do NOT use dom-to-image-more — it depends on viewport size and breaks in web/narrow
  browser environments where viewport  0) pdf.addPage([1920, 1080], 'landscape');
      pdf.addImage(imgData, 'JPEG', 0, 0, 1920, 1080);
    }

    status.textContent = 'PDF 저장 중...';
    pdf.save('{topic}-presentation.pdf');
    status.textContent = '✓ 다운로드 완료!';
  } catch (error) {
    console.error('Download failed:', error);
    status.textContent = '오류: ' + error.message;
  } finally {
    btn.disabled = false;
  }
});
```

Replace `'{topic}-presentation.pdf'` with the actual sanitized topic name
(lowercase, hyphens, no special characters). Example: `'ai-marketing-presentation.pdf'`

**File naming:** `{topic}-presentation.pdf`

**Download Section UI (MANDATORY pattern):**

The download area must prominently promote **Ctrl/Cmd+P** as the best-quality path.
The html2canvas download button is shown as a smaller, de-emphasized fallback next
to it. Never lead with the rasterized button.

```html

  
    ★ Recommended — Best Quality
    Press Ctrl+P → “Save as PDF”
    Windows: Ctrl+P &nbsp;·&nbsp; Mac: Cmd+PText stays vector-perfect. Use this option.
  
  
    Or — rasterized fallback
    Download PDF
    
  

```

```css
.download-section {
  max-width: 1400px; margin: 60px auto 80px; text-align: center;
  color: #fff; font-family: var(--font-main, 'Inter', sans-serif);
  display: flex; gap: 24px; align-items: stretch; justify-content: center;
  flex-wrap: wrap;
}
.best-option {
  flex: 1 1 520px; max-width: 680px;
  background: #1a1a1a; border: 2px solid var(--accent, #3B82F6);
  padding: 36px 44px; border-radius: 10px;
  display: flex; flex-direction: column; justify-content: center;
}
.best-label {
  color: var(--accent, #3B82F6); font-size: 12px; font-weight: 700;
  letter-spacing: 4px; text-transform: uppercase; margin-bottom: 14px;
}
.best-title {
  color: #fff; font-size: 30px; font-weight: 600;
  margin-bottom: 10px; line-height: 1.2;
}
.best-title kbd {
  background: #fff; color: #000; padding: 6px 14px;
  border-radius: 6px; font-family: 'SF Mono', 'Monaco', monospace;
  font-size: 22px; margin: 0 4px; font-weight: 700;
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.35);
}
.best-note { color: #bbb;

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [crealwork](https://github.com/crealwork)
- **Source:** [crealwork/ai-marketing-kit](https://github.com/crealwork/ai-marketing-kit)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-crealwork-ai-marketing-kit-ppt-slide-generator
- Seller: https://agentstack.voostack.com/s/crealwork
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
