| name | mck-ppt-design |
| description | Create professional, consultant-grade PowerPoint presentations from scratch using python-pptx with McKinsey-style design. Use when user asks to create slides, pitch decks, business presentations, strategy decks, quarterly reviews, board meeting slides, or any professional PPTX. Generates clean, flat-design presentations with 70 layout patterns across 12 categories (structure, data, framework, comparison, narrative, timeline, team, charts, images, advanced viz, dashboards, visual storytelling), consistent typography, zero file-corruption issues, BLOCK_ARC native shapes for circular charts (donut, pie, gauge), and production-hardened guard rails for spacing, overflow, legend consistency, title style uniformity, dynamic sizing for variable-count layouts, and chart rendering. |
McKinsey PPT Design Framework
Version: 1.10.4 ยท License: Apache-2.0 ยท Author: likaku
Required tools: Read, Write, Bash ยท Requires: python3, pip
Overview
This skill encodes the complete design specification for professional business presentations โ a consultant-grade PowerPoint framework based on McKinsey design principles. It includes:
- 70 layout patterns across 12 categories (structure, data, framework, comparison, narrative, timeline, team, charts, images, advanced viz, dashboards, visual storytelling)
- Color system and strict typography hierarchy
- Python-pptx code patterns ready to copy and customize
- Three-layer defense against file corruption (zero
p:style leaks)
- Chinese + English font handling (KaiTi / Georgia / Arial)
- Image placeholder system for image-containing layouts (v1.8)
- BLOCK_ARC native shapes for charts โ donut, pie, gauge rendered with 3-4 shapes instead of hundreds of blocks, 60-80% smaller files (v2.0)
- Production Guard Rails โ 9 mandatory rules including spacing/overflow protection, legend color consistency, title style uniformity, axis label centering, dynamic sizing, BLOCK_ARC chart rendering (v1.9+v2.0)
- Code Efficiency guidelines โ variable reuse patterns, constant extraction, loop optimization for faster generation (v1.9)
All specifications have been refined through iterative production feedback to ensure visual consistency, professional polish, and zero-defect output.
When to Use This Skill
Use this skill when users ask to:
- Create presentations โ pitch decks, strategy presentations, quarterly reviews, board meeting slides, consulting deliverables, project proposals, business plans
- Generate slides programmatically โ using python-pptx to produce .pptx files from scratch
- Apply professional design โ McKinsey / BCG / Bain consulting style, clean flat design, no shadows or gradients
- Build specific slide types โ cover pages, data dashboards, 2x2 matrices, timelines, funnels, team introductions, executive summaries, comparison layouts
- Fix PPT issues โ file corruption ("needs repair"), shadow/3D artifacts, inconsistent fonts, Chinese text rendering problems
- Maintain design consistency โ unified color palette, font hierarchy, spacing, and line treatments across all slides
Core Design Philosophy
McKinsey Design Principles
-
Extreme Minimalism - Remove all non-essential visual elements
- No color blocks unless absolutely necessary
- Lines: thin, flat, no shadows or 3D effects
- Shapes: simple, clean, no gradients
- Text: clear hierarchy, maximum readability
-
Consistency - Repeat visual language across all slides
- Unified color palette (navy + cyan + grays)
- Consistent font sizes and weights for same content types
- Aligned spacing and margins
- Matching line widths and styles
-
Hierarchy - Guide viewer through information
- Title bar (22pt) โ Sub-headers (18pt) โ Body (14pt) โ Details (9pt)
- Navy for primary elements, gray for secondary, black for divisions
- Visual weight through bold, color, size (not through effects)
-
Flat Design - No 3D, shadows, or gradients
- Pure solid colors only
- All lines are simple strokes with no effects
- Shapes have no shadow or reflection effects
- Circles are solid fills, not 3D spheres
Design Specifications
Color Palette
All colors in RGB format for python-pptx:
| Color Name | Hex | RGB | Usage | Notes |
|---|
| NAVY | #051C2C | (5, 28, 44) | Primary, titles, circles | Corporate, formal tone |
| CYAN | #00A9F4 | (0, 169, 244) | Originally used in v1 | DEPRECATED - Use NAVY for consistency |
| WHITE | #FFFFFF | (255, 255, 255) | Backgrounds, text | On navy backgrounds only |
| BLACK | #000000 | (0, 0, 0) | Lines, text separators | For clarity and contrast |
| DARK_GRAY | #333333 | (51, 51, 51) | Body text, descriptions | Main content text |
| MED_GRAY | #666666 | (102, 102, 102) | Secondary text, labels | Softer tone than DARK_GRAY |
| LINE_GRAY | #CCCCCC | (204, 204, 204) | Light separators, table rows | Table separators only |
| BG_GRAY | #F2F2F2 | (242, 242, 242) | Background panels | Takeaway/highlight areas |
Key Rule: Use navy (#051C2C) everywhere, especially for:
- All circle indicators (A, B, C, 1, 2, 3)
- All action titles
- All primary section headers
- All TOC highlight colors
Accent Colors (for multi-item differentiation)
When a slide contains 3 or more parallel items (e.g., comparison cards, pillar frameworks, multi-category overviews), use these accent colors to create visual distinction between items. Without accent colors, parallel items become visually indistinguishable.
| Accent Name | Hex | RGB | Paired Light BG | Usage |
|---|
| ACCENT_BLUE | #006BA6 | (0, 107, 166) | #E3F2FD | First item accent |
| ACCENT_GREEN | #007A53 | (0, 122, 83) | #E8F5E9 | Second item accent |
| ACCENT_ORANGE | #D46A00 | (212, 106, 0) | #FFF3E0 | Third item accent |
| ACCENT_RED | #C62828 | (198, 40, 40) | #FFEBEE | Fourth item / warning |
Accent Color Rules:
- Use accent colors for: card top accent borders (thin 0.06" rect), circle labels (
add_oval() bg param), section sub-headers (font_color)
- Use paired light BG for: card background fills only
- Body text inside cards ALWAYS remains DARK_GRAY (#333333)
- NAVY remains the primary color for single-focus elements (one card, one stat, cover title)
- Use accent colors ONLY when the slide has 3+ parallel items that need visual distinction
- The fourth item (D) can use NAVY instead of ACCENT_RED if red feels inappropriate for the content
# Accent color constants
ACCENT_BLUE = RGBColor(0x00, 0x6B, 0xA6)
ACCENT_GREEN = RGBColor(0x00, 0x7A, 0x53)
ACCENT_ORANGE = RGBColor(0xD4, 0x6A, 0x00)
ACCENT_RED = RGBColor(0xC6, 0x28, 0x28)
LIGHT_BLUE = RGBColor(0xE3, 0xF2, 0xFD)
LIGHT_GREEN = RGBColor(0xE8, 0xF5, 0xE9)
LIGHT_ORANGE = RGBColor(0xFF, 0xF3, 0xE0)
LIGHT_RED = RGBColor(0xFF, 0xEB, 0xEE)
Typography System
Font Families
English Headers: Georgia (serif, elegant)
English Body: Arial (sans-serif, clean)
Chinese (ALL): KaiTi (ๆฅทไฝ, traditional brush style)
(fallback: SimSun ๅฎไฝ)
Critical Implementation:
def set_ea_font(run, typeface='KaiTi'):
"""Set East Asian font for Chinese text"""
rPr = run._r.get_or_add_rPr()
ea = rPr.find(qn('a:ea'))
if ea is None:
ea = rPr.makeelement(qn('a:ea'), {})
rPr.append(ea)
ea.set('typeface', typeface)
Every paragraph with Chinese text MUST apply set_ea_font() to all runs.
Font Size Hierarchy
| Size | Type | Examples | Notes |
|---|
| 44pt | Cover Title | "้กน็ฎๅ็งฐ" | Cover slide only, Georgia |
| 28pt | Section Header | "็ฎๅฝ" (TOC title) | Largest body content, Georgia |
| 24pt | Subtitle | Tagline on cover | Cover slide only |
| 22pt | Action Title | Slide title bars | Main content titles, bold, Georgia |
| 18pt | Sub-Header | Column headers, section names | Supporting titles |
| 16pt | Emphasis Text | Bottom takeaway on slide 8 | Callout text, bold |
| 14pt | Body Text | Tables, lists, descriptions | PRIMARY BODY SIZE, all main content |
| 9pt | Footnote | Source attribution | Smallest, gray color only |
No other sizes should be used - stick to this hierarchy exclusively.
Line Treatment (CRITICAL)
Line Rendering Rules
- All lines are FLAT - no shadows, no effects, no 3D
- Remove theme style references - prevents automatic shadow application
- Solid color only - no gradients or patterns
- Width varies by context - see table below
Line Width Specifications
| Usage | Width | Color | Context |
|---|
| Title separator (under action titles) | 0.5pt | BLACK | Below 22pt title |
| Column/section divider (under headers) | 0.5pt | BLACK | Below 18pt headers |
| Table header line | 1.0pt | BLACK | Between header and first row |
| Table row separator | 0.5pt | LINE_GRAY (#CCCCCC) | Between table rows |
| Timeline line (roadmap) | 0.75pt | LINE_GRAY | Background for phase indicators |
| Cover accent line | 2.0pt | NAVY | Decorative bottom-left on cover |
| Column internal divider | 0.5pt | BLACK | Between "ๆฏไปไน" and "็ฌๅฐไนๅค" |
Code Implementation (v1.1 โ Rectangle-based Lines)
CRITICAL: Do NOT use slide.shapes.add_connector() for lines. Connectors carry <p:style> elements that reference theme effects and cause file corruption. Instead, draw lines as ultra-thin rectangles:
def add_hline(slide, x, y, length, color=BLACK, thickness=Pt(0.5)):
"""Draw a horizontal line using a thin rectangle (no connector, no p:style)."""
from pptx.util import Emu
h = max(int(thickness), Emu(6350)) # minimum ~0.5pt
return add_rect(slide, x, y, length, h, color)
IMPORTANT: Never use add_connector() โ it causes file corruption. Always use add_hline() (thin rectangle).
Post-Save Full Cleanup (v1.1 โ Nuclear Sanitization)
After prs.save(outpath), ALWAYS run full cleanup that sanitizes both theme XML and all slide XML:
import zipfile, os
from lxml import etree
def full_cleanup(outpath):
"""Remove ALL p:style from every slide + theme shadows/3D."""
tmppath = outpath + '.tmp'
with zipfile.ZipFile(outpath, 'r') as zin:
with zipfile.ZipFile(tmppath, 'w', zipfile.ZIP_DEFLATED) as zout:
for item in zin.infolist():
data = zin.read(item.filename)
if item.filename.endswith('.xml'):
root = etree.fromstring(data)
ns_p = 'http://schemas.openxmlformats.org/presentationml/2006/main'
ns_a = 'http://schemas.openxmlformats.org/drawingml/2006/main'
# Remove ALL p:style elements from all shapes/connectors
for style in root.findall(f'.//{{{ns_p}}}style'):
style.getparent().remove(style)
# Remove shadows and 3D from theme
if 'theme' in item.filename.lower():
for tag in ['outerShdw', 'innerShdw', 'scene3d', 'sp3d']:
for el in root.findall(f'.//{{{ns_a}}}{tag}'):
el.getparent().remove(el)
data = etree.tostring(root, xml_declaration=True,
encoding='UTF-8', standalone=True)
zout.writestr(item, data)
os.replace(tmppath, outpath)
Text Box & Shape Treatment
Text Box Padding
All text boxes must have consistent internal padding to prevent text touching edges:
bodyPr = tf._txBody.find(qn('a:bodyPr'))
# All margins: 45720 EMUs = ~0.05 inches
for attr in ['lIns','tIns','rIns','bIns']:
bodyPr.set(attr, '45720')
Vertical Anchoring
Text must be anchored correctly based on usage:
| Content Type | Anchor | Code | Notes |
|---|
| Action titles | MIDDLE | anchor='ctr' | Centered vertically in bar |
| Body text | TOP | anchor='t' | Default, aligns to top |
| Labels | CENTER | anchor='ctr' | For circle labels |
anchor_map = {
MSO_ANCHOR.MIDDLE: 'ctr',
MSO_ANCHOR.BOTTOM: 'b',
MSO_ANCHOR.TOP: 't'
}
bodyPr.set('anchor', anchor_map.get(anchor, 't'))
Shape Styling
All shapes (rectangles, circles) must have:
- Solid fill color (no gradients)
- NO border/line (
shape.line.fill.background())
- p:style removed immediately after creation (
_clean_shape())
- No shadow effects (enforced by both inline cleanup and post-save full_cleanup)
def _clean_shape(shape):
"""Remove p:style from any shape to prevent effect references."""
sp = shape._element
style = sp.find(qn('p:style'))
if style is not None:
sp.remove(style)
shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, left, top, width, height)
shape.fill.solid()
shape.fill.fore_color.rgb = BG_GRAY
shape.line.fill.background() # CRITICAL: removes border
_clean_shape(shape) # CRITICAL: removes p:style
Presentation Planning
This section provides mandatory guidance for planning presentation structure, selecting layouts, and ensuring adequate content density. These rules dramatically improve output quality across different LLM models.
Recommended Slide Structures
When creating a presentation, follow these templates unless the user explicitly specifies a different structure:
Standard Presentation (10-12 slides)
Slide 1: Cover Slide (Pattern #1 or #4)
Slide 2: Table of Contents (Pattern #6) โ list ALL content sections
Slide 3: Executive Summary / Core Thesis (Pattern #24 or #8+#10)
Slides 4-7: Supporting Arguments (one per slide, vary layouts)
Slides 8-10: Case Studies / Evidence (Pattern #33 or #19)
Slide 11: Synthesis / Roadmap (Pattern #29 or #16)
Slide 12: Key Takeaways + Closing (Pattern #34 or #36)
Short Presentation (6-8 slides)
Slide 1: Cover Slide
Slide 2: Executive Summary (Pattern #24)
Slides 3-5: Core Content (vary layouts: #8, #14, #19, #33)
Slide 6: Synthesis / Timeline (Pattern #29)
Slide 7: Key Takeaways (Pattern #34)
Slide 8: Closing (Pattern #36)
CRITICAL RULES:
- Minimum slide count: 8 slides for any substantive topic. If the user's content supports 10+, generate 10+.
- Never stop early: Generate ALL planned slides in a single script. Do not truncate.
- TOC must list ALL sections: The Table of Contents slide must enumerate every content slide by number and title.
Layout Diversity Requirement
Each content slide MUST use a DIFFERENT layout pattern from its neighbors. Repeating the same layout on consecutive slides makes the presentation feel monotonous and unprofessional.
Match content type to the optimal layout pattern:
| Content Type | Recommended Layouts | Avoid |
|---|
| Single key statistic | Big Number (#8) | Plain text |
| 2 options comparison | Side-by-Side (#19), Before/After (#20), Metric Comparison Row (#62) | Two-column text |
| 3-4 parallel concepts | Three-Pillar (#14), Four-Column (#27), Metric Cards (#10), Icon Grid (#63) | Bullet list |
| Process / steps | Process Chevron (#16), Vertical Steps (#30), Value Chain (#67) | Numbered text |
| Timeline | Timeline/Roadmap (#29), Cycle (#31) | Bullet list |
| Data table | Data Table (#9), Scorecard (#22), Harvey Ball Table (#56) | Plain text |
| Case study | Case Study (#33), Case Study with Image (#45) | Two-column text |
| Summary / conclusion | Executive Summary (#24), Key Takeaway (#25) | Bullet list |
| Multiple KPIs | Three-Stat Dashboard (#12), Two-Stat Comparison (#11), KPI Tracker (#52), Dashboard (#57) | Plain text |
| Time series + values/percentages | Grouped Bar (#37), Stacked Bar (#38), Line Chart (#50), Stacked Area (#70) | Data Table, Scorecard |
| Category ranking / comparison | Horizontal Bar (#39), Grouped Bar (#37), Pareto (#51) | Bullet list, Plain text |
| Part-of-whole / composition | Donut (#48), Pie (#64), Stacked Bar (#38) | Bullet list |
| Content with visual / photo | Content+Right Image (#40), Left Image+Content (#41), Three Images (#42) | Text-only layouts |
| Risk / evaluation matrix | Risk Matrix (#54), SWOT (#65), Harvey Ball (#56), 2x2 Matrix (#13) | Bullet list |
| Strategic recommendations | Numbered List+Panel (#69), Decision Tree (#60), Checklist (#61) | Two-column text |
|
NEVER use Two-Column Text (#26) for more than 1 slide per deck. It is the least visually engaging layout.
CHART PRIORITY RULE: When data contains dates/periods + numeric values or percentages (e.g., 3/4 ๆญฃ้ข 20% ไธญๆง 80% or Q1: ยฅ850ไธ), you MUST use a Chart pattern (#37-#39, #48-#56, #64, #70) instead of a text-based layout. Charts maximize data-ink ratio and are the most visually compelling way to present time-series data.
IMAGE PRIORITY RULE (v1.8): When the content involves case studies, product showcases, location overviews, or any scenario where a visual/photo would strengthen the narrative, prefer Image+Content layouts (#40-#47, #68) over text-only layouts. The add_image_placeholder() function creates gray placeholder boxes that users replace with real images after generation.
Content Density Requirements
"Minimalism" in McKinsey design means removing decorative noise (shadows, gradients, clip-art), NOT removing content. A slide with 80% whitespace is not minimalist โ it is EMPTY.
Mandatory minimums per content slide:
- At least 3 distinct visual blocks โ e.g., title bar + content area + takeaway box, or title + left panel + right panel
- Body text area utilization โฅ 50% of the available content space (between title bar at 1.4" and source line at 7.05")
- Action Title must be a FULL SENTENCE expressing the slide's key insight:
- โ
"่ฟๆฅ็ป็บฆๆ็AIๆจกๅๅฐ่ช็ฑๅๆฐๅๅฐ90%๏ผๅฎ็ฐๅ็ป่็ฒพๅบฆ้ขๆต"
- โ
"่ฟๆฅ็ป็บฆๆ็AIๆจกๅ"
- Use specific data points when the user provides them (numbers, percentages, names) โ display them prominently with Big Number or Metric Card patterns
- Source attribution (
add_source()) on every content slide with specific references, not generic labels
Production Guard Rails (v1.9 / v2.0)
These rules address recurring production defects observed across multiple presentation generations. Each rule is derived from real-world user feedback and must be followed without exception.
Rule 1: Spacing Between Content Blocks and Bottom Bars
Problem observed: Tables, charts, or content grids placed immediately above a bottom summary/action bar (e.g., "่กๅจๅ
ฌๅผ", "่ถๅฟๅค่ฏป", "้ฃ้ฉๆ็คบ") with zero vertical gap, making them visually merged.
MANDATORY: There MUST be at least 0.15" vertical gap between the last content block and any bottom bar/summary box. Calculate positions explicitly:
# โ WRONG: content ends at Inches(6.15), bottom bar also at Inches(6.15)
last_content_bottom = content_top + num_rows * row_height
bar_y = last_content_bottom # NO GAP!
# โ
CORRECT: explicit gap
BOTTOM_BAR_GAP = Inches(0.2)
bar_y = last_content_bottom + BOTTOM_BAR_GAP
Validation formula: bottom_bar_y >= last_content_bottom + Inches(0.15)
Rule 2: Content Overflow Protection
Problem observed: Text or shapes extending beyond the right margin (left_margin + content_width) or bottom margin (source line at 7.05").
MANDATORY overflow checks:
- Right margin: Every element's
left + width โค LM + CW (i.e., Inches(0.8) + Inches(11.733) = Inches(12.533))
- Bottom margin: Every element's
top + height โค Inches(6.95) (leaving room for source line at 7.05")
- Text in bounded boxes: When placing text inside a colored
add_rect() box, the text box MUST be inset by at least 0.15" on each side:
# โ
CORRECT: text inset within its container box
box_left = LM
box_width = CW
add_rect(s, box_left, box_y, box_width, box_h, BG_GRAY)
add_text(s, box_left + Inches(0.3), box_y, box_width - Inches(0.6), box_h,
text, ...) # 0.3" padding on each side
-
Multi-column layouts: When calculating column widths, account for inter-column gaps AND the right margin:
# total available = CW = Inches(11.733)
num_cols = 3
gap = Inches(0.2)
col_w = (CW - gap * (num_cols - 1)) / num_cols # NOT CW / num_cols
-
Long text truncation: If generated text may exceed box boundaries, reduce font_size by 1-2pt or abbreviate text. Never allow visible overflow.
Rule 3: Bottom Whitespace Elimination
Problem observed: Charts or content areas end at ~Inches(5.5) while the bottom bar sits at ~Inches(6.3), leaving ~0.8" of dead whitespace.
MANDATORY: The bottom summary bar should be positioned at no higher than Inches(6.1) and no lower than Inches(6.4). Adjust chart/content heights to fill available space. Target: visible whitespace between content and bottom bar โค 0.3".
# โ
CORRECT: Compute bottom bar position dynamically
content_bottom = chart_top + chart_height
# Place bottom bar close to content (but with minimum gap)
bar_y = max(content_bottom + Inches(0.15), Inches(6.1))
bar_y = min(bar_y, Inches(6.4)) # don't push past safe zone
Rule 4: Legend Color Consistency
Problem observed: Chart legends using plain black text "โ " symbols (โ ๅบๅๅผ โ ๅขๅ โ ๅๅฐ) while actual chart bars use NAVY, ACCENT_RED, ACCENT_GREEN โ colors don't match.
MANDATORY: Every legend indicator MUST use a colored square (add_rect()) matching the exact color used in the chart below it. Never use text-only legends with "โ " character.
# โ WRONG: Text-only legend with black squares
add_text(s, LM, legend_y, CW, Inches(0.25),
'โ ๅบๅๅผ โ ๅขๅ โ ๅๅฐ', ...)
# โ
CORRECT: Color-matched legend squares
lgx = LM + Inches(5)
add_rect(s, lgx, legend_y, Inches(0.15), Inches(0.15), NAVY)
add_text(s, lgx + Inches(0.2), legend_y, Inches(0.9), Inches(0.25),
'ๅบๅๅผ', font_size=Pt(10), font_color=MED_GRAY)
add_rect(s, lgx + Inches(1.3), legend_y, Inches(0.15), Inches(0.15), ACCENT_RED)
add_text(s, lgx + Inches(1.5), legend_y, Inches(0.9), Inches(0.25),
'ๅขๅ ', font_size=Pt(10), font_color=MED_GRAY)
# ... repeat for each series
Legend placement: Inline with or immediately below the chart subtitle line (typically at Inches(1.15)-Inches(1.20)). Legend squares are 0.15" ร 0.15" with 0.05" gap to label text.
Rule 5: Title Style Consistency
Problem observed: Some slides using add_navy_title_bar() (full-width navy background + white text) while others use add_action_title() (white background + black text + underline), creating jarring visual inconsistency.
MANDATORY: Use add_action_title() (aat()) as the ONLY title style for ALL content slides. The navy title bar (antb()) is DEPRECATED for content slides and should only appear if explicitly requested by the user.
# โ DEPRECATED: Navy background title bar
def add_navy_title_bar(slide, text):
add_rect(s, 0, 0, SW, Inches(0.75), NAVY)
add_text(s, LM, 0, CW, Inches(0.75), text, font_color=WHITE, ...)
# โ
CORRECT: Consistent white-background action title (bottom-anchored)
def add_action_title(slide, text, title_size=Pt(22)):
add_text(s, Inches(0.8), Inches(0.15), Inches(11.7), Inches(0.9), text,
font_size=title_size, font_color=BLACK, bold=True, font_name='Georgia',
anchor=MSO_ANCHOR.BOTTOM) # BOTTOM: text sits flush against separator
add_hline(s, Inches(0.8), Inches(1.05), Inches(11.7), BLACK, Pt(0.5))
Note: When add_action_title() is used, content starts at Inches(1.25) (not Inches(1.0)). Account for this when positioning grids, tables, or charts below the title.
Rule 6: Axis Label Centering in Matrix/Grid Charts
Problem observed: In 2ร2 matrix layouts (#13, #59, #65), axis labels ("็จๆท่งๆจกโ", "ๆๆฏๅฃๅโ") positioned at fixed offsets rather than centered on their respective axes, causing visual misalignment.
MANDATORY: Axis labels MUST be centered on the full span of their axis:
# Grid dimensions
grid_left = LM + Inches(2.0)
grid_top = Inches(1.65)
cell_w = Inches(4.5) # width of each quadrant
cell_h = Inches(2.0) # height of each quadrant
grid_w = 2 * cell_w # full grid width
grid_h = 2 * cell_h # full grid height
# โ
CORRECT: Y-axis label centered vertically on FULL grid height
add_text(s, LM, grid_top, Inches(1.8), grid_h,
'Y่ฝดๆ ็ญพโ', alignment=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
# โ
CORRECT: X-axis label centered horizontally on FULL grid width
add_text(s, grid_left, grid_top + grid_h + Inches(0.1), grid_w, Inches(0.3),
'X่ฝดๆ ็ญพ โ', alignment=PP_ALIGN.CENTER)
Rule 7: Image Placeholder Slide Requirement
Problem observed: Presentations generated with zero image-containing slides, resulting in a wall of text/charts that feels monotonous and lacks visual relief.
MANDATORY: For presentations with 8+ slides, at least 1 slide must include image placeholders (using add_image_placeholder() or custom gray boxes with "่ฏทๆๅ
ฅๅพ็" labels). Preferred positions:
- After the first 2-3 content slides (as a visual break)
- For case studies, product showcases, or ecosystem overviews
Standard placeholder style (when not using add_image_placeholder() helper):
# Large placeholder
img_l = LM; img_t = Inches(1.3); img_w = Inches(6.5); img_h = Inches(4.0)
add_rect(s, img_l, img_t, img_w, img_h, BG_GRAY)
add_rect(s, img_l + Inches(0.04), img_t + Inches(0.04),
img_w - Inches(0.08), img_h - Inches(0.08), WHITE)
add_rect(s, img_l + Inches(0.08), img_t + Inches(0.08),
img_w - Inches(0.16), img_h - Inches(0.16), RGBColor(0xF8, 0xF8, 0xF8))
add_text(s, img_l, img_t + img_h // 2 - Inches(0.3), img_w, Inches(0.5),
'[ ่ฏทๆๅ
ฅๅพ็ ]', font_size=Pt(22), font_color=LINE_GRAY,
bold=True, alignment=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
add_text(s, img_l, img_t + img_h // 2 + Inches(0.2), img_w, Inches(0.3),
'ๅพ็ๆ่ฟฐๆ ็ญพ', font_size=Pt(13), font_color=MED_GRAY,
alignment=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
This triple-border style (BG_GRAY โ WHITE โ #F8F8F8) creates a professional, clearly identifiable placeholder that prompts users to insert real images.
Rule 8: Dynamic Sizing for Variable-Count Layouts (v1.10.4)
Problem observed: Layouts with a variable number of items (checklist rows, value chain stages, cover multi-line titles) use fixed dimensions that only work for a specific count. When item count differs, content either overflows past page boundaries or leaves excessive whitespace.
MANDATORY: For any layout where the number of items is variable, compute dimensions dynamically:
# โ
Horizontal items (value chain, flow): fill content width
n = len(items)
gap = Inches(0.35)
item_w = (CW - gap * (n - 1)) / n # NOT a fixed Inches(2.0)
# โ
Vertical items (checklist, table rows): fit within available height
bottom_limit = BOTTOM_BAR_Y if bottom_bar else SOURCE_Y - Inches(0.05)
available_h = bottom_limit - content_start_y
item_h = min(MAX_ITEM_H, available_h / max(n, 1)) # cap at comfortable max
# โ
Multi-line titles: height scales with line count
n_lines = text.count('\n') + 1
title_h = Inches(0.8 + 0.65 * max(n_lines - 1, 0))
# Position following elements relative to title bottom, NOT at fixed y
Anti-patterns (โ NEVER DO):
stage_w = Inches(2.0) for N stages โ use (CW - gap*(N-1)) / N
row_h = Inches(0.55) for N rows โ use min(0.85, available / N)
subtitle_y = Inches(3.5) on cover โ use title_y + title_h + Inches(0.3)
Rule 9: BLOCK_ARC Native Shapes for Circular Charts (v2.0)
Problem observed: Donut charts (#48), pie charts (#64), and gauge dials (#55) rendered with hundreds to thousands of small add_rect() blocks. This creates 100-2800 shapes per chart, inflates file size by 60-80%, slows generation to 2+ minutes, and produces visual artifacts (gaps between blocks, jagged edges).
MANDATORY: Use BLOCK_ARC preset shapes via python-pptx + XML adjustment for all circular/arc charts. Each segment = 1 shape (total: 3-5 shapes per chart vs. hundreds).
BLOCK_ARC angle convention (PPT coordinate system):
- Angles measured clockwise from 12 o'clock (top), in 60000ths of a degree
- Top = 0ยฐ, Right = 90ยฐ, Bottom = 180ยฐ, Left = 270ยฐ
- Example: a full-circle donut segment from 12 o'clock CW to 3 o'clock = adj1=0, adj2=5400000
Three adj parameters:
adj1: start angle (60000ths of degree, CW from top)
adj2: end angle (60000ths of degree, CW from top)
adj3: inner radius ratio (0 = solid sector / pie, 50000 = max / invisible ring)
from pptx.oxml.ns import qn
def add_block_arc(slide, left, top, width, height, start_deg, end_deg, inner_ratio, color):
"""Draw a BLOCK_ARC shape with precise angle and ring-width control.
Args:
slide: pptx slide object
left, top, width, height: bounding box (width == height for circular arc)
start_deg: start angle in degrees, CW from 12 o'clock (0=top, 90=right, 180=bottom, 270=left)
end_deg: end angle in degrees, CW from 12 o'clock
inner_ratio: 0 = solid pie sector, 50000 = max (paper-thin ring).
For ~10px ring width: int((outer_r - Pt(10)) / outer_r * 50000)
color: RGBColor fill color
"""
from pptx.enum.shapes import MSO_SHAPE
sh = slide.shapes.add_shape(MSO_SHAPE.BLOCK_ARC, left, top, width, height)
sh.fill.solid()
sh.fill.fore_color.rgb = color
sh.line.fill.background()
_clean_shape(sh) # remove p:style to prevent file corruption
sp = sh._element.find(qn('p:spPr'))
prstGeom = sp.find(qn('a:prstGeom'))
if prstGeom is not None:
avLst = prstGeom.find(qn('a:avLst'))
if avLst is None:
avLst = prstGeom.makeelement(qn('a:avLst'), {})
prstGeom.append(avLst)
for gd in avLst.findall(qn('a:gd')):
avLst.remove(gd)
gd1 = avLst.makeelement(qn('a:gd'), {'name': 'adj1', 'fmla': f'val {int(start_deg * 60000)}'})
gd2 = avLst.makeelement(qn('a:gd'), {'name': 'adj2', 'fmla': f'val {int(end_deg * 60000)}'})
gd3 = avLst.makeelement(qn('a:gd'), {'name': 'adj3', 'fmla': f'val {inner_ratio}'})
avLst.append(gd1)
avLst.append(gd2)
avLst.append(gd3)
return sh
Usage patterns:
# โโ Donut chart: 4 segments, ~10px ring width โโ
outer_r = Inches(1.6)
inner_ratio = int((outer_r - Pt(10)) / outer_r * 50000) # ~10px ring
cum_deg = 0 # start at top (0ยฐ = 12 o'clock)
for pct, color, label in segments:
sweep = pct * 360
add_block_arc(s, cx - outer_r, cy - outer_r, outer_r * 2, outer_r * 2,
cum_deg, cum_deg + sweep, inner_ratio, color)
cum_deg += sweep
# โโ Pie chart (solid sectors): inner_ratio = 0 โโ
add_block_arc(s, cx - r, cy - r, r * 2, r * 2, 0, 151.2, 0, NAVY) # 42%
# โโ Horizontal rainbow gauge (semi-circle, leftโtopโright) โโ
# PPT coords: left=270ยฐ, top=0ยฐ, right=90ยฐ
gauge_segs = [(0.40, ACCENT_RED), (0.30, ACCENT_ORANGE), (0.30, ACCENT_GREEN)]
inner_ratio = int((outer_r - Pt(10)) / outer_r * 50000)
ppt_cum = 270 # start at left
for pct, color in gauge_segs:
sweep = pct * 180
add_block_arc(s, cx - outer_r, cy - outer_r, outer_r * 2, outer_r * 2,
ppt_cum % 360, (ppt_cum + sweep) % 360, inner_ratio, color)
ppt_cum += sweep
Anti-patterns (โ NEVER DO for circular charts):
- Nested
for deg in range(...): for r in range(...): add_rect(...) โ generates hundreds/thousands of tiny squares
- Drawing a white circle on top of a filled circle to "fake" a donut โ fragile, misaligns on resize
- Using
math.cos/sin + add_rect() loops for arcs โ always use BLOCK_ARC instead
Mandatory Slide Elements
EVERY content slide (except Cover and Closing) MUST include ALL of these:
| Element | Function | Position |
|---|
| Action Title | add_action_title(slide, text) | Top (0.15" from top) |
| Title separator line | Included in add_action_title() | 1.05" from top |
| Content area | Layout-specific content blocks | 1.4" to 6.5" |
| Source attribution | add_source(slide, text) | 7.05" from top |
| Page number | add_page_number(slide, n, total) | Bottom-right corner |
Page number helper function:
def add_page_number(slide, num, total):
add_text(slide, Inches(12.2), Inches(7.1), Inches(1), Inches(0.3),
f"{num}/{total}", font_size=Pt(9), font_color=MED_GRAY,
alignment=PP_ALIGN.RIGHT)
Layout Patterns
Slide Dimensions
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
Widescreen format (16:9), standard for all presentations.
Standard Margin/Padding
| Position | Size | Usage |
|---|
| Left margin | 0.8" | Default left edge |
| Right margin | 0.8" | Default right edge |
| Top (below title) | 1.4" | Content start position |
| Bottom | 7.05" | Source text baseline |
| Title bar height | 0.9" | Action title bar |
| Title bar top | 0.15" | From slide top |
Slide Type Patterns
1. Cover Slide (Slide 1)
Layout:
- Navy bar at very top (0.05" height)
- Main title (44pt, Georgia, navy) at y=1.2" โ height computed dynamically from line count
- Subtitle (24pt, dark gray) positioned below title dynamically
- Date/info (14pt, med gray) follows subtitle
- Decorative navy line at x=1", y=6.8" (4" wide, 2pt)
Code template:
s1 = prs.slides.add_slide(prs.slide_layouts[6])
add_rect(s1, 0, 0, prs.slide_width, Inches(0.05), NAVY)
# Dynamic title height based on line count
lines = title.split('\n') if isinstance(title, str) else title
n_lines = len(lines) if isinstance(lines, list) else title.count('\n') + 1
title_h = Inches(0.8 + 0.65 * max(n_lines - 1, 0))
add_text(s1, Inches(1), Inches(1.2), Inches(11), title_h,
'้กน็ฎๅ็งฐ', font_size=Pt(44), font_name='Georgia',
font_color=NAVY, bold=True, ea_font='KaiTi')
# Position elements BELOW title dynamically โ never use fixed y
sub_y = Inches(1.2) + title_h + Inches(0.3)
add_text(s1, Inches(1), sub_y, Inches(11), Inches(0.8),
'ๅฏๆ ้ขๆ่ฟฐ', font_size=Pt(24),
font_color=DARK_GRAY, ea_font='KaiTi')
sub_y += Inches(1.0)
add_text(s1, Inches(1), sub_y + Inches(0.3), Inches(11), Inches(0.5),
'ๆผ็คบๆ็จฟ | 2026ๅนด3ๆ', font_size=BODY_SIZE,
font_color=MED_GRAY, ea_font='KaiTi')
add_hline(s1, Inches(1), Inches(6.8), Inches(4), NAVY, Pt(2))
2. Action Title Slide (Most Content Slides)
Every main content slide has this structure:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 0.15"
โ โ Action Title (22pt, bold, black) โ โ TITLE_BAR_H = 0.9"
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค 1.05"
โ โ
โ Content area (starts at 1.4") โ
โ [Tables, lists, text, etc.] โ
โ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ 7.05"
โ Source: ... โ 9pt, med gray
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 7.5"
Code pattern:
s = prs.slides.add_slide(prs.slide_layouts[6])
add_action_title(s, 'Slide Title Here')
# Then add content below y=1.4"
add_source(s, 'Source attribution')
3. Table Layout (Slide 4 - Five Capabilities)
Structure:
- Header row with column names (BODY_SIZE, gray, bold)
- 1.0pt black line under header
- Data rows (1.0" height each, 14pt text)
- 0.5pt gray line between rows
- 3 columns: Module (1.6" wide), Function (5.0"), Scene (5.1")
# Headers
add_text(s4, left, top, width, height, text,
font_size=BODY_SIZE, font_color=MED_GRAY, bold=True)
# Header line (thicker)
add_line(s4, left, top + Inches(0.5), left + full_width, top + Inches(0.5),
color=BLACK, width=Pt(1.0))
# Rows
for i, (col1, col2, col3) in enumerate(rows):
y = header_y + row_height * i
add_text(s4, left, y, col1_w, row_h, col1, ...)
add_text(s4, left + col1_w, y, col2_w, row_h, col2, ...)
add_text(s4, left + col1_w + col2_w, y, col3_w, row_h, col3, ...)
# Row separator
add_line(s4, left, y + row_h, left + full_w, y + row_h,
color=LINE_GRAY, width=Pt(0.5))
4. Three-Column Overview (Slide 5)
Layout:
- Left column (4.1" wide): "ๆฏไปไน"
- Middle column (4.1" wide): "็ฌๅฐไนๅค"
- Right 1/4 (2.5" wide) gray panel: "Key Takeaways"
0.8" 4.9" 5.3" 9.4" 10.0" 12.5"
|-----|-----|-----|-----|------|
โๅทฆ โ โ ไธญ โ โ ๅณ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Code:
left_x = Inches(0.8)
col_w5 = Inches(4.1)
mid_x = Inches(5.3)
takeaway_left = Inches(10.0)
takeaway_width = Inches(2.5)
# Left column
add_text(s5, left_x, content_top, col_w5, ...)
add_text(s5, left_x, content_top + Inches(0.6), col_w5, ...,
bullet=True, line_spacing=Pt(8))
# Right gray takeaway area
add_rect(s5, takeaway_left, Inches(1.2), takeaway_width, Inches(5.6), BG_GRAY)
add_text(s5, takeaway_left + Inches(0.15), Inches(1.35), takeaway_width - Inches(0.3), ...,
'Key Takeaways', font_size=BODY_SIZE, color=NAVY, bold=True)
add_text(s5, takeaway_left + Inches(0.15), Inches(1.9), takeaway_width - Inches(0.3), ...,
[f'{i+1}. {t}' for i, t in enumerate(takeaways)], line_spacing=Pt(10))
็ฑปๅซ A๏ผ็ปๆๅฏผ่ช
5. Section Divider (็ซ ่ๅ้้กต)
้็จๅบๆฏ: ๅค็ซ ่ๆผ็คบๆ็จฟ็็ซ ่่ฟๆธก้กต๏ผ็จไบ่ง่งไธๅ้ไธๅไธป้ขๆจกๅใ
โโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โN โ โ
โA โ ็ฌฌไธ้จๅ โ
โV โ ็ซ ่ๆ ้ข๏ผ28pt, NAVY, bold๏ผ โ
โY โ ๅฏๆ ้ข่ฏดๆๆๅญ โ
โ โ โ
โโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_rect(s, 0, 0, Inches(0.6), SH, NAVY)
add_text(s, Inches(1.2), Inches(2.0), Inches(10), Inches(0.8),
'็ฌฌไธ้จๅ', font_size=SUB_HEADER_SIZE, font_color=MED_GRAY, font_name='Georgia')
add_text(s, Inches(1.2), Inches(2.8), Inches(10), Inches(1.2),
'็ซ ่ๆ ้ข', font_size=HEADER_SIZE, font_color=NAVY, bold=True, font_name='Georgia')
add_text(s, Inches(1.2), Inches(4.2), Inches(10), Inches(0.6),
'ๅฏๆ ้ข่ฏดๆๆๅญ', font_size=BODY_SIZE, font_color=DARK_GRAY)
6. Table of Contents / Agenda (็ฎๅฝ/่ฎฎ็จ้กต)
้็จๅบๆฏ: ๆผ็คบๆ็จฟๅผๅคด็็ฎๅฝๆไผ่ฎฎ่ฎฎ็จ๏ผๅๅบๅ็ซ ่ๅ่ฏดๆใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ ็ฎๅฝ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ (1) ็ซ ่ไธๆ ้ข ็ฎ่ฆๆ่ฟฐ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (2) ็ซ ่ไบๆ ้ข ็ฎ่ฆๆ่ฟฐ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (3) ็ซ ่ไธๆ ้ข ็ฎ่ฆๆ่ฟฐ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, '็ฎๅฝ')
items = [('1', 'ๅผ่จไธ่ๆฏ', '้กน็ฎ่ตทๆบไธๆ ธๅฟ้ฎ้ข'),
('2', 'ๅธๅบๅๆ', '็ซไบๆ ผๅฑไธๆบไผ่ฏๅซ'),
('3', 'ๆ็ฅๅปบ่ฎฎ', 'ไธๅคงๆ ธๅฟ่กๅจๆนๆก')]
iy = Inches(1.6)
for num, title, desc in items:
add_oval(s, LM, iy, num, size=Inches(0.5))
add_text(s, LM + Inches(0.7), iy, Inches(4.0), Inches(0.4),
title, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
add_text(s, Inches(5.5), iy + Inches(0.05), Inches(6.5), Inches(0.4),
desc, font_size=BODY_SIZE, font_color=MED_GRAY)
iy += Inches(0.7)
add_hline(s, LM, iy, CONTENT_W, LINE_GRAY)
iy += Inches(0.3)
7. Appendix Title (้ๅฝๆ ้ข้กต)
้็จๅบๆฏ: ๆญฃๆ็ปๆๅ็้ๅฝ/ๅค็จๆๆๅ้้กตใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ โ
โ ้ๅฝ โ
โ Appendix โ
โ โโโโโโโโ โ
โ ่กฅๅ
ๆฐๆฎไธๅ่่ตๆ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_rect(s, 0, 0, SW, Inches(0.05), NAVY)
add_text(s, Inches(1), Inches(2.5), Inches(11.3), Inches(1.0),
'้ๅฝ', font_size=Pt(36), font_color=NAVY, bold=True,
font_name='Georgia', alignment=PP_ALIGN.CENTER)
add_hline(s, Inches(5.5), Inches(3.8), Inches(2.3), NAVY, Pt(1.5))
add_text(s, Inches(1), Inches(4.2), Inches(11.3), Inches(0.5),
'่กฅๅ
ๆฐๆฎไธๅ่่ตๆ', font_size=BODY_SIZE, font_color=MED_GRAY,
alignment=PP_ALIGN.CENTER)
็ฑปๅซ B๏ผๆฐๆฎ็ป่ฎก
8. Big Number / Factoid (ๅคงๆฐๆฎๅฑ็คบ้กต)
้็จๅบๆฏ: ็จไธไธช้็ฎ็ๅคงๆฐๅญๅผๅบๆ ธๅฟๅ็ฐๆๅ
ณ้ฎๆฐๆฎ็นใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโNAVYโโโโโโโโโโ โ
โ โ 95% โ ๅณไพงไธไธๆ่ฏดๆ โ
โ โ ๅญๆ ้ข โ ่ฏฆ็ป่งฃ้ๆฐๆฎๅซไน โ
โ โโโโโโโโโโโโโโโโ โ
โ โ
โ โโBG_GRAYโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ๅ
ณ้ฎๆด่ง๏ผ่ฏฆ็ปๅๆๆๅญ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๅ
ณ้ฎๅ็ฐๆ ้ข')
add_rect(s, LM, Inches(1.4), Inches(3.5), Inches(1.8), NAVY)
add_text(s, LM + Inches(0.2), Inches(1.5), Inches(3.1), Inches(0.8),
'95%', font_size=Pt(44), font_color=WHITE, bold=True,
font_name='Georgia', alignment=PP_ALIGN.CENTER)
add_text(s, LM + Inches(0.2), Inches(2.3), Inches(3.1), Inches(0.7),
'ๆ่ฟฐๆฐๆฎๅซไน', font_size=Pt(12), font_color=WHITE, alignment=PP_ALIGN.CENTER)
add_text(s, Inches(5.0), Inches(1.5), Inches(7.5), Inches(2.0),
'ไธไธๆ่ฏดๆไธ่ฏฆ็ป่งฃ้', font_size=BODY_SIZE)
add_rect(s, LM, Inches(4.5), CONTENT_W, Inches(2.2), BG_GRAY)
add_text(s, LM + Inches(0.3), Inches(4.6), Inches(1.5), Inches(0.4),
'ๅ
ณ้ฎๆด่ง', font_size=BODY_SIZE, font_color=NAVY, bold=True)
add_text(s, LM + Inches(0.3), Inches(5.1), CONTENT_W - Inches(0.6), Inches(1.4),
['ๆด่ง่ฆ็นไธ', 'ๆด่ง่ฆ็นไบ', 'ๆด่ง่ฆ็นไธ'], line_spacing=Pt(8))
add_source(s, 'Source: ...')
9. Two-Stat Comparison (ๅๆฐๆฎๅฏนๆฏ้กต)
้็จๅบๆฏ: ๅนถๆๅฑ็คบไธคไธชๅ
ณ้ฎๆๆ ็ๅฏนๆฏ๏ผๅฆๅๆฏใ็ฏๆฏใA vs B๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโNAVYโโโโโโโโ โโโBG_GRAYโโโโโ โ
โ โ $2.4B โ โ $1.8B โ โ
โ โ 2026ๅนด็ฎๆ โ โ 2025ๅนดๅฎ้
โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ
โ ๅๆ่ฏดๆๆๅญ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๅฏนๆฏๆ ้ข')
stats = [('$2.4B', '2026ๅนด็ฎๆ ', True), ('$1.8B', '2025ๅนดๅฎ้
', False)]
sw = Inches(5.5)
sg = Inches(0.733)
for i, (big, small, is_navy) in enumerate(stats):
sx = LM + (sw + sg) * i
fill = NAVY if is_navy else BG_GRAY
bc = WHITE if is_navy else NAVY
sc = WHITE if is_navy else DARK_GRAY
add_rect(s, sx, Inches(1.8), sw, Inches(2.5), fill)
add_text(s, sx + Inches(0.3), Inches(2.0), sw - Inches(0.6), Inches(1.0),
big, font_size=Pt(44), font_color=bc, bold=True,
font_name='Georgia', alignment=PP_ALIGN.CENTER)
add_text(s, sx + Inches(0.3), Inches(3.2), sw - Inches(0.6), Inches(0.5),
small, font_size=BODY_SIZE, font_color=sc, alignment=PP_ALIGN.CENTER)
add_text(s, LM, Inches(5.0), CONTENT_W, Inches(1.5),
'ๅๆ่ฏดๆๆๅญ', font_size=BODY_SIZE)
add_source(s, 'Source: ...')
10. Three-Stat Dashboard (ไธๆๆ ไปช่กจ็)
้็จๅบๆฏ: ๅๆถๅฑ็คบไธไธชๅ
ณ้ฎไธๅกๆๆ ๏ผๅฆ KPIใๅญฃๅบฆๆฐๆฎ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโNAVYโโโ โโโBG_GRAYโโ โโโBG_GRAYโโโ
โ โ ๆฐๅญ1 โ โ ๆฐๅญ2 โ โ ๆฐๅญ3 โโ
โ โ ๅฐๆ ้ข โ โ ๅฐๆ ้ข โ โ ๅฐๆ ้ข โโ
โ โโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโโ
โ โ
โ ่ฏฆ็ป่ฏดๆๆๅญ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ไธๅคงๅ
ณ้ฎๆๆ ')
stats = [('87%', 'ๅฎขๆทๆปกๆๅบฆ', True),
('+23%', 'ๅๆฏๅข้ฟ', False),
('4.2x', 'ๆ่ตๅๆฅ็', False)]
sw = Inches(3.5)
sg = (CONTENT_W - sw * 3) / 2
for i, (big, small, is_navy) in enumerate(stats):
sx = LM + (sw + sg) * i
fill = NAVY if is_navy else BG_GRAY
bc = WHITE if is_navy else NAVY
sc = WHITE if is_navy else DARK_GRAY
add_rect(s, sx, Inches(1.4), sw, Inches(1.8), fill)
add_text(s, sx + Inches(0.2), Inches(1.5), sw - Inches(0.4), Inches(0.7),
big, font_size=Pt(28), font_color=bc, bold=True,
font_name='Georgia', alignment=PP_ALIGN.CENTER)
add_text(s, sx + Inches(0.2), Inches(2.25), sw - Inches(0.4), Inches(0.6),
small, font_size=Pt(12), font_color=sc, alignment=PP_ALIGN.CENTER)
add_source(s, 'Source: ...')
11. Data Table with Headers (ๆฐๆฎ่กจๆ ผ้กต)
้็จๅบๆฏ: ็ปๆๅๆฐๆฎๅฑ็คบ๏ผๅฆ่ดขๅกๆฐๆฎใๅ่ฝๅฏนๆฏ็ฉ้ตใ้กน็ฎๆธ
ๅใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ๅ1 ๅ2 ๅ3 ๅ4 โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ๆฐๆฎ1-1 ๆฐๆฎ1-2 ... ... โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ๆฐๆฎ2-1 ๆฐๆฎ2-2 ... ... โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ๆฐๆฎ3-1 ๆฐๆฎ3-2 ... ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆฐๆฎๆฆ่ง')
headers = ['ๆจกๅ', 'ๅ่ฝ', '็ถๆ', '่ด่ดฃไบบ']
col_widths = [Inches(2.5), Inches(4.0), Inches(2.5), Inches(2.7)]
hdr_y = Inches(1.5)
cx = LM
for hdr, cw in zip(headers, col_widths):
add_text(s, cx, hdr_y, cw, Inches(0.4), hdr,
font_size=BODY_SIZE, font_color=MED_GRAY, bold=True)
cx += cw
add_hline(s, LM, Inches(2.0), CONTENT_W, BLACK, Pt(1.0))
# Data rows
rows = [['ๆจกๅA', 'ๆ ธๅฟๅ่ฝๆ่ฟฐ', 'ๅทฒไธ็บฟ', 'ๅผ ไธ'], ...]
row_h = Inches(0.8)
for ri, row in enumerate(rows):
ry = Inches(2.1) + row_h * ri
cx = LM
for val, cw in zip(row, col_widths):
add_text(s, cx, ry, cw, row_h, val, font_size=BODY_SIZE)
cx += cw
add_hline(s, LM, ry + row_h, CONTENT_W, LINE_GRAY)
add_source(s, 'Source: ...')
12. Metric Cards Row (ๆๆ ๅก็่ก)
้็จๅบๆฏ: 3-4ไธชๅนถๆๅก็ๅฑ็คบ็ฌ็ซๆๆ ๏ผๆฏไธชๅก็ๅซๆ ้ข+ๆ่ฟฐใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโBG_GRAYโโ โโBG_GRAYโโ โโBG_GRAYโโ โ
โ โ (A) โ โ (B) โ โ (C) โ โ
โ โ ๆ ้ข โ โ ๆ ้ข โ โ ๆ ้ข โ โ
โ โ โโโ โ โ โโโ โ โ โโโ โ โ
โ โ ๆ่ฟฐ โ โ ๆ่ฟฐ โ โ ๆ่ฟฐ โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆ ธๅฟๆๆ ๆฆ่ง')
cards = [('A', '็จๆทๅข้ฟ', 'ๆๆดป็จๆท่พพๅฐ 120 ไธ\nๅๆฏๅข้ฟ 35%'),
('B', '่ฅๆถ่กจ็ฐ', 'ๅญฃๅบฆ่ฅๆถ ยฅ8,500 ไธ\n่ถ
ๅบ้ขๆ 12%'),
('C', '่ฟ่ฅๆ็', 'ๅฎข่ฏๅๅบๆถ้ด < 2h\nๆปกๆๅบฆ 94%')]
cw = Inches(3.5)
cg = (CONTENT_W - cw * 3) / 2
for i, (letter, title, desc) in enumerate(cards):
cx = LM + (cw + cg) * i
cy = Inches(1.5)
add_rect(s, cx, cy, cw, Inches(4.5), BG_GRAY)
add_oval(s, cx + Inches(1.5), cy + Inches(0.2), letter)
add_text(s, cx + Inches(0.2), cy + Inches(0.8), cw - Inches(0.4), Inches(0.4),
title, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True,
alignment=PP_ALIGN.CENTER)
add_hline(s, cx + Inches(0.4), cy + Inches(1.3), cw - Inches(0.8), LINE_GRAY)
add_text(s, cx + Inches(0.2), cy + Inches(1.5), cw - Inches(0.4), Inches(2.5),
desc.split('\n'), line_spacing=Pt(8), alignment=PP_ALIGN.CENTER)
add_source(s, 'Source: ...')
็ฑปๅซ C๏ผๆกๆถ็ฉ้ต
13. 2x2 Matrix (ๅ่ฑก้็ฉ้ต)
้็จๅบๆฏ: ๆ็ฅๅๆ๏ผๅฆ BCG ็ฉ้ตใไผๅ
็บงๆๅบใ้ฃ้ฉ่ฏไผฐ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ้ซ Y่ฝด โ
โ โโNAVYโโโโโโโ โโBG_GRAYโโโโ โ
โ โ ๅทฆไธ่ฑก้ โ โ ๅณไธ่ฑก้ โ โ
โ โโโโโโโโโโโโโ โโโโโโโโโโโโโ โ
โ โโBG_GRAYโโโโ โโBG_GRAYโโโโ โ
โ โ ๅทฆไธ่ฑก้ โ โ ๅณไธ่ฑก้ โ โ
โ โโโโโโโโโโโโโ โโโโโโโโโโโโโ โ
โ ไฝ ้ซ X่ฝด โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆ็ฅไผๅ
็บง็ฉ้ต')
mx = LM + Inches(1.5)
my = Inches(1.8)
cw = Inches(4.5)
ch = Inches(2.5)
# Axis labels
add_text(s, mx - Inches(1.3), my + Inches(0.8), Inches(1.1), Inches(0.4),
'้ซๅฝฑๅ', font_size=BODY_SIZE, font_color=NAVY, bold=True, alignment=PP_ALIGN.CENTER)
add_text(s, mx + Inches(0.8), my - Inches(0.5), Inches(3.0), Inches(0.4),
'้ซๅฏ่กๆง', font_size=BODY_SIZE, font_color=NAVY, bold=True, alignment=PP_ALIGN.CENTER)
# Quadrants
add_rect(s, mx, my, cw, ch, NAVY) # best quadrant
add_rect(s, mx + cw + Inches(0.15), my, cw, ch, BG_GRAY)
add_rect(s, mx, my + ch + Inches(0.15), cw, ch, BG_GRAY)
add_rect(s, mx + cw + Inches(0.15), my + ch + Inches(0.15), cw, ch, BG_GRAY)
# Quadrant titles + descriptions
add_text(s, mx + Inches(0.3), my + Inches(0.3), cw - Inches(0.6), Inches(0.5),
'็ซๅณๆง่ก', font_size=SUB_HEADER_SIZE, font_color=WHITE, bold=True)
# ... repeat for other 3 quadrants with DARK_GRAY text
add_source(s, 'Source: ...')
14. Three-Pillar Framework (ไธๆฏๆฑๆกๆถ)
้็จๅบๆฏ: ๅฑ็คบไธไธชๅนถๅ็ๆ ธๅฟ็ญ็ฅใ่ฝๅๆไธป้ขๆจกๅใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโNAVYโโโ โโโNAVYโโโ โโโNAVYโโโ โ
โ โ ๆ ้ข1 โ โ ๆ ้ข2 โ โ ๆ ้ข3 โ โ
โ โโโโโโโโโโค โโโโโโโโโโค โโโโโโโโโโค โ
โ โ ่ฆ็น โ โ ่ฆ็น โ โ ่ฆ็น โ โ
โ โ ่ฆ็น โ โ ่ฆ็น โ โ ่ฆ็น โ โ
โ โ ่ฆ็น โ โ ่ฆ็น โ โ ่ฆ็น โ โ
โ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ไธๅคงๆ ธๅฟๆ็ฅ')
pillars = [('ๆฐๅญๅ่ฝฌๅ', ['ๅปบ่ฎพๆฐๆฎไธญๅฐ', 'ๆ้ๅ
จๆธ ้', '่ชๅจๅ่ฟ่ฅ']),
('็ป็ปๅ็บง', ['ๆๅนณๅ็ฎก็', 'ๆๆทๅข้', 'ไบบๆๆขฏ้']),
('ๅฎขๆทๆทฑ่', ['็ฒพ็ปๅ่ฟ่ฅ', 'ไผๅไฝ็ณป', 'LTV ๆๅ'])]
pw = Inches(3.5)
pg = (CONTENT_W - pw * 3) / 2
for i, (title, points) in enumerate(pillars):
px = LM + (pw + pg) * i
add_rect(s, px, Inches(1.5), pw, Inches(0.6), NAVY)
add_text(s, px + Inches(0.15), Inches(1.5), pw - Inches(0.3), Inches(0.6),
title, font_size=SUB_HEADER_SIZE, font_color=WHITE, bold=True,
anchor=MSO_ANCHOR.MIDDLE, alignment=PP_ALIGN.CENTER)
add_rect(s, px, Inches(2.1), pw, Inches(4.0), BG_GRAY)
add_text(s, px + Inches(0.2), Inches(2.3), pw - Inches(0.4), Inches(3.5),
[f'โข {p}' for p in points], line_spacing=Pt(10))
add_source(s, 'Source: ...')
15. Pyramid / Hierarchy (้ๅญๅก/ๅฑ็บงๅพ)
้็จๅบๆฏ: ๅฑ็คบๅฑ็บงๅ
ณ็ณป๏ผๅฆ Maslow ้ๆฑๅฑๆฌกใๆ็ฅ-ๆๆฏ-ๆง่กๅๅฑ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโNAVYโโโ โ
โ โ ๆฟๆฏ โ ๅณไพง่ฏดๆ โ
โ โโโดโโโโโโโโโดโโ โ
โ โ ๆ็ฅ็ฎๆ โ ๅณไพง่ฏดๆ โ
โ โโโดโโโโโโโโโโโโโดโโ โ
โ โ ๆง่กๆชๆฝ โ ๅณไพง่ฏดๆ โ
โ โโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆ็ฅๅฑ็บงๆกๆถ')
levels = [('ๆฟๆฏ', 'ๆไธบ่กไธ็ฌฌไธ', Inches(3.5)),
('ๆ็ฅ็ฎๆ ', 'ไธๅนดๆถๅ
ฅ็ฟปๅ', Inches(5.0)),
('ๆง่กๆชๆฝ', 'ๆธ ้+ไบงๅ+็ป็ป', Inches(6.5))]
for i, (label, desc, w) in enumerate(levels):
lx = Inches(6.666) - w / 2 # centered
ly = Inches(1.8) + Inches(1.5) * i
h = Inches(1.2)
fill = NAVY if i == 0 else BG_GRAY
tc = WHITE if i == 0 else NAVY
add_rect(s, lx, ly, w, h, fill)
add_text(s, lx + Inches(0.2), ly + Inches(0.1), w - Inches(0.4), Inches(0.4),
label, font_size=SUB_HEADER_SIZE, font_color=tc, bold=True,
alignment=PP_ALIGN.CENTER)
add_text(s, lx + Inches(0.2), ly + Inches(0.55), w - Inches(0.4), Inches(0.5),
desc, font_size=BODY_SIZE, font_color=tc if i == 0 else DARK_GRAY,
alignment=PP_ALIGN.CENTER)
add_source(s, 'Source: ...')
16. Process Chevron (ๆต็จ็ฎญๅคด้กต)
้็จๅบๆฏ: ็บฟๆงๆต็จๅฑ็คบ๏ผ3-5ๆญฅ๏ผ๏ผๅฆๅฎๆฝ่ทฏๅพใไธๅกๆต็จใๆนๆณ่ฎบๆญฅ้ชคใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โNAVYโ -> โGRAYโ -> โGRAYโ -> โGRAYโ โ
โ โ S1 โ โ S2 โ โ S3 โ โ S4 โ โ
โ โโโโโโ โโโโโโ โโโโโโ โโโโโโ โ
โ ๆ่ฟฐ ๆ่ฟฐ ๆ่ฟฐ ๆ่ฟฐ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๅฎๆฝ่ทฏๅพ')
steps = [('่ฏๆญ', '็ฐ็ถ่ฏไผฐ\n็็น่ฏๅซ'),
('่ฎพ่ฎก', 'ๆนๆกๅถๅฎ\n่ตๆบ่งๅ'),
('ๅฎๆฝ', 'ๅ้ถๆฎต่ฝๅฐ\nๅฟซ้่ฟญไปฃ'),
('ไผๅ', 'ๆๆ่ฟฝ่ธช\nๆ็ปญๆน่ฟ')]
sw = Inches(2.5)
sg = (CONTENT_W - sw * len(steps)) / (len(steps) - 1)
for i, (title, desc) in enumerate(steps):
sx = LM + (sw + sg) * i
fill = NAVY if i == 0 else BG_GRAY
tc = WHITE if i == 0 else NAVY
add_rect(s, sx, Inches(2.0), sw, Inches(1.2), fill)
add_oval(s, sx + Inches(0.1), Inches(2.1), str(i + 1),
bg=WHITE if i == 0 else NAVY, fg=NAVY if i == 0 else WHITE)
add_text(s, sx + Inches(0.6), Inches(2.1), sw - Inches(0.8), Inches(1.0),
title, font_size=SUB_HEADER_SIZE, font_color=tc, bold=True,
anchor=MSO_ANCHOR.MIDDLE)
add_text(s, sx + Inches(0.1), Inches(3.4), sw - Inches(0.2), Inches(1.5),
desc, font_size=BODY_SIZE, alignment=PP_ALIGN.CENTER)
if i < len(steps) - 1:
add_text(s, sx + sw + Inches(0.05), Inches(2.3), Inches(0.4), Inches(0.5),
'->', font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
add_source(s, 'Source: ...')
17. Venn Diagram Concept (็ปดๆฉๅพๆฆๅฟต้กต)
้็จๅบๆฏ: ๅฑ็คบไธคไธไธชๆฆๅฟต็ไบค้ๅ
ณ็ณป๏ผๅฆ่ฝๅไบคๅใๅธๅบๅฎไฝ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโBGโโโ โ
โ โฑๆฆๅฟตA โฒ โ
โ โฑ โโโโ โฒ ๅณไพง่ฏดๆ โ
โ โ โไบคโ โ โ
โ โฒ โโโโ โฑ โ
โ โฒๆฆๅฟตB โฑ โ
โ โโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆ ธๅฟ่ฝๅไบคๅ')
# Use overlapping rectangles to represent Venn concept
add_rect(s, Inches(1.5), Inches(1.8), Inches(4.5), Inches(4.0), BG_GRAY)
add_text(s, Inches(1.7), Inches(2.0), Inches(2.0), Inches(0.4),
'ๆๆฏ่ฝๅ', font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
add_rect(s, Inches(3.5), Inches(2.8), Inches(4.5), Inches(4.0), BG_GRAY)
add_text(s, Inches(5.5), Inches(5.5), Inches(2.0), Inches(0.4),
'ไธๅกๆดๅฏ', font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
# Intersection area
add_rect(s, Inches(3.5), Inches(2.8), Inches(2.5), Inches(3.0), NAVY)
add_text(s, Inches(3.7), Inches(3.5), Inches(2.1), Inches(0.8),
'ๆ ธๅฟ\n็ซไบๅ', font_size=SUB_HEADER_SIZE, font_color=WHITE, bold=True,
alignment=PP_ALIGN.CENTER)
# Right explanation
add_text(s, Inches(9.0), Inches(2.0), Inches(3.5), Inches(4.0),
'ๅฝๆๆฏ่ฝๅไธไธๅกๆดๅฏไบคๅๆถ...', font_size=BODY_SIZE)
add_source(s, 'Source: ...')
18. Temple / House Framework (ๆฎฟๅ ๆกๆถ)
้็จๅบๆฏ: ๅฑ็คบ"ๅฑ้กถ-ๆฏๆฑ-ๅบๅบง"็็ปๆ๏ผๅฆไผไธๆถๆใ่ฝๅไฝ็ณป๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโโโโโโโโโโNAVY๏ผๆฟๆฏ/ๅฑ้กถ๏ผโโโโโโโโโโโโ
โ โโโโโโค โโโโโโค โโโโโโค โโโโโโค โโ
โ โๆฏๆฑโ โๆฏๆฑโ โๆฏๆฑโ โๆฏๆฑโ โโ
โ โ 1 โ โ 2 โ โ 3 โ โ 4 โ โโ
โ โโโโโโงโโโงโโโโโงโโโงโโโโโงโโโงโโโโโงโโโโโโโโโคโ
โ โ ๅบๅบง๏ผๅบ็ก่ฝๅ/ๆๅ๏ผ โโ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ไผไธ่ฝๅๆถๆ')
# Roof
add_rect(s, LM, Inches(1.5), CONTENT_W, Inches(0.8), NAVY)
add_text(s, LM + Inches(0.3), Inches(1.5), CONTENT_W - Inches(0.6), Inches(0.8),
'ไผไธๆฟๆฏ๏ผๆไธบ่กไธ้ขๅ
็ๆฐๅญๅๅนณๅฐ',
font_size=SUB_HEADER_SIZE, font_color=WHITE, bold=True,
anchor=MSO_ANCHOR.MIDDLE, alignment=PP_ALIGN.CENTER)
# Pillars
pillars = ['ไบงๅๅ', 'ๆๆฏๅ', '่ฟ่ฅๅ', 'ๅ็ๅ']
pw = Inches(2.5)
pg = (CONTENT_W - pw * 4) / 3
for i, name in enumerate(pillars):
px = LM + (pw + pg) * i
add_rect(s, px, Inches(2.5), pw, Inches(3.0), BG_GRAY)
add_text(s, px + Inches(0.15), Inches(2.6), pw - Inches(0.3), Inches(0.5),
name, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True,
alignment=PP_ALIGN.CENTER)
# Foundation
add_rect(s, LM, Inches(5.7), CONTENT_W, Inches(0.8), NAVY)
add_text(s, LM + Inches(0.3), Inches(5.7), CONTENT_W - Inches(0.6), Inches(0.8),
'ๅบๅบง๏ผๆฐๆฎ้ฉฑๅจ + ไบบๆไฝ็ณป + ไผไธๆๅ',
font_size=BODY_SIZE, font_color=WHITE, bold=True,
anchor=MSO_ANCHOR.MIDDLE, alignment=PP_ALIGN.CENTER)
add_source(s, 'Source: ...')
็ฑปๅซ D๏ผๅฏนๆฏ่ฏไผฐ
19. Side-by-Side Comparison (ๅทฆๅณๅฏนๆฏ้กต)
้็จๅบๆฏ: ไธคไธชๆนๆก/้้กน/ไบงๅ็ๅนถๆๅฏนๆฏๅๆใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโๆนๆก Aโโโโโโโ โโโๆนๆก Bโโโโโโโ โ
โ โ ๆ ้ข๏ผNAVY๏ผ โ โ ๆ ้ข๏ผNAVY๏ผ โ โ
โ โโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโค โ
โ โ ไผๅฟ โ โ ไผๅฟ โ โ
โ โ ๅฃๅฟ โ โ ๅฃๅฟ โ โ
โ โ ๆๆฌ โ โ ๆๆฌ โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆนๆกๅฏนๆฏๅๆ')
cw = Inches(5.5)
cg = Inches(0.733)
options = [('ๆนๆก A๏ผ่ชๅปบๅข้', ['ๆๅ
ฅๅฏๆง', 'ๅจๆ่พ้ฟ', 'ๆๆฌ ยฅ500ไธ/ๅนด']),
('ๆนๆก B๏ผๅค้จๅไฝ', ['ๅฟซ้ๅฏๅจ', 'ไพ่ตไพๅบๅ', 'ๆๆฌ ยฅ300ไธ/ๅนด'])]
for i, (title, points) in enumerate(options):
cx = LM + (cw + cg) * i
add_rect(s, cx, Inches(1.5), cw, Inches(0.6), NAVY)
add_text(s, cx + Inches(0.15), Inches(1.5), cw - Inches(0.3), Inches(0.6),
title, font_size=SUB_HEADER_SIZE, font_color=WHITE, bold=True,
anchor=MSO_ANCHOR.MIDDLE, alignment=PP_ALIGN.CENTER)
add_rect(s, cx, Inches(2.1), cw, Inches(4.0), BG_GRAY)
add_text(s, cx + Inches(0.3), Inches(2.3), cw - Inches(0.6), Inches(3.5),
[f'โข {p}' for p in points], line_spacing=Pt(10))
add_source(s, 'Source: ...')
20. Before / After (ๅๅๅฏนๆฏ้กต)
้็จๅบๆฏ: ๅฑ็คบๅ้ฉๅๅ็ๅฏนๆฏ๏ผๅฆๆต็จไผๅใ็ป็ปๅ้ฉ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโBG_GRAYโโโโโ โโ> โโโNAVYโโโโโ โ
โ โ ็ฐ็ถ โ โ ็ฎๆ โ โ
โ โ (Before) โ โ (After) โ โ
โ โ ็็นๅ่กจ โ โ ๆน่ฟ็น โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ไป็ฐ็ถๅฐ็ฎๆ ')
hw = Inches(5.0)
# Before
add_rect(s, LM, Inches(1.5), hw, Inches(4.5), BG_GRAY)
add_text(s, LM + Inches(0.3), Inches(1.6), hw - Inches(0.6), Inches(0.5),
'X ็ฐ็ถ๏ผBefore๏ผ', font_size=SUB_HEADER_SIZE, font_color=DARK_GRAY, bold=True)
add_hline(s, LM + Inches(0.3), Inches(2.2), hw - Inches(0.6), LINE_GRAY)
add_text(s, LM + Inches(0.3), Inches(2.4), hw - Inches(0.6), Inches(3.0),
['็็นไธ', '็็นไบ', '็็นไธ'], line_spacing=Pt(10))
# Arrow
add_text(s, LM + hw + Inches(0.1), Inches(3.2), Inches(1.5), Inches(0.5),
'->', font_size=Pt(36), font_color=NAVY, bold=True, alignment=PP_ALIGN.CENTER)
# After
ax = LM + hw + Inches(1.733)
add_rect(s, ax, Inches(1.5), hw, Inches(4.5), NAVY)
add_text(s, ax + Inches(0.3), Inches(1.6), hw - Inches(0.6), Inches(0.5),
'V ็ฎๆ ๏ผAfter๏ผ', font_size=SUB_HEADER_SIZE, font_color=WHITE, bold=True)
add_hline(s, ax + Inches(0.3), Inches(2.2), hw - Inches(0.6), WHITE)
add_text(s, ax + Inches(0.3), Inches(2.4), hw - Inches(0.6), Inches(3.0),
['ๆน่ฟไธ', 'ๆน่ฟไบ', 'ๆน่ฟไธ'], font_color=WHITE, line_spacing=Pt(10))
add_source(s, 'Source: ...')
21. Pros and Cons (ไผๅฃๅๆ้กต)
้็จๅบๆฏ: ่ฏไผฐๆไธชๅณ็ญ/ๆนๆก็ไผๅฟไธ้ฃ้ฉใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ V ไผๅฟ X ้ฃ้ฉ โ
โ โโโโโโโโโโโ โโโโโโโโโโ โ
โ โข ่ฆ็น1 โข ่ฆ็น1 โ
โ โข ่ฆ็น2 โข ่ฆ็น2 โ
โ โข ่ฆ็น3 โข ่ฆ็น3 โ
โ โ
โ โโโBG_GRAY ็ป่ฎบ/ๅปบ่ฎฎโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆนๆก่ฏไผฐ๏ผไผๅฟไธ้ฃ้ฉ')
hw = Inches(5.5)
# Pros column
add_text(s, LM, Inches(1.5), hw, Inches(0.4),
'V ไผๅฟ', font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
add_hline(s, LM, Inches(2.0), hw, NAVY)
add_text(s, LM, Inches(2.2), hw, Inches(2.5),
['โข ไผๅฟ่ฆ็นไธ', 'โข ไผๅฟ่ฆ็นไบ', 'โข ไผๅฟ่ฆ็นไธ'], line_spacing=Pt(10))
# Cons column
cx = LM + hw + Inches(0.733)
add_text(s, cx, Inches(1.5), hw, Inches(0.4),
'X ้ฃ้ฉ', font_size=SUB_HEADER_SIZE, font_color=DARK_GRAY, bold=True)
add_hline(s, cx, Inches(2.0), hw, DARK_GRAY)
add_text(s, cx, Inches(2.2), hw, Inches(2.5),
['โข ้ฃ้ฉ่ฆ็นไธ', 'โข ้ฃ้ฉ่ฆ็นไบ', 'โข ้ฃ้ฉ่ฆ็นไธ'], line_spacing=Pt(10))
# Bottom conclusion
add_rect(s, LM, Inches(5.2), CONTENT_W, Inches(1.5), BG_GRAY)
add_text(s, LM + Inches(0.3), Inches(5.3), Inches(1.5), Inches(0.4),
'็ป่ฎบ', font_size=BODY_SIZE, font_color=NAVY, bold=True)
add_text(s, LM + Inches(0.3), Inches(5.8), CONTENT_W - Inches(0.6), Inches(0.6),
'็ปผๅ่ฏไผฐๅปบ่ฎฎๆๅญ', font_size=BODY_SIZE)
add_source(s, 'Source: ...')
22. Traffic Light / RAG Status (็บข็ปฟ็ฏ็ถๆ้กต)
้็จๅบๆฏ: ๅค้กน็ฎ/ๅคๆจกๅ็็ถๆๆป่ง๏ผ็ปฟ=ๆญฃๅธธใ้ป=ๅ
ณๆณจใ็บข=้ฃ้ฉ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ้กน็ฎ ็ถๆ ่ฟๅบฆ ๅคๆณจ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ้กน็ฎA (G) 85% ๆ่ฎกๅๆจ่ฟ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ้กน็ฎB (Y) 60% ้ๅ
ณๆณจ่ตๆบ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ้กน็ฎC (R) 30% ๅญๅจ้ปๅก โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, '้กน็ฎ็ถๆๆป่ง')
# Header
headers = ['้กน็ฎ', '็ถๆ', '่ฟๅบฆ', 'ๅคๆณจ']
widths = [Inches(3.0), Inches(1.5), Inches(2.0), Inches(5.233)]
hx = LM
for hdr, w in zip(headers, widths):
add_text(s, hx, Inches(1.5), w, Inches(0.4), hdr,
font_size=BODY_SIZE, font_color=MED_GRAY, bold=True)
hx += w
add_hline(s, LM, Inches(2.0), CONTENT_W, BLACK, Pt(1.0))
# Rows with status indicators
rows = [('ไบงๅ็ ๅ', 'NAVY', '85%', 'ๆ่ฎกๅๆจ่ฟ'),
('ๅธๅบๆจๅนฟ', 'MED_GRAY', '60%', '้ๅ
ณๆณจ้ข็ฎ'),
('ๅข้ๆฉๆ', 'DARK_GRAY', '30%', 'ๅญๅจ้ปๅก')]
color_map = {'NAVY': NAVY, 'MED_GRAY': MED_GRAY, 'DARK_GRAY': DARK_GRAY}
ry = Inches(2.2)
for name, status_color, pct, note in rows:
add_text(s, LM, ry, Inches(3.0), Inches(0.6), name, font_size=BODY_SIZE)
add_oval(s, LM + Inches(3.3), ry + Inches(0.05), '', size=Inches(0.35),
bg=color_map[status_color])
add_text(s, LM + Inches(4.5), ry, Inches(2.0), Inches(0.6), pct, font_size=BODY_SIZE)
add_text(s, LM + Inches(6.5), ry, Inches(5.233), Inches(0.6), note, font_size=BODY_SIZE)
ry += Inches(0.7)
add_hline(s, LM, ry, CONTENT_W, LINE_GRAY)
ry += Inches(0.15)
add_source(s, 'Source: ...')
23. Scorecard (่ฎกๅๅก้กต)
้็จๅบๆฏ: ๅฑ็คบๅค้กน่ฏไผฐ็ปดๅบฆ็ๅพๅ/่ฏ็บง๏ผๅฆไพๅบๅ่ฏไผฐใๅข้็ปฉๆใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ่ฏไผฐ็ปดๅบฆ ๅพๅ ่ฏ็บง โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ ๅฎขๆทๆปกๆๅบฆ 92 โโโโโโโโโโ โ
โ ไบงๅ่ดจ้ 85 โโโโโโโโโโ โ
โ ไบคไป้ๅบฆ 78 โโโโโโโโโโ โ
โ ๅๆฐ่ฝๅ 65 โโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, '็ปผๅ่ฏไผฐ่ฎกๅๅก')
headers = ['่ฏไผฐ็ปดๅบฆ', 'ๅพๅ', '่ฏ็บง']
add_text(s, LM, Inches(1.5), Inches(4.0), Inches(0.4), headers[0],
font_size=BODY_SIZE, font_color=MED_GRAY, bold=True)
add_text(s, Inches(5.0), Inches(1.5), Inches(1.5), Inches(0.4), headers[1],
font_size=BODY_SIZE, font_color=MED_GRAY, bold=True)
add_text(s, Inches(7.0), Inches(1.5), Inches(5.5), Inches(0.4), headers[2],
font_size=BODY_SIZE, font_color=MED_GRAY, bold=True)
add_hline(s, LM, Inches(2.0), CONTENT_W, BLACK, Pt(1.0))
items = [('ๅฎขๆทๆปกๆๅบฆ', '92', 0.92), ('ไบงๅ่ดจ้', '85', 0.85),
('ไบคไป้ๅบฆ', '78', 0.78), ('ๅๆฐ่ฝๅ', '65', 0.65)]
ry = Inches(2.2)
bar_max = Inches(5.0)
for name, score, pct in items:
add_text(s, LM, ry, Inches(4.0), Inches(0.5), name, font_size=BODY_SIZE)
add_text(s, Inches(5.0), ry, Inches(1.5), Inches(0.5), score,
font_size=BODY_SIZE, font_color=NAVY, bold=True)
add_rect(s, Inches(7.0), ry + Inches(0.1), bar_max, Inches(0.3), BG_GRAY)
add_rect(s, Inches(7.0), ry + Inches(0.1), Inches(5.0 * pct), Inches(0.3), NAVY)
ry += Inches(0.7)
add_hline(s, LM, ry, CONTENT_W, LINE_GRAY)
ry += Inches(0.15)
add_source(s, 'Source: ...')
็ฑปๅซ E๏ผๅ
ๅฎนๅไบ
24. Executive Summary (ๆง่กๆ่ฆ้กต)
้็จๅบๆฏ: ๆผ็คบๆ็จฟ็ๆ ธๅฟ็ป่ฎบๆฑๆป๏ผ้ๅธธๆพๅจๅผๅคดๆ็ปๅฐพใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโNAVY๏ผๆ ธๅฟ็ป่ฎบ๏ผโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ไธๅฅ่ฏๆ ธๅฟ็ป่ฎบ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ (1) ๆฏๆ่ฎบ็นไธ ่ฏฆ็ป่ฏดๆ โ
โ (2) ๆฏๆ่ฎบ็นไบ ่ฏฆ็ป่ฏดๆ โ
โ (3) ๆฏๆ่ฎบ็นไธ ่ฏฆ็ป่ฏดๆ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆง่กๆ่ฆ')
add_rect(s, LM, Inches(1.4), CONTENT_W, Inches(1.0), NAVY)
add_text(s, LM + Inches(0.3), Inches(1.4), CONTENT_W - Inches(0.6), Inches(1.0),
'ๆ ธๅฟ็ป่ฎบ๏ผไธๅฅ่ฏๆฆๆฌๆ้่ฆ็ๅ็ฐๆๅปบ่ฎฎ',
font_size=SUB_HEADER_SIZE, font_color=WHITE, bold=True,
anchor=MSO_ANCHOR.MIDDLE)
points = [('1', '่ฎบ็นไธๆ ้ข', 'ๆฏๆ่ฎบ็น็่ฏฆ็ป่ฏดๆๆๅญ'),
('2', '่ฎบ็นไบๆ ้ข', 'ๆฏๆ่ฎบ็น็่ฏฆ็ป่ฏดๆๆๅญ'),
('3', '่ฎบ็นไธๆ ้ข', 'ๆฏๆ่ฎบ็น็่ฏฆ็ป่ฏดๆๆๅญ')]
iy = Inches(2.8)
for num, title, desc in points:
add_oval(s, LM, iy, num)
add_text(s, LM + Inches(0.6), iy, Inches(3.5), Inches(0.4),
title, font_size=BODY_SIZE, font_color=NAVY, bold=True)
add_text(s, Inches(5.0), iy, Inches(7.5), Inches(0.4),
desc, font_size=BODY_SIZE)
iy += Inches(0.6)
add_hline(s, LM, iy, CONTENT_W, LINE_GRAY)
iy += Inches(0.3)
add_source(s, 'Source: ...')
25. Key Takeaway with Detail (ๆ ธๅฟๆด่ง้กต)
้็จๅบๆฏ: ๅทฆไพง่ฏฆ็ป่ฎบ่ฟฐ + ๅณไพง็ฐๅบ่ฆ็นๆ็ผ๏ผ็จไบๆ ธๅฟๅ็ฐ้กตใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโBG_GRAYโโโโโโโโโโ
โ ๅทฆไพงๆญฃๆๅ
ๅฎน โ Key Takeaways โโ
โ ่ฏฆ็ปๅๆ่ฎบ่ฟฐ โ 1. ่ฆ็นไธ โโ
โ ๆฐๆฎไธๆฏๆ โ 2. ่ฆ็นไบ โโ
โ โ 3. ่ฆ็นไธ โโ
โ โโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆ ธๅฟๅ็ฐ')
# Left content
add_text(s, LM, Inches(1.5), Inches(7.5), Inches(0.4),
'ๅๆๆ ้ข', font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
add_hline(s, LM, Inches(2.0), Inches(7.5), LINE_GRAY)
add_text(s, LM, Inches(2.2), Inches(7.5), Inches(4.0),
['่ฏฆ็ปๅๆๆฎต่ฝไธ', '', '่ฏฆ็ปๅๆๆฎต่ฝไบ'], line_spacing=Pt(8))
# Right takeaway
tk_x = Inches(9.0)
tk_w = Inches(3.5)
add_rect(s, tk_x, Inches(1.5), tk_w, Inches(5.0), BG_GRAY)
add_text(s, tk_x + Inches(0.2), Inches(1.7), tk_w - Inches(0.4), Inches(0.4),
'Key Takeaways', font_size=BODY_SIZE, font_color=NAVY, bold=True)
add_hline(s, tk_x + Inches(0.2), Inches(2.2), tk_w - Inches(0.4), LINE_GRAY)
add_text(s, tk_x + Inches(0.2), Inches(2.4), tk_w - Inches(0.4), Inches(3.8),
['1. ่ฆ็นไธ', '2. ่ฆ็นไบ', '3. ่ฆ็นไธ'], line_spacing=Pt(10))
add_source(s, 'Source: ...')
26. Quote / Insight Page (ๅผ่จ/ๆด่ง้กต)
้็จๅบๆฏ: ็ชๅบไธๆฎต้่ฆๅผ่จใไธๅฎถ่ง็นๆๆ ธๅฟๆดๅฏใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ โโโโโโโโโโ โ
โ โ
โ "ๅผ่จๅ
ๅฎน๏ผๅฑ
ไธญๆพ็คบ๏ผ โ
โ ๅคงๅญๅทๅผบ่ฐๆ ธๅฟ่ง็น" โ
โ โ
โ โโโโโโโโโโ โ
โ โ ๆฅๆบ/ไฝ่
โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_rect(s, 0, 0, SW, Inches(0.05), NAVY)
add_hline(s, Inches(5.5), Inches(2.0), Inches(2.3), NAVY, Pt(1.5))
add_text(s, Inches(1.5), Inches(2.5), Inches(10.3), Inches(2.5),
'"ๅผ่จๅ
ๅฎน๏ผ็จไบๅผบ่ฐๆไธชๆ ธๅฟ่ง็นๆไธๅฎถๆด่ง"',
font_size=Pt(24), font_color=DARK_GRAY, alignment=PP_ALIGN.CENTER)
add_hline(s, Inches(5.5), Inches(5.3), Inches(2.3), NAVY, Pt(1.5))
add_text(s, Inches(1.5), Inches(5.6), Inches(10.3), Inches(0.5),
'โ ไฝ่
ๅงๅ๏ผๆฅๆบ',
font_size=BODY_SIZE, font_color=MED_GRAY, alignment=PP_ALIGN.CENTER)
27. Two-Column Text (ๅๆ ๆๆฌ้กต)
้็จๅบๆฏ: ๅนณ่กกๅฑ็คบไธคไธชไธป้ข/ๆน้ข๏ผๆฏๅ็ฌ็ซๆ ้ข+ๆญฃๆใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ (A) ๅทฆๆ ๆ ้ข (B) ๅณๆ ๆ ้ข โ
โ โโโโโโโโโโโโโ โโโโโโโโโโโโโ โ
โ ๅทฆๆ ๆญฃๆๅ
ๅฎน ๅณๆ ๆญฃๆๅ
ๅฎน โ
โ ่ฏฆ็ปๅๆ ่ฏฆ็ปๅๆ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๅ็ปดๅบฆๅๆ')
cw = Inches(5.5)
cg = Inches(0.733)
cols = [('A', '็ปดๅบฆไธๆ ้ข', ['ๅๆ่ฆ็นไธ', 'ๅๆ่ฆ็นไบ', 'ๅๆ่ฆ็นไธ']),
('B', '็ปดๅบฆไบๆ ้ข', ['ๅๆ่ฆ็นไธ', 'ๅๆ่ฆ็นไบ', 'ๅๆ่ฆ็นไธ'])]
for i, (letter, title, points) in enumerate(cols):
cx = LM + (cw + cg) * i
add_oval(s, cx, Inches(1.5), letter)
add_text(s, cx + Inches(0.6), Inches(1.5), cw - Inches(0.6), Inches(0.4),
title, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
add_hline(s, cx, Inches(2.0), cw, LINE_GRAY)
add_text(s, cx, Inches(2.2), cw, Inches(4.0),
[f'โข {p}' for p in points], line_spacing=Pt(10))
add_source(s, 'Source: ...')
28. Four-Column Overview (ๅๆ ๆฆ่ง้กต)
้็จๅบๆฏ: ๅไธชๅนถๅ็ปดๅบฆ็ๆฆ่ง๏ผๅฆๅๅคงไธๅก็บฟใๅไธช่ฝๅๅ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ (1) (2) (3) (4) โ
โ ๆ ้ข1 ๆ ้ข2 ๆ ้ข3 ๆ ้ข4 โ
โ โโโโ โโโโ โโโโ โโโโ โ
โ ๆ่ฟฐ ๆ่ฟฐ ๆ่ฟฐ ๆ่ฟฐ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๅๅคงไธๅกๆฟๅ')
items = [('1', 'ๆฟๅไธ', 'ๆ่ฟฐๅ
ๅฎน\nๅ
ณ้ฎๆฐๆฎ'),
('2', 'ๆฟๅไบ', 'ๆ่ฟฐๅ
ๅฎน\nๅ
ณ้ฎๆฐๆฎ'),
('3', 'ๆฟๅไธ', 'ๆ่ฟฐๅ
ๅฎน\nๅ
ณ้ฎๆฐๆฎ'),
('4', 'ๆฟๅๅ', 'ๆ่ฟฐๅ
ๅฎน\nๅ
ณ้ฎๆฐๆฎ')]
cw = Inches(2.7)
cg = (CONTENT_W - cw * 4) / 3
for i, (num, title, desc) in enumerate(items):
cx = LM + (cw + cg) * i
add_rect(s, cx, Inches(1.5), cw, Inches(4.8), BG_GRAY)
add_oval(s, cx + Inches(1.1), Inches(1.65), num)
add_text(s, cx + Inches(0.15), Inches(2.3), cw - Inches(0.3), Inches(0.4),
title, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True,
alignment=PP_ALIGN.CENTER)
add_hline(s, cx + Inches(0.3), Inches(2.8), cw - Inches(0.6), LINE_GRAY)
add_text(s, cx + Inches(0.15), Inches(3.0), cw - Inches(0.3), Inches(3.0),
desc.split('\n'), line_spacing=Pt(8), alignment=PP_ALIGN.CENTER)
add_source(s, 'Source: ...')
็ฑปๅซ F๏ผๆถ้ดๆต็จ
29. Timeline / Roadmap (ๆถ้ด่ฝด/่ทฏ็บฟๅพ)
้็จๅบๆฏ: ๅฑ็คบๆถ้ด็ปดๅบฆ็้็จ็ข่ฎกๅ๏ผๅญฃๅบฆ/ๆๅบฆ/ๅนดๅบฆ่ทฏ็บฟๅพ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ (1)โโโโโโ(2)โโโโโโ(3)โโโโโโ(4) โ
โ Q1 Q2 Q3 Q4 โ
โ ้็จ็ข1 ้็จ็ข2 ้็จ็ข3 ้็จ็ข4 โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, '2026 ๅนดๅบฆ่ทฏ็บฟๅพ')
# Timeline bar
add_hline(s, LM + Inches(0.5), Inches(3.0), Inches(10.7), LINE_GRAY, Pt(2))
milestones = [('Q1', 'ไบงๅ MVP\nๅๅธ'), ('Q2', '็จๆทๅข้ฟ\n่พพๅฐ10ไธ'),
('Q3', '็ๅฉ\n็ช็ ด'), ('Q4', 'ๅฝ้
ๅ\nๆๅฑ')]
spacing = Inches(10.7) / (len(milestones) - 1)
for i, (label, desc) in enumerate(milestones):
mx = LM + Inches(0.5) + spacing * i
add_oval(s, mx - Inches(0.225), Inches(2.775), str(i + 1))
add_text(s, mx - Inches(1.0), Inches(2.0), Inches(2.0), Inches(0.5),
label, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True,
alignment=PP_ALIGN.CENTER)
add_text(s, mx - Inches(1.0), Inches(3.5), Inches(2.0), Inches(1.5),
desc, font_size=BODY_SIZE, alignment=PP_ALIGN.CENTER)
add_source(s, 'Source: ...')
30. Vertical Steps (ๅ็ดๆญฅ้ชค้กต)
้็จๅบๆฏ: ไปไธๅฐไธ็ๆไฝๆญฅ้ชคๆๅฎๆฝ้ถๆฎตใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ (1) ๆญฅ้ชคไธๆ ้ข ่ฏฆ็ป่ฏดๆ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (2) ๆญฅ้ชคไบๆ ้ข ่ฏฆ็ป่ฏดๆ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (3) ๆญฅ้ชคไธๆ ้ข ่ฏฆ็ป่ฏดๆ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (4) ๆญฅ้ชคๅๆ ้ข ่ฏฆ็ป่ฏดๆ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๅฎๆฝๆญฅ้ชค')
steps = [('1', '้ๆฑๅๆ', 'ๆทฑๅ
ฅ่ฐ็ ็จๆท้ๆฑไธไธๅก็็น'),
('2', 'ๆนๆก่ฎพ่ฎก', 'ๅถๅฎๆๆฏๆถๆไธๅฎๆฝ่ฎกๅ'),
('3', 'ๅผๅๅฎๆฝ', 'ๅ้ถๆฎต่ฟญไปฃไบคไปๆ ธๅฟๅ่ฝ'),
('4', 'ไธ็บฟ่ฟ่ฅ', '็ๆงๆๆๅนถๆ็ปญไผๅ')]
iy = Inches(1.5)
for num, title, desc in steps:
add_oval(s, LM, iy, num)
add_text(s, LM + Inches(0.6), iy, Inches(3.5), Inches(0.4),
title, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True)
add_text(s, Inches(5.0), iy, Inches(7.5), Inches(0.4),
desc, font_size=BODY_SIZE)
iy += Inches(0.6)
add_hline(s, LM, iy, CONTENT_W, LINE_GRAY)
iy += Inches(0.5)
add_source(s, 'Source: ...')
31. Cycle / Loop (ๅพช็ฏๅพ้กต)
้็จๅบๆฏ: ้ญ็ฏๆต็จๆ่ฟญไปฃๅพช็ฏ๏ผๅฆ PDCAใๆๆท่ฟญไปฃใๅ้ฆๅพช็ฏ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโ้ถๆฎต1โโโ โ
โ โ โ โ
โ โ้ถๆฎต4โโ โโ้ถๆฎต2โ ๅณไพง่ฏดๆ โ
โ โ โโโโโโโโโโโโ โ โ
โ โโโโโโโ โโโโโโโ โ
โ โโโ้ถๆฎต3โโโ โ
โ โโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆ็ปญๆน่ฟๅพช็ฏ๏ผPDCA๏ผ')
phases = [('Plan\n่ฎกๅ', Inches(2.8), Inches(1.5)),
('Do\nๆง่ก', Inches(5.0), Inches(3.0)),
('Check\nๆฃๆฅ', Inches(2.8), Inches(4.5)),
('Act\nๆน่ฟ', Inches(0.6), Inches(3.0))]
for i, (label, px, py) in enumerate(phases):
fill = NAVY if i == 0 else BG_GRAY
tc = WHITE if i == 0 else NAVY
add_rect(s, LM + px, py, Inches(2.2), Inches(1.2), fill)
add_text(s, LM + px + Inches(0.1), py + Inches(0.1), Inches(2.0), Inches(1.0),
label, font_size=SUB_HEADER_SIZE, font_color=tc, bold=True,
alignment=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
# Arrows between phases (text arrows)
add_text(s, LM + Inches(4.5), Inches(2.0), Inches(1.0), Inches(0.5),
'->', font_size=Pt(24), font_color=NAVY, alignment=PP_ALIGN.CENTER)
add_text(s, LM + Inches(5.0), Inches(4.0), Inches(1.0), Inches(0.5),
'v', font_size=Pt(24), font_color=NAVY, alignment=PP_ALIGN.CENTER)
add_text(s, LM + Inches(2.0), Inches(5.0), Inches(1.0), Inches(0.5),
'<-', font_size=Pt(24), font_color=NAVY, alignment=PP_ALIGN.CENTER)
add_text(s, LM + Inches(0.8), Inches(2.0), Inches(1.0), Inches(0.5),
'^', font_size=Pt(24), font_color=NAVY, alignment=PP_ALIGN.CENTER)
# Right side explanation
add_rect(s, Inches(8.5), Inches(1.5), Inches(4.0), Inches(5.0), BG_GRAY)
add_text(s, Inches(8.8), Inches(1.7), Inches(3.4), Inches(0.4),
'ๅพช็ฏ่ฆ็น', font_size=BODY_SIZE, font_color=NAVY, bold=True)
add_text(s, Inches(8.8), Inches(2.3), Inches(3.4), Inches(3.5),
['ๆฏไธช้ถๆฎต็่ฏดๆ...'], line_spacing=Pt(10))
add_source(s, 'Source: ...')
32. Funnel (ๆผๆๅพ้กต)
้็จๅบๆฏ: ่ฝฌๅๆผๆ๏ผๅฆ้ๅฎๆผๆใ็จๆท่ฝฌๅ่ทฏๅพ๏ผใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ 100% โ
โ โ ่ฎค็ฅ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโค 60% โ
โ โ ๅ
ด่ถฃ โ โ
โ โโโโโโโโโโโโโโโโโโค 35% โ
โ โ ่ดญไนฐ โ โ
โ โโโโโโโโโโโโค 15% โ
โ โ ็ๅญ โ โ
โ โโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, '็จๆท่ฝฌๅๆผๆ')
stages = [('่ฎค็ฅ', '100,000', 1.0), ('ๅ
ด่ถฃ', '60,000', 0.6),
('่ดญไนฐ', '35,000', 0.35), ('็ๅญ', '15,000', 0.15)]
max_w = Inches(8.0)
fy = Inches(1.6)
for i, (name, count, pct) in enumerate(stages):
w = max_w * pct
fx = Inches(6.666) - w / 2 # center
fill = NAVY if i == 0 else BG_GRAY
tc = WHITE if i == 0 else NAVY
add_rect(s, fx, fy, w, Inches(1.0), fill)
add_text(s, fx + Inches(0.2), fy, w - Inches(0.4), Inches(1.0),
name, font_size=SUB_HEADER_SIZE, font_color=tc, bold=True,
anchor=MSO_ANCHOR.MIDDLE, alignment=PP_ALIGN.CENTER)
add_text(s, fx + w + Inches(0.3), fy + Inches(0.2), Inches(2.5), Inches(0.5),
f'{count} ({int(pct*100)}%)', font_size=BODY_SIZE, font_color=NAVY, bold=True)
fy += Inches(1.2)
add_source(s, 'Source: ...')
็ฑปๅซ G๏ผๅข้ไธ้ข
33. Meet the Team (ๅข้ไป็ป้กต)
้็จๅบๆฏ: ๅข้ๆๅ/ๆ ธๅฟ้ซ็ฎก/้กน็ฎ็ป็ฎไปใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโBGโโโ โโBGโโโ โโBGโโโ โ
โ โ(ๅคดๅ)โ โ(ๅคดๅ)โ โ(ๅคดๅ)โ โ
โ โ ๅงๅ โ โ ๅงๅ โ โ ๅงๅ โ โ
โ โ ่ไฝ โ โ ่ไฝ โ โ ่ไฝ โ โ
โ โ ็ฎไป โ โ ็ฎไป โ โ ็ฎไป โ โ
โ โโโโโโโโ โโโโโโโโ โโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆ ธๅฟๅข้')
members = [('ๅผ ไธ', 'CEO', '15ๅนด่กไธ็ป้ช\nๅXXๅ
ฌๅธVP'),
('ๆๅ', 'CTO', 'ๆๆฏๆถๆไธๅฎถ\nๅXXๅ
ฌๅธๆป็'),
('็ไบ', 'COO', '่ฟ่ฅ็ฎก็ไธๅฎถ\nๅXXๅ
ฌๅธ่ด่ดฃไบบ')]
cw = Inches(3.5)
cg = (CONTENT_W - cw * 3) / 2
for i, (name, role, bio) in enumerate(members):
cx = LM + (cw + cg) * i
add_rect(s, cx, Inches(1.5), cw, Inches(5.0), BG_GRAY)
add_oval(s, cx + Inches(1.25), Inches(1.7), name[0], size=Inches(1.0))
add_text(s, cx + Inches(0.15), Inches(2.9), cw - Inches(0.3), Inches(0.4),
name, font_size=SUB_HEADER_SIZE, font_color=NAVY, bold=True,
alignment=PP_ALIGN.CENTER)
add_text(s, cx + Inches(0.15), Inches(3.4), cw - Inches(0.3), Inches(0.4),
role, font_size=BODY_SIZE, font_color=MED_GRAY, alignment=PP_ALIGN.CENTER)
add_hline(s, cx + Inches(0.3), Inches(3.9), cw - Inches(0.6), LINE_GRAY)
add_text(s, cx + Inches(0.15), Inches(4.1), cw - Inches(0.3), Inches(2.0),
bio.split('\n'), line_spacing=Pt(8), alignment=PP_ALIGN.CENTER)
add_source(s, 'Source: ...')
34. Case Study (ๆกไพ็ ็ฉถ้กต)
้็จๅบๆฏ: ๅฑ็คบๆๅๆกไพ๏ผๆ"ๆ
ๅข-่กๅจ-็ปๆ"็ปๆ็ป็ปใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Action Title โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโSituationโโโ โโApproachโโโ โResultโโ โ
โ โ ่ๆฏ/ๆๆ โ โ ้ๅ่กๅจ โ โ ๆๆ โ โ
โ โ โ โ โ โ โ โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโ โโโโโโโโโ โ
โ โ
โ โโโBG_GRAY ๅฎขๆท่ฏไปท/ๅ
ณ้ฎๆๆ โโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
s = prs.slides.add_slide(BL)
add_action_title(s, 'ๆกไพ็ ็ฉถ๏ผXX้กน็ฎ')
sections = [('S', 'Situation\nๆ
ๅข', 'ๅฎขๆท้ขไธด็\nๆๆๆ่ฟฐ'),
('A', 'Approach\nๆนๆณ', 'ๆไปฌ้ๅ็\n่งฃๅณๆนๆก'),
('R', 'Result\nๆๆ', 'ๅๅพ็้ๅ\nๆๆๆฐๆฎ')]