---
name: pptx
description: "Use this skill whenever the user wants to create, edit, read, or analyze PowerPoint presentations (.pptx files). Triggers include: any mention of 'PowerPoint', 'pptx', 'presentation', 'slide deck', 'slides', or requests to produce pitch decks, proposal decks, product overviews, or business reports in slide format. Also use when extracting text from .pptx files, rearranging slides, applying themes/layouts, adding charts or tables to slides, or converting content into a polished presentation. If the user asks for a 'proposal', 'deck', 'pitch', '보고서', '제안서', '발표자료', or similar deliverable as a .pptx file, use this skill. Do NOT use for Word documents, spreadsheets, PDFs, or Google Slides tasks unrelated to .pptx generation."
license: Proprietary. LICENSE.txt has complete terms
---

# PPTX 생성, 편집, 분석

## Overview

.pptx 파일은 ZIP 아카이브 형태로, 내부에 XML 파일이 포함되어 있습니다(OOXML 표준).
python-pptx 라이브러리를 우선 사용하며, 복잡한 XML 편집이 필요한 경우 unzip/rezip 방식으로 직접 OOXML 조작이 가능합니다.

## Quick Reference

| 작업 | 방법 |
|------|------|
| 텍스트 추출/분석 | `python -m markitdown file.pptx` 또는 python-pptx로 슬라이드 순회 |
| 새 프레젠테이션 생성 | python-pptx — 아래 Quick Start 참조 |
| 기존 파일 편집 | python-pptx로 로드 후 편집, 또는 unzip → XML 편집 → rezip |
| 슬라이드 미리보기 | LibreOffice로 PDF 변환 후 pdftoppm으로 이미지 추출 |
| 슬라이드 레이아웃 확인 | `prs.slide_layouts` 열거 |

---

## 설치 요구사항

```bash
pip install python-pptx
pip install markitdown   # 텍스트 추출 선택적 설치
```

---

## Quick Start

```python
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN

# 새 프레젠테이션 생성
prs = Presentation()

# 슬라이드 크기: 와이드스크린 16:9 (기본값)
prs.slide_width  = Inches(13.33)
prs.slide_height = Inches(7.5)

# 슬라이드 추가 (레이아웃 0 = 제목 슬라이드)
slide_layout = prs.slide_layouts[0]
slide = prs.slides.add_slide(slide_layout)

# 제목/부제목 입력
slide.shapes.title.text = "2024년 보험 실적 보고"
slide.placeholders[1].text = "개발6팀 | 2024-12-31"

prs.save("output.pptx")
```

---

## 슬라이드 레이아웃

python-pptx의 기본 슬라이드 레이아웃 인덱스:

| 인덱스 | 레이아웃 이름 | 주요 용도 |
|--------|--------------|-----------|
| 0 | Title Slide | 표지 슬라이드 |
| 1 | Title and Content | 본문 슬라이드 |
| 2 | Title and Two Content | 2단 비교 슬라이드 |
| 5 | Title Only | 제목만 있는 슬라이드 |
| 6 | Blank | 완전 빈 슬라이드 |

```python
# 사용 가능한 레이아웃 전체 출력
prs = Presentation()
for i, layout in enumerate(prs.slide_layouts):
    print(f"{i}: {layout.name}")
```

---

## 텍스트 및 도형 추가

### 텍스트 박스

```python
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN

slide = prs.slides.add_slide(prs.slide_layouts[6])  # Blank

# 텍스트 박스 추가: add_textbox(left, top, width, height)
txBox = slide.shapes.add_textbox(Inches(0.5), Inches(1.0), Inches(12.0), Inches(1.5))
tf = txBox.text_frame
tf.word_wrap = True

p = tf.paragraphs[0]
p.alignment = PP_ALIGN.LEFT
run = p.add_run()
run.text = "손해율 분석 결과"
run.font.size = Pt(28)
run.font.bold = True
run.font.color.rgb = RGBColor(0x1F, 0x35, 0x64)  # 네이비
run.font.name = "맑은 고딕"
```

### 한국어 폰트 설정

```python
from pptx.oxml.ns import qn
from lxml import etree

def set_korean_font(run, korean_font="맑은 고딕", latin_font="Calibri"):
    """런(run)에 한국어/영문 폰트를 각각 설정합니다."""
    rPr = run._r.get_or_add_rPr()
    # 한국어 폰트 (동아시아)
    eastAsian = etree.SubElement(rPr, qn('a:ea'))
    eastAsian.set('typeface', korean_font)
    # 영문 폰트
    latin = rPr.find(qn('a:latin'))
    if latin is None:
        latin = etree.SubElement(rPr, qn('a:latin'))
    latin.set('typeface', latin_font)
```

---

## 표 추가

