Spaces:
Sleeping
Sleeping
import os | |
from pptx import Presentation | |
from pptx.util import Inches | |
from PIL import Image | |
from utils import remove_all_slides | |
from logger import LOG # 引入日志模块 | |
def format_text(paragraph, text): | |
""" | |
格式化文本,处理加粗内容。假设 ** 包围的文本表示需要加粗。 | |
""" | |
while '**' in text: | |
start = text.find('**') | |
end = text.find('**', start + 2) | |
if start != -1 and end != -1: | |
# 添加加粗之前的普通文本 | |
if start > 0: | |
run = paragraph.add_run() | |
run.text = text[:start] | |
# 添加加粗文本 | |
bold_run = paragraph.add_run() | |
bold_run.text = text[start + 2:end] | |
bold_run.font.bold = True # 设置加粗 | |
# 处理剩余文本 | |
text = text[end + 2:] | |
else: | |
break | |
# 添加剩余的普通文本 | |
if text: | |
run = paragraph.add_run() | |
run.text = text | |
def insert_image_centered_in_placeholder(new_slide, image_path): | |
""" | |
将图片插入到 Slide 中,使其中心与 placeholder 的中心对齐。 | |
如果图片尺寸超过 placeholder,则进行缩小适配。 | |
在插入成功后删除 placeholder。 | |
""" | |
# 构建图片的绝对路径 | |
image_full_path = os.path.join(os.getcwd(), image_path) | |
# 检查图片是否存在 | |
if not os.path.exists(image_full_path): | |
LOG.warning(f"图片路径 '{image_full_path}' 不存在,跳过此图片。") | |
return | |
# 打开图片并获取其大小(以像素为单位) | |
with Image.open(image_full_path) as img: | |
img_width_px, img_height_px = img.size | |
# 遍历找到图片的 placeholder(type 18 表示图片 placeholder) | |
for shape in new_slide.placeholders: | |
if shape.placeholder_format.type == 18: | |
placeholder_width = shape.width | |
placeholder_height = shape.height | |
placeholder_left = shape.left | |
placeholder_top = shape.top | |
# 计算 placeholder 的中心点 | |
placeholder_center_x = placeholder_left + placeholder_width / 2 | |
placeholder_center_y = placeholder_top + placeholder_height / 2 | |
# 图片的宽度和高度转换为 PowerPoint 的单位 (Inches) | |
img_width = Inches(img_width_px / 96) # 假设图片 DPI 为 96 | |
img_height = Inches(img_height_px / 96) | |
# 如果图片的宽度或高度超过 placeholder,按比例缩放图片 | |
if img_width > placeholder_width or img_height > placeholder_height: | |
scale = min(placeholder_width / img_width, placeholder_height / img_height) | |
img_width *= scale | |
img_height *= scale | |
# 计算图片左上角位置,使其中心对准 placeholder 中心 | |
left = placeholder_center_x - img_width / 2 | |
top = placeholder_center_y - img_height / 2 | |
# 插入图片到指定位置并设定缩放后的大小 | |
new_slide.shapes.add_picture(image_full_path, left, top, width=img_width, height=img_height) | |
LOG.debug(f"图片已插入,并以 placeholder 中心对齐,路径: {image_full_path}") | |
# 移除占位符 | |
sp = shape._element # 获取占位符的 XML 元素 | |
sp.getparent().remove(sp) # 从父元素中删除 | |
LOG.debug("已删除图片的 placeholder") | |
break | |
# 生成 PowerPoint 演示文稿 | |
def generate_presentation(powerpoint_data, template_path: str, output_path: str): | |
# 检查模板文件是否存在 | |
if not os.path.exists(template_path): | |
LOG.error(f"模板文件 '{template_path}' 不存在。") # 记录错误日志 | |
raise FileNotFoundError(f"模板文件 '{template_path}' 不存在。") | |
prs = Presentation(template_path) # 加载 PowerPoint 模板 | |
remove_all_slides(prs) # 清除模板中的所有幻灯片 | |
prs.core_properties.title = powerpoint_data.title # 设置 PowerPoint 的核心标题 | |
# 遍历所有幻灯片数据,生成对应的 PowerPoint 幻灯片 | |
for slide in powerpoint_data.slides: | |
# 确保布局索引不超出范围,超出则使用默认布局 | |
if slide.layout_id >= len(prs.slide_layouts): | |
slide_layout = prs.slide_layouts[0] | |
else: | |
slide_layout = prs.slide_layouts[slide.layout_id] | |
new_slide = prs.slides.add_slide(slide_layout) # 添加新的幻灯片 | |
# 设置幻灯片标题 | |
if new_slide.shapes.title: | |
new_slide.shapes.title.text = slide.content.title | |
LOG.debug(f"设置幻灯片标题: {slide.content.title}") | |
# 添加文本内容 | |
for shape in new_slide.shapes: | |
# 只处理非标题的文本框 | |
if shape.has_text_frame and not shape == new_slide.shapes.title: | |
text_frame = shape.text_frame | |
text_frame.clear() # 清除原有内容 | |
# 直接使用第一个段落,不添加新的段落,避免额外空行 | |
first_paragraph = text_frame.paragraphs[0] | |
# 将要点内容作为项目符号列表添加到文本框中 | |
for point in slide.content.bullet_points: | |
# 第一个要点覆盖初始段落,其他要点添加新段落 | |
paragraph = first_paragraph if point == slide.content.bullet_points[0] else text_frame.add_paragraph() | |
paragraph.level = point["level"] # 设置项目符号的级别 | |
format_text(paragraph, point["text"]) # 调用 format_text 方法来处理加粗文本 | |
LOG.debug(f"添加列表项: {paragraph.text},级别: {paragraph.level}") | |
break | |
# 插入图片 | |
if slide.content.image_path: | |
insert_image_centered_in_placeholder(new_slide, slide.content.image_path) | |
# 保存生成的 PowerPoint 文件 | |
prs.save(output_path) | |
LOG.info(f"演示文稿已保存到 '{output_path}'") | |