134 lines
4.8 KiB
Python
Executable File
134 lines
4.8 KiB
Python
Executable File
import logging
|
|
import os
|
|
import torch
|
|
|
|
from pathlib import Path
|
|
from PIL import Image
|
|
from pptx import Presentation
|
|
from pptx.dml.color import RGBColor
|
|
from pptx.enum.text import PP_ALIGN
|
|
from pptx.oxml import parse_xml
|
|
from pptx.util import Inches, Pt
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class Pptx:
|
|
|
|
duration_template = "oxml_duration_template.xml"
|
|
pixels_per_inch = 96 # Subjective fixed value
|
|
|
|
def __init__(self, output_folder):
|
|
self.output_folder = output_folder
|
|
Path(self.output_folder).mkdir(parents=True, exist_ok=True)
|
|
|
|
def get_next_filepath(self, title):
|
|
"""
|
|
Generate the next filepath available by appending `_X`
|
|
"""
|
|
extension = 'pptx'
|
|
filename = title.lower().replace(' ', '_')
|
|
filepath = f"{self.output_folder}/{filename}.{extension}"
|
|
if os.path.isfile(filepath):
|
|
log.info(f"file exsists {filepath}")
|
|
i = 1
|
|
filepath = f"{self.output_folder}/{filename}_{i}.{extension}"
|
|
while os.path.isfile(filepath):
|
|
log.info(filepath)
|
|
i = i+1
|
|
filepath = f"{self.output_folder}/{filename}_{i}.{extension}"
|
|
return filepath
|
|
|
|
def get_duration_fragment(self, duration=20):
|
|
"""
|
|
Get the duration xml fragment to add to each slide with automatic transition
|
|
duration: slide duration in seconds
|
|
returns: the xml fragment to add to the slide
|
|
"""
|
|
template_path = os.path.join(os.path.dirname(__file__), self.duration_template)
|
|
with open(template_path) as f:
|
|
xml_fragment = f.read()
|
|
xml_fragment = xml_fragment.replace("{duration}", str(duration * 1000))
|
|
return parse_xml(xml_fragment)
|
|
|
|
def add_presentation_title(self, root, title):
|
|
"""
|
|
Generate the first presentaiton slide
|
|
"""
|
|
title_slide = root.slides.add_slide(root.slide_layouts[0])
|
|
|
|
main_title = title_slide.shapes.title
|
|
main_title.width = root.slide_width
|
|
main_title.left = 0
|
|
main_title.top = root.slide_height//2
|
|
main_title.text = title
|
|
main_title.text_frame.paragraphs[0].font.color.rgb = RGBColor(188, 194, 200)
|
|
|
|
sub_title = title_slide.placeholders[1]
|
|
sub_title.width = root.slide_width
|
|
sub_title.left = 0
|
|
sub_title.top = root.slide_height - Pt(12) - Inches(0.2)
|
|
sub_title.text = "Generated using ComfyUI-PechaKucha"
|
|
sub_title.text_frame.paragraphs[0].font.color.rgb = RGBColor(188, 194, 200)
|
|
sub_title.text_frame.paragraphs[0].font.size = Pt(12)
|
|
|
|
background = title_slide.background
|
|
fill = background.fill
|
|
fill.solid()
|
|
fill.fore_color.rgb = RGBColor(2, 3, 3)
|
|
|
|
def generate(self, title, slide_duration, images, show_titles, titles):
|
|
"""
|
|
Generate the powerpoint presentation file
|
|
Ref for slide types:
|
|
0 -> title and subtitle
|
|
1 -> title and content
|
|
2 -> section header
|
|
3 -> two content
|
|
4 -> Comparison
|
|
5 -> Title only
|
|
6 -> Blank
|
|
7 -> Content with caption
|
|
8 -> Pic with caption
|
|
"""
|
|
log.debug(f"{title} Pecha Kucha generation with {len(images)} images and duration {slide_duration}")
|
|
|
|
root = Presentation()
|
|
first_image = Image.open(images[0])
|
|
width, height = first_image.size
|
|
root.slide_width = Inches(width//self.pixels_per_inch)
|
|
root.slide_height = Inches(height//self.pixels_per_inch)
|
|
|
|
self.add_presentation_title(root, title)
|
|
|
|
if show_titles:
|
|
assert(len(images) == len(titles))
|
|
|
|
slide_layout = root.slide_layouts[0]
|
|
left = top = Inches(0)
|
|
width = root.slide_width
|
|
|
|
for i, image in enumerate(images):
|
|
slide = root.slides.add_slide(slide_layout)
|
|
|
|
pic = slide.shapes.add_picture(image, left, top, width)
|
|
# Send picture to background
|
|
slide.shapes._spTree.remove(pic._element)
|
|
slide.shapes._spTree.insert(2, pic._element)
|
|
|
|
if show_titles:
|
|
slide.shapes.title.text = titles[i]
|
|
slide.shapes.title.width = root.slide_width
|
|
slide.shapes.title.left = 0
|
|
slide.shapes.title.top = root.slide_height - Pt(16) - Inches(0.5)
|
|
slide.shapes.title.text_frame.paragraphs[0].font.color.rgb = RGBColor(2, 3, 3)
|
|
slide.shapes.title.text_frame.paragraphs[0].font.size = Pt(16)
|
|
|
|
# Adding automatic transition
|
|
slide.element.insert(-1, self.get_duration_fragment(slide_duration))
|
|
|
|
filepath = self.get_next_filepath(title)
|
|
root.save(filepath)
|
|
|
|
return filepath
|