import fitz from pptx import Presentation from pptx.util import Inches, Pt from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN import io PDF_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\EDITABLEMyOwnSacredGrove.pdf" OUT_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\MyOwnSacredGrove.pptx" DPI = 96 SLIDE_W = Inches(13.333) # 16:9 widescreen SLIDE_H = Inches(7.5) TEXT_BOX_H = Inches(2.1) FONT_SIZE = Pt(44) TEXT_COLOR = RGBColor(0x1B, 0x43, 0x32) # deep forest green # (0-based PDF page index, lyric line) # Pages: 0=boy→grove, 1=boy reading+praying, 2=boy praying+pillar of light, # 3=HF+Jesus plain, 4=HF+Jesus in clouds, 5=two kids praying by bed (warm), # 6=child with headphones/globe, 7=two kids praying+HF in clouds, # 8=two kids praying by bed (same scene), 9=confused boy+arrows, # 10=boy at fork in road, 11=wheelchair child thinking of Jesus, # 12=Black child praying+golden light, 13=girl reading+boy pondering+stars, # 14=girl praying+HF in clouds, 15=boy with clock, 16=three children, # 17=girl holding light+stars, 18=girl by window, 19=two kids praying in grove SLIDES = [ # Verse 1 (0, "Joseph Smith went to a grove full of trees"), (1, "Seeking God's wisdom, he fell to his knees"), (2, "As he pled with the heavens, the sky filled with light"), (3, "And the Father appeared with His Son Jesus Christ"), (4, "Standing above in the air\nComing to answer his prayer"), # Chorus (19, "I will find my own sacred grove"), (6, "Away from all of the noise of the world"), (8, "I will turn to prayer"), (7, "For I know He's there"), (19, "I will find my own sacred grove"), # Verse 2 (9, "So many choices with so much at stake"), (10, "Life's full of pathways, but which should I take?"), (12, "If I lift up in prayer in the name of the Son"), (13, "Through the power of the Holy Ghost, answers will come"), (14, "Heavenly Father is there\nReady to answer my prayer"), # Chorus (19, "I will find my own sacred grove"), (6, "Away from all of the noise of the world"), (5, "I will turn to prayer"), (7, "For I know He's there"), (19, "I will find my own sacred grove"), # Bridge (15, "Sometimes the answers take time"), (16, "So I'll listen in heart and mind"), (17, "Revelation will come my way"), (18, "As I wait patiently in faith"), # Chorus (19, "I will find my own sacred grove"), (6, "Away from all of the noise of the world"), (8, "I will turn to prayer"), (7, "For I know He's there"), (19, "I will find my own sacred grove"), # Ending (11, "I will find my own sacred grove"), ] def render_page(page): mat = fitz.Matrix(DPI / 72, DPI / 72) pix = page.get_pixmap(matrix=mat, alpha=False) return io.BytesIO(pix.tobytes("jpeg", jpg_quality=85)), pix.width, pix.height def add_slide(doc, prs, blank_layout, page_idx, lyric): page = doc[page_idx] img_bytes, pw, ph = render_page(page) slide = prs.slides.add_slide(blank_layout) # Scale image to fit within slide, centered aspect = pw / ph h = SLIDE_H w = int(h * aspect) if w > SLIDE_W: w = SLIDE_W h = int(w / aspect) left = (SLIDE_W - w) // 2 top = (SLIDE_H - h) // 2 slide.shapes.add_picture(img_bytes, left, top, width=w, height=h) # Lyric text box at the bottom of the slide tb_top = SLIDE_H - TEXT_BOX_H txBox = slide.shapes.add_textbox(Inches(0.25), tb_top, SLIDE_W - Inches(0.5), TEXT_BOX_H) tf = txBox.text_frame tf.word_wrap = True lines = lyric.split("\n") for i, line in enumerate(lines): p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() p.alignment = PP_ALIGN.CENTER run = p.add_run() run.text = line run.font.bold = True run.font.size = FONT_SIZE run.font.color.rgb = TEXT_COLOR print(f" Slide {len(prs.slides):2d} (pg {page_idx+1:2d}): {lyric[:55]}") def main(): doc = fitz.open(PDF_PATH) prs = Presentation() prs.slide_width = SLIDE_W prs.slide_height = SLIDE_H blank_layout = prs.slide_layouts[6] for page_idx, lyric in SLIDES: add_slide(doc, prs, blank_layout, page_idx, lyric) prs.save(OUT_PATH) print(f"\nSaved: {OUT_PATH} ({len(SLIDES)} slides)") if __name__ == "__main__": main()