Initial commit: organize Primary song slides repo
One folder per song under songs/. Tracks PPTX slide files and Python generation scripts. PDFs and videos excluded (too large for git; originals are in Google Drive). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,124 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user