Presentations

Create a presentation

prs = Podium.new()

Create from your own template

prs = Podium.from_template("brand/skeleton.pptx")

Custom slide dimensions

prs = Podium.new(
  slide_width: {10, :inches},
  slide_height: {7.5, :inches}
)

Add a slide

slide = Podium.Slide.new()
prs = Podium.add_slide(prs, slide)

Add a slide with layout

slide = Podium.Slide.new(:title_slide)
prs = Podium.add_slide(prs, slide)

Save to file

:ok = Podium.save(prs, "output.pptx")

Save to memory

{:ok, binary} = Podium.save_to_memory(prs)

Replace slide in presentation

prs = Podium.put_slide(prs, slide)

Core properties

prs = Podium.new(title: "Report", author: "Team")

Text

Plain text box

slide = Podium.add_text_box(slide, "Hello",
  x: {1, :inches}, y: {1, :inches},
  width: {8, :inches}, height: {1, :inches},
  font_size: 24)

Rich text with formatting

slide = Podium.add_text_box(slide, [
  {[{"Title", bold: true, font_size: 32, color: "003366"}],
   alignment: :center}
], x: {1, :inches}, y: {1, :inches},
   width: {8, :inches}, height: {1, :inches})

Bullet list

slide = Podium.add_text_box(slide, [
  {["First item"], bullet: true},
  {["Sub-item"], bullet: true, level: 1},
  {["Second item"], bullet: true}
], x: {1, :inches}, y: {2, :inches},
   width: {8, :inches}, height: {3, :inches})

Run formatting options

{"text", bold: true, italic: true,
 underline: :single, color: "FF0000",
 font_size: 18, font: "Arial"}

Shapes

Auto shape

slide = Podium.add_auto_shape(slide, :rectangle,
  x: {1, :inches}, y: {1, :inches},
  width: {3, :inches}, height: {2, :inches},
  fill: "4472C4")

Shape with text

slide = Podium.add_auto_shape(slide, :rounded_rectangle,
  x: {1, :inches}, y: {1, :inches},
  width: {3, :inches}, height: {1, :inches},
  fill: "003366",
  text: [[{"Click me", bold: true, color: "FFFFFF"}]])

Gradient fill

fill: {:gradient, [{0, "001133"}, {100_000, "004488"}],
  angle: 5_400_000}

Pattern fill

fill: {:pattern, :lt_horz,
  foreground: "003366", background: "FFFFFF"}

Connector

slide = Podium.add_connector(slide, :straight,
  {1, :inches}, {1, :inches},
  {5, :inches}, {3, :inches},
  line: [color: "000000", width: {1.5, :pt}])

Line dash styles

line: [color: "000000", width: {1, :pt},
  dash_style: :dash]
# :solid, :dash, :dot, :dash_dot, :long_dash,
# :long_dash_dot, :long_dash_dot_dot

Charts

Create chart data

alias Podium.Chart.ChartData

data =
  ChartData.new()
  |> ChartData.add_categories(["Q1", "Q2", "Q3"])
  |> ChartData.add_series("Revenue", [100, 200, 300],
    color: "4472C4")

Add chart to slide

slide =
  Podium.add_chart(slide, :column_clustered, data,
    x: {0.5, :inches}, y: {1, :inches},
    width: {9, :inches}, height: {5, :inches})

Chart with title and legend

slide =
  Podium.add_chart(slide, :pie, data,
    x: {2, :inches}, y: {1, :inches},
    width: {9, :inches}, height: {5, :inches},
    title: "Sales", legend: :bottom,
    data_labels: [:category, :percent])

Scatter chart

alias Podium.Chart.XyChartData

data = XyChartData.new()
  |> XyChartData.add_series("Series 1",
    [1, 2, 3, 4], [10, 20, 15, 25],
    color: "4472C4")

Bubble chart

alias Podium.Chart.BubbleChartData

data = BubbleChartData.new()
  |> BubbleChartData.add_series("Region A",
    [1, 3, 5], [10, 25, 15], [5, 12, 8],
    color: "4472C4")

Combo chart

slide =
  Podium.add_combo_chart(slide, data, [
    {:column_clustered, series: [0]},
    {:line_markers, series: [1], secondary_axis: true}
  ], x: {0.5, :inches}, y: {1, :inches},
     width: {12, :inches}, height: {5, :inches})

Tables

Basic table

slide = Podium.add_table(slide, [
  ["Name", "Role"],
  ["Alice", "Engineer"],
  ["Bob", "Designer"]
], x: {1, :inches}, y: {1, :inches},
   width: {8, :inches}, height: {3, :inches})

Table with styling

slide = Podium.add_table(slide, rows,
  x: {1, :inches}, y: {1, :inches},
  width: {8, :inches}, height: {3, :inches},
  table_style: [first_row: true, band_row: true])

Cell formatting

{"Header", fill: "003366",
  borders: [bottom: [color: "000000", width: {2, :pt}]],
  anchor: :middle}

Merged cells

[{"Merged Header", col_span: 3}, :merge, :merge]

Images

Add image

binary = File.read!("photo.png")
slide =
  Podium.add_image(slide, binary,
    x: {2, :inches}, y: {1, :inches},
    width: {6, :inches}, height: {4, :inches})

Image with width only (auto height)

slide =
  Podium.add_image(slide, binary,
    x: {2, :inches}, y: {1, :inches},
    width: {8, :inches})

Image with shape mask

slide =
  Podium.add_image(slide, binary,
    x: {1, :inches}, y: {1, :inches},
    width: {3, :inches}, height: {3, :inches},
    shape: :ellipse)

Cropped image

slide =
  Podium.add_image(slide, binary,
    x: {1, :inches}, y: {1, :inches},
    width: {6, :inches}, height: {4, :inches},
    crop: [top: 5000, bottom: 5000])

Placeholders

Set text placeholder

slide = Podium.set_placeholder(slide, :title,
  "Annual Report")

Picture placeholder

slide =
  Podium.set_picture_placeholder(slide, :picture, image_binary)

Chart in content placeholder

slide =
  Podium.set_chart_placeholder(prs, slide, :content, :pie, chart_data,
    title: "Revenue", legend: :bottom)

Table in content placeholder

slide =
  Podium.set_table_placeholder(prs, slide, :left_content, rows,
    table_style: [first_row: true])

Address a template's own placeholders

template = Podium.Template.inspect("brand/skeleton.pptx")
template.layouts[2].placeholders

slide = Podium.set_placeholder(slide, {"title", 0}, "Title")
slide = Podium.set_chart_placeholder(prs, slide,
  {"obj", 14}, :column_clustered, chart_data)
prs = Podium.set_footer(prs,
  footer: "Confidential",
  date: "Feb 2026",
  slide_number: true)

Speaker notes

slide = Podium.set_notes(slide,
  "Talking points for this slide.")

Units

Inches

{1, :inches}     # 914,400 EMU
{0.5, :inches}   # 457,200 EMU

Centimeters

{2.54, :cm}      # 914,400 EMU
{1, :cm}         # 360,000 EMU

Points

{72, :pt}        # 914,400 EMU
{1, :pt}         # 12,700 EMU

Raw EMU

914_400           # 1 inch in EMU