Add songs: Anytime Anywhere, Pray in Faith, Search Ponder and Pray
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,111 @@
|
|||||||
|
from pptx import Presentation
|
||||||
|
from pptx.util import Inches, Pt
|
||||||
|
from pptx.dml.color import RGBColor
|
||||||
|
from pptx.enum.text import PP_ALIGN
|
||||||
|
|
||||||
|
OUT_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\songs\anytime-anywhere\AnytimeAnywhere.pptx"
|
||||||
|
|
||||||
|
SLIDE_W = Inches(13.333)
|
||||||
|
SLIDE_H = Inches(7.5)
|
||||||
|
|
||||||
|
# Calm, prayerful palette: deep twilight blue background, soft gold accents
|
||||||
|
TWILIGHT = RGBColor(0x1C, 0x2E, 0x4A)
|
||||||
|
GOLD = RGBColor(0xE8, 0xC5, 0x6A)
|
||||||
|
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
|
||||||
|
|
||||||
|
TITLE = "Anytime, Anywhere"
|
||||||
|
|
||||||
|
VERSES = [
|
||||||
|
(
|
||||||
|
"Verse 1",
|
||||||
|
"I can pray to my Heav'nly Father\n"
|
||||||
|
"Anytime, anywhere.\n"
|
||||||
|
"For He always cares,\n"
|
||||||
|
"And He hears my prayers—\n"
|
||||||
|
"Anytime, anywhere."
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Verse 2",
|
||||||
|
"I can listen for heav'nly guidance\n"
|
||||||
|
"Anytime, anywhere.\n"
|
||||||
|
"The Spirit will guide\n"
|
||||||
|
"As I seek His light—\n"
|
||||||
|
"Anytime, anywhere."
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def set_background_color(slide, color):
|
||||||
|
fill = slide.background.fill
|
||||||
|
fill.solid()
|
||||||
|
fill.fore_color.rgb = color
|
||||||
|
|
||||||
|
|
||||||
|
def add_gold_rule(slide, top):
|
||||||
|
rule = slide.shapes.add_shape(
|
||||||
|
1, # rectangle
|
||||||
|
Inches(1.5), top, SLIDE_W - Inches(3.0), Inches(0.06)
|
||||||
|
)
|
||||||
|
rule.fill.solid()
|
||||||
|
rule.fill.fore_color.rgb = GOLD
|
||||||
|
rule.line.fill.background()
|
||||||
|
|
||||||
|
|
||||||
|
def add_slide(prs, blank_layout, verse_label, verse_text):
|
||||||
|
slide = prs.slides.add_slide(blank_layout)
|
||||||
|
set_background_color(slide, TWILIGHT)
|
||||||
|
|
||||||
|
add_gold_rule(slide, Inches(0.55))
|
||||||
|
|
||||||
|
# Song title
|
||||||
|
title_box = slide.shapes.add_textbox(Inches(0.5), Inches(0.65), SLIDE_W - Inches(1.0), Inches(0.85))
|
||||||
|
p = title_box.text_frame.paragraphs[0]
|
||||||
|
p.alignment = PP_ALIGN.CENTER
|
||||||
|
run = p.add_run()
|
||||||
|
run.text = TITLE
|
||||||
|
run.font.size = Pt(36)
|
||||||
|
run.font.color.rgb = GOLD
|
||||||
|
run.font.bold = True
|
||||||
|
|
||||||
|
# Verse label
|
||||||
|
label_box = slide.shapes.add_textbox(Inches(0.5), Inches(1.45), SLIDE_W - Inches(1.0), Inches(0.5))
|
||||||
|
p2 = label_box.text_frame.paragraphs[0]
|
||||||
|
p2.alignment = PP_ALIGN.CENTER
|
||||||
|
run2 = p2.add_run()
|
||||||
|
run2.text = verse_label
|
||||||
|
run2.font.size = Pt(22)
|
||||||
|
run2.font.color.rgb = WHITE
|
||||||
|
run2.font.italic = True
|
||||||
|
|
||||||
|
add_gold_rule(slide, Inches(1.95))
|
||||||
|
|
||||||
|
# Verse lyrics
|
||||||
|
lyric_box = slide.shapes.add_textbox(Inches(0.5), Inches(2.3), SLIDE_W - Inches(1.0), Inches(4.8))
|
||||||
|
tf = lyric_box.text_frame
|
||||||
|
tf.word_wrap = True
|
||||||
|
for i, line in enumerate(verse_text.split("\n")):
|
||||||
|
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.size = Pt(40)
|
||||||
|
run.font.color.rgb = WHITE
|
||||||
|
|
||||||
|
print(f" Added: {verse_label}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
prs = Presentation()
|
||||||
|
prs.slide_width = SLIDE_W
|
||||||
|
prs.slide_height = SLIDE_H
|
||||||
|
blank_layout = prs.slide_layouts[6]
|
||||||
|
|
||||||
|
for label, text in VERSES:
|
||||||
|
add_slide(prs, blank_layout, label, text)
|
||||||
|
|
||||||
|
prs.save(OUT_PATH)
|
||||||
|
print(f"\nSaved: {OUT_PATH} ({len(VERSES)} slides)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
from pptx import Presentation
|
||||||
|
from pptx.util import Inches, Pt
|
||||||
|
from pptx.dml.color import RGBColor
|
||||||
|
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
|
||||||
|
from pptx.enum.shapes import MSO_SHAPE
|
||||||
|
|
||||||
|
OUT_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\songs\anytime-anywhere\AnytimeAnywhereFlowchart.pptx"
|
||||||
|
|
||||||
|
SLIDE_W = Inches(13.333)
|
||||||
|
SLIDE_H = Inches(7.5)
|
||||||
|
|
||||||
|
TWILIGHT = RGBColor(0x1C, 0x2E, 0x4A)
|
||||||
|
GOLD = RGBColor(0xE8, 0xC5, 0x6A)
|
||||||
|
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
|
||||||
|
|
||||||
|
TITLE = "Anytime, Anywhere"
|
||||||
|
|
||||||
|
# Flow: A -> R, then R <-> B loop (R -> B -> back to R)
|
||||||
|
# The refrain box R appears once; both other lines feed into it.
|
||||||
|
BOX_R = "Anytime,\nanywhere"
|
||||||
|
|
||||||
|
# (verse label, box A text, box B text)
|
||||||
|
VERSES = [
|
||||||
|
("Verse 1",
|
||||||
|
"I can pray to my\nHeav'nly Father",
|
||||||
|
"For He always cares,\nand He hears my prayers"),
|
||||||
|
("Verse 2",
|
||||||
|
"I can listen for\nheav'nly guidance",
|
||||||
|
"The Spirit will guide\nas I seek His light"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def add_box(slide, left, top, w, h, text, is_refrain):
|
||||||
|
box = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, left, top, w, h)
|
||||||
|
box.fill.solid()
|
||||||
|
box.fill.fore_color.rgb = GOLD if is_refrain else WHITE
|
||||||
|
box.line.color.rgb = GOLD
|
||||||
|
box.line.width = Pt(2.5)
|
||||||
|
|
||||||
|
tf = box.text_frame
|
||||||
|
tf.word_wrap = True
|
||||||
|
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
|
||||||
|
for i, line in enumerate(text.split("\n")):
|
||||||
|
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.size = Pt(28)
|
||||||
|
run.font.bold = True
|
||||||
|
run.font.color.rgb = TWILIGHT
|
||||||
|
return box
|
||||||
|
|
||||||
|
|
||||||
|
def add_arrow(slide, shape_type, left, top, w, h):
|
||||||
|
arrow = slide.shapes.add_shape(shape_type, left, top, w, h)
|
||||||
|
arrow.fill.solid()
|
||||||
|
arrow.fill.fore_color.rgb = GOLD
|
||||||
|
arrow.line.fill.background()
|
||||||
|
return arrow
|
||||||
|
|
||||||
|
|
||||||
|
def add_flow_slide(prs, verse_label, box_a, box_b):
|
||||||
|
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
||||||
|
|
||||||
|
fill = slide.background.fill
|
||||||
|
fill.solid()
|
||||||
|
fill.fore_color.rgb = TWILIGHT
|
||||||
|
|
||||||
|
# Title
|
||||||
|
title_box = slide.shapes.add_textbox(Inches(0.5), Inches(0.35), SLIDE_W - Inches(1.0), Inches(0.9))
|
||||||
|
p = title_box.text_frame.paragraphs[0]
|
||||||
|
p.alignment = PP_ALIGN.CENTER
|
||||||
|
run = p.add_run()
|
||||||
|
run.text = TITLE
|
||||||
|
run.font.size = Pt(40)
|
||||||
|
run.font.color.rgb = GOLD
|
||||||
|
run.font.bold = True
|
||||||
|
|
||||||
|
# Verse label under the title
|
||||||
|
label_box = slide.shapes.add_textbox(Inches(0.5), Inches(1.05), SLIDE_W - Inches(1.0), Inches(0.45))
|
||||||
|
p2 = label_box.text_frame.paragraphs[0]
|
||||||
|
p2.alignment = PP_ALIGN.CENTER
|
||||||
|
run2 = p2.add_run()
|
||||||
|
run2.text = verse_label
|
||||||
|
run2.font.size = Pt(20)
|
||||||
|
run2.font.color.rgb = WHITE
|
||||||
|
run2.font.italic = True
|
||||||
|
|
||||||
|
# Layout: A on top of the left column, R below it, B to the right of R.
|
||||||
|
# A flows down into R; R and B loop (R -> B on top, B -> R underneath).
|
||||||
|
box_w = Inches(4.8)
|
||||||
|
gap_x = Inches(1.7)
|
||||||
|
left_x = (SLIDE_W - (box_w * 2 + gap_x)) / 2
|
||||||
|
right_x = left_x + box_w + gap_x
|
||||||
|
|
||||||
|
a_y = Inches(1.7)
|
||||||
|
a_h = Inches(1.6)
|
||||||
|
r_y = Inches(4.3)
|
||||||
|
r_h = Inches(1.9)
|
||||||
|
|
||||||
|
add_box(slide, left_x, a_y, box_w, a_h, box_a, False)
|
||||||
|
add_box(slide, left_x, r_y, box_w, r_h, BOX_R, True)
|
||||||
|
add_box(slide, right_x, r_y, box_w, r_h, box_b, False)
|
||||||
|
|
||||||
|
# A -> R: down arrow
|
||||||
|
arrow_w = Inches(0.7)
|
||||||
|
arrow_l = Inches(0.85)
|
||||||
|
gap_y = r_y - (a_y + a_h)
|
||||||
|
add_arrow(slide, MSO_SHAPE.DOWN_ARROW,
|
||||||
|
left_x + (box_w - arrow_w) / 2,
|
||||||
|
a_y + a_h + (gap_y - arrow_l) / 2,
|
||||||
|
arrow_w, arrow_l)
|
||||||
|
|
||||||
|
# R <-> B loop: right arrow above, left arrow below
|
||||||
|
loop_arrow_w = Inches(1.3)
|
||||||
|
loop_x = left_x + box_w + (gap_x - loop_arrow_w) / 2
|
||||||
|
add_arrow(slide, MSO_SHAPE.RIGHT_ARROW,
|
||||||
|
loop_x, r_y + r_h * 0.22 - arrow_w / 2, loop_arrow_w, arrow_w)
|
||||||
|
add_arrow(slide, MSO_SHAPE.LEFT_ARROW,
|
||||||
|
loop_x, r_y + r_h * 0.78 - arrow_w / 2, loop_arrow_w, arrow_w)
|
||||||
|
|
||||||
|
print(f" Added: {verse_label}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
prs = Presentation()
|
||||||
|
prs.slide_width = SLIDE_W
|
||||||
|
prs.slide_height = SLIDE_H
|
||||||
|
|
||||||
|
for verse_label, box_a, box_b in VERSES:
|
||||||
|
add_flow_slide(prs, verse_label, box_a, box_b)
|
||||||
|
|
||||||
|
prs.save(OUT_PATH)
|
||||||
|
print(f"\nSaved: {OUT_PATH} ({len(VERSES)} slides)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,39 @@
|
|||||||
|
import fitz
|
||||||
|
from pptx import Presentation
|
||||||
|
from pptx.util import Inches
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
PDF_PATH = os.path.join(HERE, "PrayInFaith.pdf")
|
||||||
|
OUT_PATH = os.path.join(HERE, "PrayInFaith.pptx")
|
||||||
|
DPI = 150
|
||||||
|
|
||||||
|
SLIDE_W = Inches(13.333) # widescreen landscape
|
||||||
|
SLIDE_H = Inches(7.5)
|
||||||
|
|
||||||
|
doc = fitz.open(PDF_PATH)
|
||||||
|
prs = Presentation()
|
||||||
|
prs.slide_width = SLIDE_W
|
||||||
|
prs.slide_height = SLIDE_H
|
||||||
|
blank_layout = prs.slide_layouts[6]
|
||||||
|
|
||||||
|
for i, page in enumerate(doc, start=1):
|
||||||
|
mat = fitz.Matrix(DPI / 72, DPI / 72)
|
||||||
|
pix = page.get_pixmap(matrix=mat, alpha=False)
|
||||||
|
img = io.BytesIO(pix.tobytes("png"))
|
||||||
|
|
||||||
|
slide = prs.slides.add_slide(blank_layout)
|
||||||
|
aspect = pix.width / pix.height
|
||||||
|
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, left, top, width=w, height=h)
|
||||||
|
print(f" Slide: page {i}")
|
||||||
|
|
||||||
|
prs.save(OUT_PATH)
|
||||||
|
print(f"\nSaved: {OUT_PATH}")
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,54 @@
|
|||||||
|
import fitz
|
||||||
|
from pptx import Presentation
|
||||||
|
from pptx.util import Inches
|
||||||
|
import io
|
||||||
|
|
||||||
|
PDF_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\songs\search-ponder-and-pray\SearchPonderandPrayWithLyrics.pdf"
|
||||||
|
OUT_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\songs\search-ponder-and-pray\SearchPonderAndPray.pptx"
|
||||||
|
DPI = 96
|
||||||
|
|
||||||
|
SLIDE_W = Inches(13.333)
|
||||||
|
SLIDE_H = Inches(7.5)
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
page = doc[page_idx]
|
||||||
|
img_bytes, pw, ph = render_page(page)
|
||||||
|
slide = prs.slides.add_slide(blank_layout)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
print(f" Slide {len(prs.slides):2d} (pg {page_idx+1:2d})")
|
||||||
|
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
# Lyrics are embedded in the artwork, so every page is one slide
|
||||||
|
for page_idx in range(len(doc)):
|
||||||
|
add_slide(doc, prs, blank_layout, page_idx)
|
||||||
|
|
||||||
|
prs.save(OUT_PATH)
|
||||||
|
print(f"\nSaved: {OUT_PATH} ({len(doc)} slides)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user