Spaces:
Sleeping
Sleeping
File size: 6,607 Bytes
395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 395f954 8613cb4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
import io
from typing import List, Tuple, Dict, Any, Optional
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE
from PIL import Image
import matplotlib.pyplot as plt
def _add_logo(prs: Presentation, slide, logo_bytes: Optional[bytes]):
if not logo_bytes:
return
img = Image.open(io.BytesIO(logo_bytes)).convert("RGBA")
# Resize to fit width<=2.0in, height<=1.0in
max_w, max_h = Inches(2.0), Inches(1.0)
w, h = img.size
ratio = min(max_w / max(w, 1), max_h / max(h, 1))
new_size = (max(1, int(w * ratio)), max(1, int(h * ratio)))
resized = img.resize(new_size)
b = io.BytesIO()
resized.save(b, format="PNG")
b.seek(0)
# Place top-right with small margin
left = prs.slide_width - max_w - Inches(0.5)
top = Inches(0.2)
slide.shapes.add_picture(b, left, top)
def _apply_theme_bg(slide, rgb):
fill = slide.background.fill
fill.solid()
fill.fore_color.rgb = RGBColor(*rgb)
def _title_slide(prs, title_text: str, theme_rgb, logo_bytes):
slide_layout = prs.slide_layouts[0] # Title slide
slide = prs.slides.add_slide(slide_layout)
title = slide.shapes.title
subtitle = slide.placeholders[1]
title.text = title_text
subtitle.text = "自動生成プレゼンテーション"
# Accent band
_apply_theme_bg(slide, theme_rgb)
# White rounded rectangle for readability
left = Inches(0.6)
top = Inches(1.8)
width = prs.slide_width - Inches(1.2)
height = Inches(2.2)
box = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.ROUNDED_RECTANGLE, left, top, width, height)
box.fill.solid()
box.fill.fore_color.rgb = RGBColor(255, 255, 255)
box.line.color.rgb = RGBColor(0, 0, 0)
box.line.transparency = 0.8
# Reposition title
title.left = left + Inches(0.3)
title.top = top + Inches(0.3)
title.width = width - Inches(0.6)
title.height = Inches(1.4)
for p in title.text_frame.paragraphs:
p.font.size = Pt(40)
p.font.bold = True
# Subtitle
subtitle.left = left + Inches(0.3)
subtitle.top = top + Inches(1.6)
subtitle.width = width - Inches(0.6)
subtitle.height = Inches(0.8)
for p in subtitle.text_frame.paragraphs:
p.font.size = Pt(16)
p.font.bold = False
_add_logo(prs, slide, logo_bytes)
def _summary_slide(prs, summary: str):
if not summary:
return
slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title and Content
slide.shapes.title.text = "エグゼクティブサマリー"
tf = slide.placeholders[1].text_frame
tf.clear()
# Split summary into bullet-ish lines
lines = [ln.strip() for ln in summary.splitlines() if ln.strip()]
if not lines:
lines = [summary]
for i, ln in enumerate(lines):
p = tf.add_paragraph() if i > 0 else tf.paragraphs[0]
p.text = ln
p.level = 0
def _section_slide(prs, title: str, bullets: List[str]):
slide = prs.slides.add_slide(prs.slide_layouts[1])
slide.shapes.title.text = title[:90]
tf = slide.placeholders[1].text_frame
tf.clear()
if not bullets:
bullets = ["(要点なし)"]
for i, b in enumerate(bullets[:12]):
p = tf.add_paragraph() if i > 0 else tf.paragraphs[0]
p.text = b
p.level = 0
def _table_slide(prs, title: str, pairs: List[tuple]):
slide = prs.slides.add_slide(prs.slide_layouts[5]) # Title Only
slide.shapes.title.text = title
rows = len(pairs) + 1
cols = 2
left = Inches(0.5)
top = Inches(1.8)
width = prs.slide_width - Inches(1.0)
height = prs.slide_height - Inches(2.6)
table = slide.shapes.add_table(rows, cols, left, top, width, height).table
table.cell(0, 0).text = "項目"
table.cell(0, 1).text = "値"
for r, (k, v) in enumerate(pairs, start=1):
table.cell(r, 0).text = str(k)
table.cell(r, 1).text = str(v)
def _chart_slide(prs, title: str, series: List[tuple]):
slide = prs.slides.add_slide(prs.slide_layouts[5]) # Title Only
slide.shapes.title.text = title
# Build bar chart via matplotlib and embed as image
labels = [x[0] for x in series]
values = [x[1] for x in series]
fig = plt.figure(figsize=(8, 4.5))
plt.bar(range(len(values)), values)
plt.xticks(range(len(labels)), labels, rotation=20, ha='right')
plt.tight_layout()
buf = io.BytesIO()
fig.savefig(buf, format='png', dpi=200)
plt.close(fig)
buf.seek(0)
left = Inches(0.5)
top = Inches(1.6)
width = prs.slide_width - Inches(1.0)
height = prs.slide_height - Inches(2.2)
slide.shapes.add_picture(buf, left, top, width=width, height=height)
def _add_footer(prs, theme_rgb):
for idx, slide in enumerate(prs.slides, start=1):
left = Inches(0.3)
top = prs.slide_height - Inches(0.4)
width = prs.slide_width - Inches(0.6)
height = Inches(0.3)
shp = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left, top, width, height)
shp.fill.solid()
shp.fill.fore_color.rgb = RGBColor(*theme_rgb)
shp.line.fill.background()
# Slide number text box
tx = slide.shapes.add_textbox(prs.slide_width - Inches(1.0), top - Inches(0.05), Inches(0.8), Inches(0.3))
tf = tx.text_frame
p = tf.paragraphs[0]
p.text = f"{idx}"
p.font.size = Pt(10)
p.alignment = PP_ALIGN.RIGHT
def build_presentation(output_path: str,
title: str,
theme_rgb: tuple,
logo_bytes: Optional[bytes],
executive_summary: Optional[str],
sections: List[Tuple[str, str]],
bullets_by_section: Dict[int, List[str]],
tables: List[Dict[str, Any]],
charts: List[Dict[str, Any]]):
prs = Presentation()
# Title slide
_title_slide(prs, title, theme_rgb, logo_bytes)
# Summary
_summary_slide(prs, executive_summary)
# Sections
for idx, (sec_title, _body) in enumerate(sections):
bullets = bullets_by_section.get(idx, [])
_section_slide(prs, sec_title, bullets)
# Tables
for tbl in tables:
_table_slide(prs, tbl.get("title", "表"), tbl.get("pairs", []))
# Charts
for ch in charts:
_chart_slide(prs, ch.get("title", "チャート"), ch.get("series", []))
_add_footer(prs, theme_rgb)
prs.save(output_path)
|