File size: 6,777 Bytes
6787477
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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)}")