PPTCreator / slide_themes.py
dseditor's picture
CommitForLocalAndCloud
09c9f6f verified
# slide_themes.py
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
class SlideThemeManager:
def __init__(self):
# 16:9 簡報尺寸 (單位:英吋)
self.slide_width = Inches(13.333) # 16:9 寬度
self.slide_height = Inches(7.5) # 16:9 高度
# 版型配置 - 重新設計,減少留白,增加多樣化配色,改善文字對比度
self.themes = {
"商務專業": {
"bg_color": RGBColor(248, 252, 255), # 更淺的藍白背景
"title_color": RGBColor(8, 47, 91), # 更深的藍色標題
"text_color": RGBColor(33, 37, 41), # 深黑文字,提高對比度
"accent_color": RGBColor(52, 144, 220), # 藍色強調
"secondary_color": RGBColor(255, 255, 255), # 純白輔助背景
"content_bg_color": RGBColor(255, 255, 255), # 內容框背景色
"layout": "image_right",
"has_gradient": True,
"gradient_start": RGBColor(248, 252, 255),
"gradient_end": RGBColor(235, 245, 255),
# 縮小邊距,擴大內容區域
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 0.2, "top": 1.3, "width": 6.8, "height": 5.9},
"image_area": {"left": 7.2, "top": 1.3, "width": 5.9, "height": 4.8}
},
"科技創新": {
"bg_color": RGBColor(18, 28, 42), # 深藍黑背景
"title_color": RGBColor(120, 255, 235), # 更亮的青綠標題
"text_color": RGBColor(245, 248, 252), # 更亮的淺色文字
"accent_color": RGBColor(255, 107, 107), # 紅色強調
"secondary_color": RGBColor(35, 47, 62), # 深灰輔助
"content_bg_color": RGBColor(35, 47, 62), # 內容框背景色
"layout": "image_bottom",
"has_gradient": True,
"gradient_start": RGBColor(18, 28, 42),
"gradient_end": RGBColor(35, 47, 62),
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 0.2, "top": 1.3, "width": 12.9, "height": 3.2},
"image_area": {"left": 0.2, "top": 4.7, "width": 12.9, "height": 2.6}
},
"創意設計": {
"bg_color": RGBColor(255, 245, 250), # 更淺的粉色背景
"title_color": RGBColor(136, 14, 79), # 更深的紫色標題
"text_color": RGBColor(33, 33, 33), # 深黑文字,提高對比度
"accent_color": RGBColor(255, 152, 0), # 橙色強調
"secondary_color": RGBColor(255, 255, 255), # 純白輔助
"content_bg_color": RGBColor(255, 255, 255), # 內容框背景色
"layout": "image_left",
"has_gradient": True,
"gradient_start": RGBColor(255, 245, 250),
"gradient_end": RGBColor(250, 224, 235),
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 6.0, "top": 1.3, "width": 7.1, "height": 5.9},
"image_area": {"left": 0.2, "top": 1.3, "width": 5.6, "height": 4.8}
},
"教育學術": {
"bg_color": RGBColor(252, 255, 252), # 更淺的綠白背景
"title_color": RGBColor(46, 125, 50), # 更深的綠色標題
"text_color": RGBColor(33, 37, 41), # 深黑文字
"accent_color": RGBColor(255, 193, 7), # 黃色強調
"secondary_color": RGBColor(255, 255, 255), # 純白輔助
"content_bg_color": RGBColor(255, 255, 255), # 內容框背景色
"layout": "image_top",
"has_gradient": True,
"gradient_start": RGBColor(252, 255, 252),
"gradient_end": RGBColor(240, 248, 240),
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 0.2, "top": 4.0, "width": 12.9, "height": 3.3},
"image_area": {"left": 0.2, "top": 1.3, "width": 12.9, "height": 2.5}
},
"現代簡約": {
"bg_color": RGBColor(255, 255, 255), # 純白背景
"title_color": RGBColor(33, 37, 41), # 深黑標題
"text_color": RGBColor(33, 37, 41), # 深黑文字,提高對比度
"accent_color": RGBColor(0, 123, 255), # 藍色強調
"secondary_color": RGBColor(248, 249, 250), # 淺灰輔助
"content_bg_color": RGBColor(248, 249, 250), # 內容框背景色
"layout": "image_right",
"has_gradient": False,
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 0.2, "top": 1.3, "width": 6.8, "height": 5.9},
"image_area": {"left": 7.2, "top": 1.3, "width": 5.9, "height": 4.8}
},
"溫暖橙色": {
"bg_color": RGBColor(255, 252, 245), # 更淺的橙白背景
"title_color": RGBColor(191, 54, 12), # 更深的橙色標題
"text_color": RGBColor(33, 37, 41), # 深黑文字,提高對比度
"accent_color": RGBColor(255, 138, 101), # 淺橙強調
"secondary_color": RGBColor(255, 255, 255), # 純白輔助
"content_bg_color": RGBColor(255, 255, 255), # 內容框背景色
"layout": "image_bottom",
"has_gradient": True,
"gradient_start": RGBColor(255, 252, 245),
"gradient_end": RGBColor(255, 243, 224),
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 0.2, "top": 1.3, "width": 12.9, "height": 3.2},
"image_area": {"left": 0.2, "top": 4.7, "width": 12.9, "height": 2.6}
},
"深邃紫色": {
"bg_color": RGBColor(67, 18, 125), # 深紫背景
"title_color": RGBColor(224, 164, 234), # 更亮的淺紫標題
"text_color": RGBColor(255, 255, 255), # 純白文字
"accent_color": RGBColor(255, 204, 0), # 金黃強調
"secondary_color": RGBColor(98, 26, 142), # 中紫輔助
"content_bg_color": RGBColor(98, 26, 142), # 內容框背景色
"layout": "image_left",
"has_gradient": True,
"gradient_start": RGBColor(67, 18, 125),
"gradient_end": RGBColor(118, 30, 152),
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 6.0, "top": 1.3, "width": 7.1, "height": 5.9},
"image_area": {"left": 0.2, "top": 1.3, "width": 5.6, "height": 4.8}
},
"清新藍綠": {
"bg_color": RGBColor(240, 255, 252), # 更淺的藍綠背景
"title_color": RGBColor(0, 105, 92), # 更深的藍綠標題
"text_color": RGBColor(33, 37, 41), # 深黑文字
"accent_color": RGBColor(255, 111, 97), # 珊瑚紅強調
"secondary_color": RGBColor(255, 255, 255), # 純白輔助
"content_bg_color": RGBColor(255, 255, 255), # 內容框背景色
"layout": "image_top",
"has_gradient": True,
"gradient_start": RGBColor(240, 255, 252),
"gradient_end": RGBColor(224, 242, 235),
"title_area": {"left": 0.2, "top": 0.1, "width": 12.9, "height": 1.0},
"content_area": {"left": 0.2, "top": 4.0, "width": 12.9, "height": 3.3},
"image_area": {"left": 0.2, "top": 1.3, "width": 12.9, "height": 2.5}
}
}
# 圖片風格
self.image_styles = {
"professional": "business professional corporate clean",
"creative": "creative artistic colorful vibrant",
"minimalist": "minimal clean simple white space",
"modern": "modern contemporary sleek design",
"natural": "natural outdoor organic environment",
"technology": "technology digital modern tech innovation"
}
def get_theme(self, theme_name):
"""獲取指定主題"""
return self.themes.get(theme_name, self.themes["商務專業"])
def get_all_theme_names(self):
"""獲取所有主題名稱"""
return list(self.themes.keys())
def apply_background(self, slide, theme):
"""應用背景色彩或漸變"""
try:
# 獲取投影片背景
background = slide.background
fill = background.fill
if theme.get("has_gradient", False):
# 應用漸變背景
fill.gradient()
fill.gradient_angle = 45.0 # 45度角漸變
# 設置漸變色彩停靠點
gradient_stops = fill.gradient_stops
gradient_stops[0].color.rgb = theme["gradient_start"]
gradient_stops[1].color.rgb = theme["gradient_end"]
else:
# 應用純色背景
fill.solid()
fill.fore_color.rgb = theme["bg_color"]
except Exception as e:
print(f"應用背景錯誤: {e}")
def add_decorative_elements(self, slide, theme):
"""添加裝飾性元素(邊框、圖形等)"""
try:
# 根據主題添加裝飾線條或圖形
if theme.get("layout") == "image_right":
# 在左側內容區域添加垂直裝飾線
line = slide.shapes.add_connector(
connector_type=1, # 直線
begin_x=Inches(0.1),
begin_y=Inches(1.3),
end_x=Inches(0.1),
end_y=Inches(7.2)
)
line.line.color.rgb = theme["accent_color"]
line.line.width = Pt(4)
elif theme.get("layout") == "image_bottom":
# 在標題下方添加水平裝飾線
line = slide.shapes.add_connector(
connector_type=1, # 直線
begin_x=Inches(0.2),
begin_y=Inches(1.2),
end_x=Inches(13.1),
end_y=Inches(1.2)
)
line.line.color.rgb = theme["accent_color"]
line.line.width = Pt(3)
except Exception as e:
print(f"添加裝飾元素錯誤: {e}")
def format_title(self, shape, theme, font_size, font_getter=None):
"""格式化標題"""
try:
paragraph = shape.text_frame.paragraphs[0]
if font_getter:
paragraph.font.name = font_getter()
else:
paragraph.font.name = self.get_font_name()
paragraph.font.size = Pt(font_size)
paragraph.font.color.rgb = theme["title_color"]
paragraph.alignment = PP_ALIGN.LEFT
paragraph.font.bold = True
# 為深色背景的主題添加陰影效果
if self.is_dark_background(theme):
try:
# 添加文字陰影增強可讀性
paragraph.font.color.rgb = RGBColor(255, 255, 255)
except:
pass
except Exception as e:
print(f"格式化標題錯誤: {e}")
def format_content(self, paragraph, theme, font_size, font_getter=None):
"""格式化內容"""
try:
if font_getter:
paragraph.font.name = font_getter()
else:
paragraph.font.name = self.get_font_name()
paragraph.font.size = Pt(font_size)
paragraph.font.color.rgb = theme["text_color"]
paragraph.space_before = Pt(6)
paragraph.space_after = Pt(6)
paragraph.line_spacing = 1.2
except Exception as e:
print(f"格式化內容錯誤: {e}")
def get_font_name(self):
"""獲取中文字型名稱"""
import os
# 檢查是否有自定義中文字型檔案
font_path = os.path.join(os.path.dirname(__file__), "cht.ttf")
if os.path.exists(font_path):
return "cht"
else:
return "Arial Unicode MS"
def is_dark_background(self, theme):
"""判斷是否為深色背景"""
bg_color = theme["bg_color"]
# 計算亮度(簡單的RGB平均值判斷)
# RGBColor 物件沒有 .r 屬性,需要使用 ._color_val 或直接從 RGB 值計算
try:
# 嘗試獲取 RGB 值
r = bg_color._color_val & 0xFF
g = (bg_color._color_val >> 8) & 0xFF
b = (bg_color._color_val >> 16) & 0xFF
brightness = (r + g + b) / 3
return brightness < 128
except:
# 如果無法獲取,使用保守判斷
# 檢查是否為已知的深色主題
dark_themes = ["科技創新", "深邃紫色"]
theme_name = getattr(theme, 'name', '')
return theme_name in dark_themes
def setup_slide_background_and_layout(self, slide, theme):
"""設置投影片背景和基本布局"""
# 應用背景
self.apply_background(slide, theme)
# 添加裝飾元素
self.add_decorative_elements(slide, theme)
def create_content_box_with_background(self, slide, theme, content_area):
"""創建帶背景的內容框"""
try:
# 在內容區域添加背景框
bg_shape = slide.shapes.add_shape(
1, # 矩形
Inches(content_area["left"] - 0.1),
Inches(content_area["top"] - 0.1),
Inches(content_area["width"] + 0.2),
Inches(content_area["height"] + 0.2)
)
# 設置背景框樣式
fill = bg_shape.fill
fill.solid()
# 使用主題中定義的內容背景色
if "content_bg_color" in theme:
fill.fore_color.rgb = theme["content_bg_color"]
else:
# 回退邏輯
if self.is_dark_background(theme):
fill.fore_color.rgb = RGBColor(40, 40, 40) # 深色背景用深灰框
else:
fill.fore_color.rgb = RGBColor(255, 255, 255) # 淺色背景用白色框
# 設置邊框
bg_shape.line.color.rgb = theme["accent_color"]
bg_shape.line.width = Pt(2)
# 對於深色背景,增加透明度
if self.is_dark_background(theme):
fill.transparency = 0.2 # 20% 透明度
else:
fill.transparency = 0.05 # 5% 透明度
return bg_shape
except Exception as e:
print(f"創建內容背景框錯誤: {e}")
return None