AutoPresent / SlidesLib /ppt_gen.py
para-lost's picture
add slides lib
2dd1349
from pptx import Presentation
from pptx.util import Inches as _Inches, Pt as _Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_AUTO_SIZE
from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE, MSO_SHAPE_TYPE
from io import BytesIO
ARROW_ADD = '"""<a:tailEnd type="arrow" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"/>"""'
class SlideAgent:
def __init__(self, slide_width=13.33, slide_height=7.5):
"""Initialize a new presentation with specified slide dimensions in inches."""
self.prs = Presentation()
self.prs.slide_width = self._inches(slide_width)
self.prs.slide_height = self._inches(slide_height)
self.slide = None
def _inches(self, val):
"""Helper method to convert to Inches."""
return _Inches(val)
def _points(self, val):
"""Helper method to convert to Points."""
return _Pt(val)
# ------- Slide APIs -------
def add_slide(self, layout=0):
"""Create a new slide with a specific layout."""
slide_layout = self.prs.slide_layouts[layout]
self.slide = self.prs.slides.add_slide(slide_layout)
# ------- Text APIs -------
def add_title(self, text, font_size=44, font_color=(0, 0, 0)):
"""Add a title to the slide with a custom font size (in points) and font color (RGB tuple)."""
title_shape = self.slide.shapes.title
title_shape.text = text
self._format_text(title_shape.text_frame, self._points(font_size), RGBColor(*font_color))
def add_text(self, text, top, left, width, height, font_size=20, bold=False, color=(0, 0, 0), background_color=None, auto_size=True):
"""Add a text box at a specified location with custom text settings and optional background color."""
# Create the text box shape
text_box = self.slide.shapes.add_textbox(self._inches(left), self._inches(top), self._inches(width), self._inches(height))
# Set background color if provided
if background_color:
text_box.fill.solid()
text_box.fill.fore_color.rgb = RGBColor(*background_color)
else:
text_box.fill.background() # No fill if no color is specified
# Handle line breaks and adjust height
lines = text.split("\n")
adjusted_height = height * len(lines) # Adjust height based on the number of lines
text_box.height = self._inches(adjusted_height)
# Set text and format it
text_frame = text_box.text_frame
text_frame.word_wrap = True
if auto_size:
text_frame.auto_size = MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT # Automatically fit the text box to the text
self._format_paragraph(text_frame, text, self._points(font_size), bold, RGBColor(*color))
def add_bullet_points(self, bullet_points, top, left, width, height, font_size=18, color=(0, 0, 0)):
"""Add a text box with bullet points."""
text_box = self.slide.shapes.add_textbox(self._inches(left), self._inches(top), self._inches(width), self._inches(height))
text_frame = text_box.text_frame
text_frame.word_wrap = True
text_frame.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
for point in bullet_points:
p = text_frame.add_paragraph()
p.text = point
self._format_text(p, self._points(font_size), RGBColor(*color))
p.level = bullet_points.index(point)
# ------- Image APIs -------
def add_image(self, image_path, top, left, width, height):
"""Add an image at a specified location."""
self.slide.shapes.add_picture(image_path, self._inches(left), self._inches(top), self._inches(width), self._inches(height))
def add_image_centered(self, image_path, image_width, image_height):
"""Add an image centered on the slide."""
slide_width = self.prs.slide_width.inches
slide_height = self.prs.slide_height.inches
left = (slide_width - image_width) / 2
top = (slide_height - image_height) / 2
self.add_image(image_path, top, left, image_width, image_height)
# ------- Shape APIs -------
def add_shape(self, shape_type, top, left, width, height, fill_color=None):
"""Add a shape to the slide, supporting MSO_AUTO_SHAPE_TYPE."""
if isinstance(shape_type, str):
# Check if the shape type is a valid string, otherwise raise an error
try:
shape_type = getattr(MSO_AUTO_SHAPE_TYPE, shape_type.upper())
except AttributeError:
raise ValueError(f"Invalid shape type: {shape_type}. Must be a valid MSO_AUTO_SHAPE_TYPE.")
# Now create the shape with the validated or passed enum type
shape = self.slide.shapes.add_shape(shape_type, self._inches(left), self._inches(top), self._inches(width), self._inches(height))
if fill_color:
shape.fill.solid()
shape.fill.fore_color.rgb = RGBColor(*fill_color)
def add_straight_arrow(self, start_x, start_y, end_x, end_y):
connector = self.slide.shapes.add_connector("MSO_CONNECTOR.STRAIGHT", start_x, start_y, end_x, end_y)
def add_straight_line(self, start_x, start_y, end_x, end_y):
connector = self.slide.shapes.add_connector("MSO_CONNECTOR.STRAIGHT", start_x, start_y, end_x, end_y)
line_elem = connector.line._get_or_add_ln()
line_elem.append(parse_xml({ARROW_ADD}))
# ------- Table APIs -------
def add_table(self, rows, cols, top, left, width, height, column_widths=None):
"""Add a table to the slide."""
table = self.slide.shapes.add_table(rows, cols, left, top, width, height).table
if column_widths:
for idx, col_width in enumerate(column_widths):
table.columns[idx].width = Inches(col_width)
return table
# ------- Helper APIs -------
def set_background_color(self, color):
"""Set background color for the current slide."""
background = self.slide.background
fill = background.fill
fill.solid()
fill.fore_color.rgb = color
def duplicate_slide(self, slide_index):
"""Duplicate a slide by index."""
template_slide = self.prs.slides[slide_index]
new_slide = self.prs.slides.add_slide(template_slide.slide_layout)
for shape in template_slide.shapes:
self._copy_shape(shape, new_slide)
def save_presentation(self, file_name):
"""Save the PowerPoint presentation."""
self.prs.save(file_name)
# ------- Internal Helper Methods -------
def _format_paragraph(self, text_frame, text, font_size, bold, color):
"""Helper function to format text within a text frame."""
p = text_frame.add_paragraph()
p.text = text
p.font.size = font_size
p.font.bold = bold
p.font.color.rgb = color
def _format_text(self, text_frame, font_size, font_color):
"""Helper function to format text in a text frame."""
for paragraph in text_frame.paragraphs:
paragraph.font.size = font_size
paragraph.font.color.rgb = font_color
def _copy_shape(self, shape, slide):
"""Copy a shape from one slide to another."""
if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
image = BytesIO(shape.image.blob)
slide.shapes.add_picture(image, shape.left, shape.top, shape.width, shape.height)
elif shape.has_text_frame:
new_shape = slide.shapes.add_textbox(shape.left, shape.top, shape.width, shape.height)
new_shape.text = shape.text
self._format_text(new_shape.text_frame, shape.text_frame.paragraphs[0].font.size, shape.text_frame.paragraphs[0].font.color.rgb)