```python
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor

slide = prs.slides.add_slide(prs.slide_layouts[6])

# 표 추가: add_table(rows, cols, left, top, width, height)
rows, cols = 5, 3
table = slide.shapes.add_table(rows, cols, Inches(0.5), Inches(1.5), Inches(12.0), Inches(4.0)).table

# 헤더 행 서식
headers = ["보험 상품", "계약건수", "손해율(%)"]
for col_idx, header_text in enumerate(headers):
    cell = table.cell(0, col_idx)
    cell.text = header_text
    cell.fill.solid()
    cell.fill.fore_color.rgb = RGBColor(0x1F, 0x35, 0x64)  # 헤더 배경: 네이비
    para = cell.text_frame.paragraphs[0]
    run = para.runs[0]
    run.font.bold = True
    run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)  # 흰색 글씨
    run.font.size = Pt(13)

# 데이터 행 입력
data = [
    ("실손의료보험", "120,450", "78.3"),
    ("자동차보험",   "89,200",  "82.1"),
    ("종신보험",     "45,100",  "62.5"),
    ("화재보험",     "30,800",  "55.0"),
]
for row_idx, (product, contracts, ratio) in enumerate(data, start=1):
    table.cell(row_idx, 0).text = product
    table.cell(row_idx, 1).text = contracts
    table.cell(row_idx, 2).text = ratio
    # 짝수 행 줄무늬
    if row_idx % 2 == 0:
        for col_idx in range(cols):
            table.cell(row_idx, col_idx).fill.solid()
            table.cell(row_idx, col_idx).fill.fore_color.rgb = RGBColor(0xE8, 0xEF, 0xF7)
```

---

## 도형 및 선

```python
from pptx.util import Inches, Pt, Emu
from pptx.enum.shapes import MSO_SHAPE_TYPE
from pptx.dml.color import RGBColor

# 사각형 추가
shape = slide.shapes.add_shape(
    MSO_AUTO_SHAPE_TYPE.ROUNDED_RECTANGLE,
    Inches(0.5), Inches(0.5), Inches(3.0), Inches(1.0)
)
shape.fill.solid()
shape.fill.fore_color.rgb = RGBColor(0x2E, 0x75, 0xB6)
shape.line.color.rgb = RGBColor(0x1F, 0x35, 0x64)

# 텍스트 삽입
tf = shape.text_frame
tf.text = "핵심 지표"
tf.paragraphs[0].runs[0].font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
tf.paragraphs[0].runs[0].font.bold = True
```

---

## 이미지 삽입

```python
from pptx.util import Inches

# 이미지 파일에서 삽입
pic = slide.shapes.add_picture(
    "logo.png",
    left=Inches(11.5), top=Inches(0.1),
    width=Inches(1.5), height=Inches(0.6)
)

# 이미지 파일 없이 BytesIO로 삽입 (matplotlib 차트 등)
import io
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(["1Q", "2Q", "3Q", "4Q"], [120, 135, 118, 142])
ax.set_title("분기별 계약 건수 (천 건)")

buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
buf.seek(0)
slide.shapes.add_picture(buf, Inches(0.5), Inches(2.0), Inches(6.0), Inches(4.0))
plt.close(fig)
```

---

## 기존 프레젠테이션 읽기 / 편집

### 텍스트 추출

```python
from pptx import Presentation

prs = Presentation("existing.pptx")

for slide_num, slide in enumerate(prs.slides, start=1):
    print(f"\n--- 슬라이드 {slide_num} ---")
    for shape in slide.shapes:
        if shape.has_text_frame:
            for para in shape.text_frame.paragraphs:
                print(para.text)
```

### markitdown으로 빠른 텍스트 추출

```bash
# 설치
pip install markitdown

# 실행
python -m markitdown presentation.pptx
```

### 특정 슬라이드 텍스트 교체

```python
prs = Presentation("template.pptx")

for slide in prs.slides:
    for shape in slide.shapes:
        if not shape.has_text_frame:
            continue
        for para in shape.text_frame.paragraphs:
            for run in para.runs:
                # 플레이스홀더 치환
                run.text = run.text.replace("{{회사명}}", "한국보험주식회사")
                run.text = run.text.replace("{{날짜}}", "2024-12-31")

prs.save("output.pptx")
```

### OOXML 직접 편집 (고급)

```python
import zipfile, shutil, os

# 1. 언패킹
shutil.unpack_archive("presentation.pptx", "unpacked/", "zip")

# 2. XML 편집 (예: ppt/slides/slide1.xml)
# Edit 도구로 XML 직접 수정

# 3. 리패킹
with zipfile.ZipFile("output.pptx", "w", zipfile.ZIP_DEFLATED) as zf:
    for root, dirs, files in os.walk("unpacked/"):
        for file in files:
            file_path = os.path.join(root, file)
            arcname = os.path.relpath(file_path, "unpacked/")
            zf.write(file_path, arcname)
```

---

## 슬라이드 미리보기 생성

```bash
# LibreOffice로 PDF 변환
libreoffice --headless --convert-to pdf presentation.pptx

# PDF를 이미지로 변환 (슬라이드별)
pdftoppm -jpeg -r 150 presentation.pdf slide
# → slide-1.jpg, slide-2.jpg, ...
```

