Spaces:
Runtime error
Runtime error
import os | |
from typing import List, Dict, Union | |
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.shapes.base import BaseShape | |
class SlideDeck: | |
""" | |
A class to create and manage PowerPoint presentations. | |
""" | |
def __init__(self, output_folder: str = "generated"): | |
""" | |
Initialize the SlideDeck class. | |
Args: | |
output_folder (str): The folder where the presentation will be saved. | |
""" | |
self.prs = Presentation() | |
self.output_folder = output_folder | |
def add_slide(self, slide_data: Dict[str, Union[str, List[str], List[List[str]]]]) -> None: | |
""" | |
Add a new slide to the presentation. | |
Args: | |
slide_data (Dict): A dictionary containing the slide content. | |
""" | |
prs = self.prs | |
bullet_slide_layout = prs.slide_layouts[1] | |
slide = prs.slides.add_slide(bullet_slide_layout) | |
shapes = slide.shapes | |
# Add title | |
title_shape = shapes.title | |
title_shape.text = slide_data.get("title_text", "") | |
# Add body text | |
if "text" in slide_data: | |
body_shape = shapes.placeholders[1] | |
tf = body_shape.text_frame | |
for bullet in slide_data.get("text", []): | |
p = tf.add_paragraph() | |
p.text = bullet | |
p.level = 0 | |
if "p1" in slide_data: | |
p = tf.add_paragraph() | |
p.text = slide_data.get("p1", "") | |
p.level = 1 | |
# Add images | |
if "img_path" in slide_data: | |
cur_left = 6 | |
for img_path in slide_data.get("img_path", []): | |
try: | |
top = Inches(2) | |
left = Inches(cur_left) | |
height = Inches(4) | |
pic = slide.shapes.add_picture(img_path, left, top, height=height) | |
cur_left += 1 | |
except FileNotFoundError: | |
print(f"Warning: Image file not found: {img_path}") | |
# Add table | |
if "table" in slide_data: | |
self.add_table(slide, slide_data["table"]) | |
def add_title_slide(self, title_page_data: Dict[str, str]) -> None: | |
""" | |
Add a title slide to the presentation. | |
Args: | |
title_page_data (Dict): A dictionary containing the title slide content. | |
""" | |
prs = self.prs | |
title_slide_layout = prs.slide_layouts[0] | |
slide = prs.slides.add_slide(title_slide_layout) | |
title = slide.shapes.title | |
subtitle = slide.placeholders[1] | |
if "title_text" in title_page_data: | |
title.text = title_page_data.get("title_text") | |
if "subtitle_text" in title_page_data: | |
subtitle.text = title_page_data.get("subtitle_text") | |
def add_table(self, slide, table_data: List[List[str]]) -> None: | |
""" | |
Add a table to the given slide. | |
Args: | |
slide (Slide): The slide to add the table to. | |
table_data (List[List[str]]): The data for the table. | |
""" | |
# Determine the maximum number of columns | |
max_cols = max(len(row) for row in table_data) | |
rows = len(table_data) | |
left = Inches(0.5) | |
top = Inches(1.5) | |
width = Inches(9) | |
height = Inches(5.5) | |
table = slide.shapes.add_table(rows, max_cols, left, top, width, height).table | |
# Set column widths | |
first_col_width = 3.5 | |
remaining_width = 9 - first_col_width | |
other_col_width = remaining_width / (max_cols - 1) if max_cols > 1 else remaining_width | |
table.columns[0].width = Inches(first_col_width) | |
for i in range(1, max_cols): | |
table.columns[i].width = Inches(other_col_width) | |
# Populate the table with data | |
for i, row in enumerate(table_data): | |
if len(row) < max_cols: | |
row.insert(0, " ") | |
for j, cell in enumerate(row): | |
table.cell(i, j).text = str(cell) | |
paragraph = table.cell(i, j).text_frame.paragraphs[0] | |
paragraph.font.size = Pt(10) | |
paragraph.alignment = PP_ALIGN.CENTER if j > 0 or i == 0 else PP_ALIGN.LEFT | |
# Style the header row | |
for cell in table.rows[0].cells: | |
cell.fill.solid() | |
cell.fill.fore_color.rgb = RGBColor(0, 112, 192) # Blue color | |
cell.text_frame.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) # White text | |
cell.text_frame.paragraphs[0].font.bold = True | |
# Style the first column | |
for i in range(1, rows): | |
cell = table.cell(i, 0) | |
cell.fill.solid() | |
cell.fill.fore_color.rgb = RGBColor(230, 230, 230) # Light gray | |
cell.text_frame.paragraphs[0].font.bold = True | |
def create_presentation(self, title_slide_info: Dict[str, str], slide_pages_data: List[Dict[str, Union[str, List[str], List[List[str]]]]] = []) -> str: | |
""" | |
Create a complete presentation. | |
Args: | |
title_slide_info (Dict): Information for the title slide. | |
slide_pages_data (List[Dict]): Data for all other slides. | |
Returns: | |
str: The file path of the saved presentation. | |
Raises: | |
OSError: If there's an error creating or saving the file. | |
ValueError: If the input data is invalid. | |
""" | |
try: | |
# Generate file name from title | |
file_name = title_slide_info.get("title_text", "presentation").\ | |
lower().replace(",", "").replace(":", "").replace(" ", "-") | |
file_name += ".pptx" | |
file_name = os.path.join(self.output_folder, file_name) | |
# Create output folder if it doesn't exist | |
os.makedirs(self.output_folder, exist_ok=True) | |
# Add title slide | |
self.add_title_slide(title_slide_info) | |
# Add content slides | |
for slide_data in slide_pages_data: | |
self.add_slide(slide_data) | |
# Save the presentation | |
self.prs.save(file_name) | |
return file_name | |
except OSError as e: | |
raise OSError(f"Error creating or saving the presentation: {str(e)}") | |
except ValueError as e: | |
raise ValueError(f"Invalid input data: {str(e)}") | |
except Exception as e: | |
raise Exception(f"An unexpected error occurred: {str(e)}") |