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, MSO_CONNECTOR from pptx.enum.dml import MSO_THEME_COLOR from pptx.oxml import parse_xml from pptx.oxml.ns import nsdecls import os, math prs = Presentation() prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) # Palette NAVY = RGBColor(13, 39, 76) BLUE = RGBColor(25, 99, 190) CYAN = RGBColor(74, 205, 220) EMERALD = RGBColor(0, 153, 116) MINT = RGBColor(220, 248, 239) PALE = RGBColor(242, 248, 252) WHITE = RGBColor(255,255,255) DARK = RGBColor(35, 54, 74) GREY = RGBColor(94, 112, 130) RED = RGBColor(218, 65, 73) GOLD = RGBColor(255, 190, 70) LIME = RGBColor(161, 220, 110) PURPLE = RGBColor(126, 101, 214) def set_bg(slide, color=PALE): fill = slide.background.fill fill.solid(); fill.fore_color.rgb = color def shape(slide, typ, x,y,w,h, fill, line=None, radius=False): s = slide.shapes.add_shape(typ, Inches(x), Inches(y), Inches(w), Inches(h)) s.fill.solid(); s.fill.fore_color.rgb = fill s.line.fill.background() if line is None else None if line: s.line.color.rgb = line return s def textbox(slide, text, x,y,w,h, size=18, color=DARK, bold=False, font="Aptos", align=PP_ALIGN.LEFT, valign=MSO_ANCHOR.TOP, margin=0.06, italic=False): tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h)) tf = tb.text_frame; tf.clear(); tf.word_wrap = True tf.margin_left = Inches(margin); tf.margin_right = Inches(margin) tf.margin_top = Inches(margin); tf.margin_bottom = Inches(margin) tf.vertical_anchor = valign p = tf.paragraphs[0]; p.alignment = align r = p.add_run(); r.text = text r.font.name = font; r.font.size = Pt(size); r.font.bold = bold r.font.italic = italic; r.font.color.rgb = color return tb def rich_lines(slide, lines, x,y,w,h, size=16, color=DARK, gap=7): tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h)) tf=tb.text_frame; tf.clear(); tf.word_wrap=True tf.margin_left=Inches(.08); tf.margin_right=Inches(.08) tf.margin_top=Inches(.06); tf.margin_bottom=Inches(.04) for i,item in enumerate(lines): p=tf.paragraphs[0] if i==0 else tf.add_paragraph() p.text=item p.font.name="Aptos"; p.font.size=Pt(size); p.font.color.rgb=color p.space_after=Pt(gap) p.level=0 return tb def header(slide, title, kicker=None, n=None): # blue top rail and 3D-ish accent shape(slide, MSO_SHAPE.RECTANGLE, 0,0,13.333,.16, BLUE) shape(slide, MSO_SHAPE.OVAL, 12.35,.32,.38,.38, CYAN) shape(slide, MSO_SHAPE.OVAL, 12.63,.48,.20,.20, EMERALD) if kicker: textbox(slide, kicker.upper(), .55,.32,10,.25,10,EMERALD,True) textbox(slide, title, .55,.65,11.7,.62,26,NAVY,True) if n: textbox(slide, f"{n:02d}", 12.25,6.98,.5,.25,10,GREY,True,align=PP_ALIGN.RIGHT) def card