---

## 디자인 원칙

### 컬러 팔레트 (보험/금융 표준)

| 용도 | 색상 이름 | HEX | RGB |
|------|-----------|-----|-----|
| 제목/강조 | 딥 네이비 | `#1F3564` | 31, 53, 100 |
| 주요 액센트 | 코퍼레이트 블루 | `#2E75B6` | 46, 117, 182 |
| 보조 액센트 | 라이트 블루 | `#BDD7EE` | 189, 215, 238 |
| 긍정 지표 | 그린 | `#375623` | 55, 86, 35 |
| 경고 지표 | 오렌지 | `#C55A11` | 197, 90, 17 |
| 위험 지표 | 레드 | `#C00000` | 192, 0, 0 |
| 배경 | 화이트 | `#FFFFFF` | 255, 255, 255 |
| 줄무늬 행 | 라이트 그레이 | `#E8EFF7` | 232, 239, 247 |
| 본문 텍스트 | 다크 그레이 | `#404040` | 64, 64, 64 |

### 타이포그래피

| 요소 | 폰트 | 크기 | 굵기 |
|------|------|------|------|
| 슬라이드 제목 | 맑은 고딕 | 32–40pt | Bold |
| 소제목 | 맑은 고딕 | 20–24pt | Bold |
| 본문 | 맑은 고딕 | 14–18pt | Regular |
| 캡션/주석 | 나눔고딕 | 10–12pt | Regular |
| 영문/숫자 | Calibri / Arial | — | — |

### 레이아웃 원칙

- **여백 (Margin)**: 슬라이드 상하좌우 최소 0.5인치 여백 유지
- **제목 위치**: 상단 0.3~0.5인치 고정, 높이 약 1.0인치
- **콘텐츠 영역**: 제목 아래 1.3인치부터 하단 0.5인치까지
- **1슬라이드 1메시지**: 슬라이드당 핵심 메시지 1개
- **텍스트 최소화**: 슬라이드당 텍스트 6줄 이하 권장
- **정렬**: 도형/텍스트박스는 그리드 기준 정렬 (Inches 단위 정수 또는 0.5 단위)

---

## 보험/금융 도메인 예시: 상품 제안서 슬라이드 덱

```python
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN

NAVY   = RGBColor(0x1F, 0x35, 0x64)
BLUE   = RGBColor(0x2E, 0x75, 0xB6)
WHITE  = RGBColor(0xFF, 0xFF, 0xFF)
GRAY   = RGBColor(0x40, 0x40, 0x40)

def add_title_slide(prs, title, subtitle):
    slide = prs.slides.add_slide(prs.slide_layouts[0])
    slide.shapes.title.text = title
    slide.shapes.title.text_frame.paragraphs[0].runs[0].font.color.rgb = NAVY
    slide.shapes.title.text_frame.paragraphs[0].runs[0].font.size = Pt(36)
    slide.placeholders[1].text = subtitle
    return slide

def add_content_slide(prs, title, bullet_points):
    slide = prs.slides.add_slide(prs.slide_layouts[1])
    slide.shapes.title.text = title
    tf = slide.placeholders[1].text_frame
    tf.text = bullet_points[0]
    for point in bullet_points[1:]:
        p = tf.add_paragraph()
        p.text = point
        p.level = 0
    return slide

prs = Presentation()
prs.slide_width  = Inches(13.33)
prs.slide_height = Inches(7.5)

add_title_slide(prs,
    title="실손의료보험 갱신 제안서",
    subtitle="한국보험(주) 기업영업팀 | 2024-12-31"
)

add_content_slide(prs,
    title="주요 변경 사항",
    bullet_points=[
        "보험료: 월 85,000원 → 91,000원 (7% 인상)",
        "자기부담금 비율: 20% → 30% (표준형 기준)",
        "비급여 항목 급여 전환에 따른 보장 범위 확대",
        "실손24 청구 시스템 연동으로 온라인 청구 간소화",
    ]
)

prs.save("실손보험_갱신제안서.pptx")
```

---

## 워크플로우 요약

```
요청 수신
   ↓
파일 존재 여부 확인
   ├─ 신규 생성 → python-pptx로 슬라이드 구성 → 저장
   └─ 기존 편집 → Presentation() 로드
                    ├─ 간단 편집: shape/run 직접 수정 → 저장
                    └─ XML 수준 편집: unzip → XML 수정 → rezip
```

---

## 주의사항

- python-pptx는 `.ppt` (구형 바이너리 포맷) 미지원. `.pptx`만 지원
- 기존 파일 편집 시 폰트/테마는 원본 유지됨 (run.font 재설정 시 덮어씀)
- 슬라이드 레이아웃 인덱스는 템플릿 파일에 따라 달라짐 — 항상 `prs.slide_layouts` 목록 확인
- LibreOffice 변환은 한국어 폰트가 서버에 설치되어 있어야 정확히 렌더링됨
