Elixir library for generating PowerPoint (.pptx) files with editable charts.
Podium provides a pipe-friendly API for building presentations programmatically.
Create slides independently with Podium.Slide.new/1, populate them with
content using add_chart/4, add_image/3, add_text_box/3, etc., then
add them to a presentation with add_slide/2 and save.
Example
alias Podium.Chart.ChartData
data =
ChartData.new()
|> ChartData.add_categories(["Q1", "Q2", "Q3"])
|> ChartData.add_series("Revenue", [100, 200, 300])
slide =
Podium.Slide.new(:title_content)
|> Podium.add_chart(:column_clustered, data, x: {1, :inches}, y: {2, :inches}, width: {8, :inches}, height: {4, :inches})
|> Podium.add_text_box("Hello", x: {1, :inches}, y: {6, :inches}, width: {4, :inches}, height: {1, :inches})
Podium.new()
|> Podium.add_slide(slide)
|> Podium.save("output.pptx")Text Formatting Reference
Text content can be a plain string or rich text (a list of paragraphs).
Run options
Each run is a {text, opts} tuple. Available options:
:font_size- font size in points (e.g.12):bold-trueto bold:italic-trueto italicize:underline-truefor single underline, or one of:single,:double,:heavy,:dotted,:dotted_heavy,:dash,:dash_heavy,:dash_long,:dash_long_heavy,:dot_dash,:dot_dash_heavy,:dot_dot_dash,:dot_dot_dash_heavy,:wavy,:wavy_heavy,:wavy_double,:words:strikethrough-truefor single strikethrough:superscript-truefor superscript:subscript-truefor subscript:color- hex RGB string (e.g."FF0000"):font- font family name (e.g."Arial"):hyperlink- URL string,[url: "...", tooltip: "..."],{:slide, slide}for slide-to-slide links, or an action atom (:next_slide,:previous_slide,:first_slide,:last_slide,:end_show):lang- language code (default"en-US")
Paragraph options
When using {runs, opts} tuple paragraphs:
:alignment-:left,:center,:right, or:justify:line_spacing- line spacing multiplier (e.g.1.5):space_before- space before paragraph in points:space_after- space after paragraph in points:bullet-truefor bullet,:numberfor numbered, a character string for custom bullet, orfalseto suppress:level- indentation level (0-based)
Fill types
- Hex string -
"4472C4"for solid fill {:gradient, stops, opts}- gradient fill where stops are[{position, color}, ...]and opts include:angle(default5_400_000= 90 degrees in 60,000ths){:pattern, preset, opts}- pattern fill, seePodium.Patternfor presets; opts::foreground,:background(hex color strings){:picture, binary}- picture fill from image binary data
Line types
- Hex string -
"000000"for a line with default width - Keyword list -
[color: "000000", width: {2, :pt}, dash_style: :dash] - Dash styles:
:solid,:dash,:dot,:dash_dot,:long_dash,:long_dash_dot,:long_dash_dot_dot,:sys_dot,:sys_dash,:sys_dash_dot,:sys_dash_dot_dot
Summary
Functions
Adds an auto shape to a slide.
Adds a chart to a slide. Returns the updated slide.
Adds a combo chart (multiple chart types in one plot area) to a slide. Returns the updated slide.
Adds a connector between two points on a slide.
Adds a freeform shape (built with Podium.Freeform) to a slide.
Adds an image to a slide. Returns the updated slide.
Adds a video (movie) to a slide. Returns the updated slide.
Adds a text box with a picture (blip) fill to a slide. Returns the updated slide.
Adds a slide to the presentation. Returns the updated presentation.
Adds a table to a slide.
Adds a text box to a slide.
Creates a new presentation from your own template package.
Creates a new presentation from the bundled template.
Replaces a slide in the presentation with an updated version (matched by ref).
Saves the presentation to a file.
Saves the presentation to an in-memory binary.
Places a chart into a content placeholder. Returns the updated slide.
Sets core document properties (Dublin Core metadata).
Sets presentation-level footer, date, and slide number.
Sets speaker notes on a slide.
Sets a picture placeholder on a slide. Returns the updated slide.
Sets a placeholder's text content on a slide.
Places a table into a content placeholder. Returns the updated slide.
Types
@type alignment() :: :left | :center | :right | :justify
@type anchor() :: :top | :middle | :bottom
@type chart_type() ::
:column_clustered
| :column_stacked
| :column_stacked_100
| :bar_clustered
| :bar_stacked
| :bar_stacked_100
| :line
| :line_markers
| :line_stacked
| :line_markers_stacked
| :line_stacked_100
| :line_markers_stacked_100
| :pie
| :pie_exploded
| :area
| :area_stacked
| :area_stacked_100
| :doughnut
| :doughnut_exploded
| :radar
| :radar_filled
| :radar_markers
| :scatter
| :scatter_lines
| :scatter_lines_no_markers
| :scatter_smooth
| :scatter_smooth_no_markers
| :bubble
| :bubble_3d
@type connector_type() :: :straight | :elbow | :curved
@type emu() :: non_neg_integer()
@type hex_color() :: String.t()
@type layout() :: :title_slide | :title_content | :section_header | :two_content | :comparison | :title_only | :blank | :content_caption | :picture_caption | :title_vertical_text | :vertical_title_text | pos_integer()
Functions
@spec add_auto_shape(Podium.Slide.t(), atom(), keyword()) :: Podium.Slide.t()
Adds an auto shape to a slide.
Parameters
preset- shape type atom (seePodium.AutoShapeTypefor all 187 presets)
Options (required)
:x,:y,:width,:height- position and size
Options (optional)
:text- text content for the shape (string or rich text):fill- fill (omit to use theme default):line- line color or line opts:rotation- rotation in degrees:margin_left,:margin_right,:margin_top,:margin_bottom- text margins:anchor- vertical text anchor:auto_size- auto sizing behavior:word_wrap- word wrap setting:alignment- default text alignment:font_size- default font size in points
@spec add_chart( Podium.Slide.t(), chart_type(), Podium.Chart.ChartData.t() | Podium.Chart.XyChartData.t() | Podium.Chart.BubbleChartData.t(), keyword() ) :: Podium.Slide.t()
Adds a chart to a slide. Returns the updated slide.
Parameters
chart_type- chart type atom (seePodium.Chart.ChartTypefor all types)chart_data-Podium.Chart.ChartData,Podium.Chart.XyChartData, orPodium.Chart.BubbleChartDatastruct
Options (required)
:x,:y,:width,:height- position and size
Options (optional)
:title- chart title as string, or keyword list with:text,:font_size,:bold,:italic,:color,:font:legend- legend position atom (:left,:right,:top,:bottom) orfalseto hide, or keyword list with:position,:font_size,:bold,:italic,:color,:font:data_labels- list of atoms to show (:value,:category,:series,:percent) or keyword list with:show(list),:position(:center,:inside_end,:inside_base,:outside_end,:top,:bottom,:left,:right,:best_fit),:number_format(Excel format string),:color(hex color for label text):category_axis- keyword list with:title,:type(:datefor date axis),:number_format,:major_gridlines,:minor_gridlines,:visible,:reverse,:crosses,:label_rotation,:major_tick_mark,:minor_tick_mark,:base_time_unit,:major_time_unit,:minor_time_unit,:major_unit,:minor_unit,:color(hex color for tick label text),:line_color(hex color for axis line):value_axis- keyword list with:title,:number_format,:major_gridlines,:minor_gridlines,:min,:max,:major_unit,:minor_unit,:visible,:reverse,:crosses,:label_rotation,:major_tick_mark,:minor_tick_mark,:color(hex color for tick label text),:line_color(hex color for axis line),:major_gridlines_color,:minor_gridlines_color(hex colors for gridlines)
@spec add_combo_chart( Podium.Slide.t(), Podium.Chart.ChartData.t(), [{chart_type(), keyword()}], keyword() ) :: Podium.Slide.t()
Adds a combo chart (multiple chart types in one plot area) to a slide. Returns the updated slide.
Parameters
chart_data-%ChartData{}with shared categories and seriesplots- list of{chart_type, opts}tuples where opts are::series- list of zero-based series indices to include in this plot:secondary_axis-trueto plot on secondary value axis (defaultfalse)
Options (required)
:x,:y,:width,:height- position and size
Options (optional)
:title- chart title (string or keyword list):legend- legend position or keyword list:data_labels- data label configuration:category_axis- category axis options:value_axis- primary value axis options:secondary_value_axis- secondary value axis options (same keys as:value_axis)
@spec add_connector( Podium.Slide.t(), connector_type(), dimension(), dimension(), dimension(), dimension(), keyword() ) :: Podium.Slide.t()
Adds a connector between two points on a slide.
Parameters
connector_type-:straight,:elbow, or:curvedbegin_x,begin_y- start point coordinatesend_x,end_y- end point coordinates
Options
:line- line color string or keyword list with:color,:width,:dash_style
@spec add_freeform(Podium.Slide.t(), Podium.Freeform.t(), keyword()) :: Podium.Slide.t()
Adds a freeform shape (built with Podium.Freeform) to a slide.
Options
:origin_x,:origin_y- offset for the shape's bounding box origin:fill- fill color or fill tuple:line- line color or line opts:rotation- rotation in degrees
@spec add_image(Podium.Slide.t(), binary(), keyword()) :: Podium.Slide.t()
Adds an image to a slide. Returns the updated slide.
Image format is auto-detected from magic bytes. Supported formats: PNG, JPEG, BMP, GIF, TIFF, EMF, WMF.
Options (required)
:x- horizontal position:y- vertical position
Options (optional)
:width- image width (auto-calculated from native size if only:heightgiven or neither given):height- image height (auto-calculated from native size if only:widthgiven or neither given):crop- keyword list with:left,:top,:right,:bottom(values in 1/1000ths of a percent):rotation- rotation in degrees:shape- clip shape preset::ellipse,:diamond,:round_rect,:star5,:star6,:star8,:heart,:triangle,:hexagon,:octagon, or a preset geometry string (default"rect")
@spec add_movie(Podium.Slide.t(), binary(), keyword()) :: Podium.Slide.t()
Adds a video (movie) to a slide. Returns the updated slide.
All position/size options are required (no auto-scaling for video).
Options (required)
:x,:y,:width,:height- position and size
Options (optional)
:mime_type- MIME type string (default"video/unknown"):poster_frame- poster frame image binary (default: 1x1 transparent PNG)
@spec add_picture_fill_text_box( Podium.Slide.t(), rich_text(), binary(), keyword() ) :: Podium.Slide.t()
Adds a text box with a picture (blip) fill to a slide. Returns the updated slide.
Options (required)
:x,:y,:width,:height- position and size
Options (optional)
:fill_mode-:stretch(default) or:tile- All other text box options (
:rotation,:margin_*,:anchor, etc.)
@spec add_slide(Podium.Presentation.t(), Podium.Slide.t()) :: Podium.Presentation.t()
Adds a slide to the presentation. Returns the updated presentation.
Slides are created independently with Podium.Slide.new/1, populated
with content, then added to the presentation.
Example
slide = Podium.Slide.new(:title_content)
prs = Podium.add_slide(prs, slide)
@spec add_table(Podium.Slide.t(), [[term()]], keyword()) :: Podium.Slide.t()
Adds a table to a slide.
rows is a list of lists where each inner list is a row of cell values.
Cell values can be:
- Plain string -
"text" - Rich text list -
[{"bold", bold: true}] - Cell tuple with options -
{"text", col_span: 2, fill: "FF0000"} :mergeatom for cells covered by a span
Options (required)
:x,:y,:width,:height- position and size
Options (optional)
:col_widths- list of column widths (auto-calculated if omitted):row_heights- list of row heights (auto-calculated if omitted):table_style- keyword list of style booleans::first_row- highlight first row (defaulttrue):last_row- highlight last row (defaultfalse):first_col- highlight first column (defaultfalse):last_col- highlight last column (defaultfalse):band_row- alternating row bands (defaulttrue):band_col- alternating column bands (defaultfalse)
Cell options
:col_span- number of columns to span:row_span- number of rows to span:fill- cell fill color or fill tuple:borders-[left: color_or_opts, right: ..., top: ..., bottom: ...]:padding-[left: dim, right: dim, top: dim, bottom: dim]:anchor- vertical text alignment::top,:middle,:bottom
@spec add_text_box(Podium.Slide.t(), rich_text(), keyword()) :: Podium.Slide.t()
Adds a text box to a slide.
Options (required)
:x- horizontal position (EMU integer or{value, unit}):y- vertical position:width- box width:height- box height
Options (optional)
:fill- fill color or fill tuple (see module doc):line- line color or line opts (see module doc):rotation- rotation in degrees:margin_left,:margin_right,:margin_top,:margin_bottom- text margins:anchor- vertical text anchor::top,:middle, or:bottom:auto_size-:none,:text_to_fit_shape, or:shape_to_fit_text:word_wrap-true(default) orfalse:alignment- default text alignment::left,:center,:right,:justify:font_size- default font size in points
@spec from_template( binary(), keyword() ) :: Podium.Presentation.t()
Creates a new presentation from your own template package.
source is either a path to a .pptx/.potx file or the bytes of one. The
template keeps everything it declares: masters, layouts, themes, media, notes
master, default text style and notes size. Podium adds slides to it.
Template requirements
ppt/presentation.xml,ppt/_rels/presentation.xml.relsand[Content_Types].xmlmust be present, as they are in any package saved by PowerPoint, Keynote or Google Slides.- The template must contain no slides. Slide masters, layouts and a notes master are what a template is for; existing slides would collide with the ones Podium writes.
- The template must relate to at least one slide master, or its layouts have nothing for slides to sit on.
- To set core properties, the template's
_rels/.relsmust already relate todocProps/core.xml. A package saved by PowerPoint always does.
Differences from new/1
- Slide size comes from the template, so
:slide_widthand:slide_heightare ignored. Layout and master coordinates are never scaled. - Placeholders are addressed by the
{type, idx}pairPodium.Template.inspect/1reports, not by the bundled template's names.set_placeholder/3,set_picture_placeholder/3,set_chart_placeholder/6andset_table_placeholder/5all take a pair; the last two take the layout placeholder's box as the chart's or table's position and size. Passing a name raises.
Core-property options are the same as new/1.
Example
Podium.from_template("priv/brand/skeleton.pptx", title: "Q3 review")
|> Podium.add_slide(slide)
|> Podium.save("q3.pptx")
@spec new(keyword()) :: Podium.Presentation.t()
Creates a new presentation from the bundled template.
Use from_template/2 to build on a template of your own.
Options
:slide_width- slide width, default 16:9 (12_192_000 EMU). Accepts EMU integer or{value, unit}.:slide_height- slide height, default 16:9 (6_858_000 EMU). Accepts EMU integer or{value, unit}.:title- document title string:author- document author string:subject- document subject string:keywords- keywords string:category- category string:comments- comments/description string:last_modified_by- last modified by string:created-DateTimefor creation timestamp:modified-DateTimefor modification timestamp:last_printed-DateTimefor last printed timestamp:revision- revision number (integer):content_status- content status string:language- language code string:version- version string
@spec put_slide(Podium.Presentation.t(), Podium.Slide.t()) :: Podium.Presentation.t()
Replaces a slide in the presentation with an updated version (matched by ref).
@spec save(Podium.Presentation.t(), String.t()) :: :ok | {:error, term()}
Saves the presentation to a file.
@spec save_to_memory(Podium.Presentation.t()) :: {:ok, binary()} | {:error, term()}
Saves the presentation to an in-memory binary.
@spec set_chart_placeholder( Podium.Presentation.t(), Podium.Slide.t(), atom() | {String.t(), non_neg_integer()}, chart_type(), Podium.Chart.ChartData.t(), keyword() ) :: Podium.Slide.t()
Places a chart into a content placeholder. Returns the updated slide.
Addressed by a bundled-template name, the placeholder must be a content
placeholder (type: nil) — e.g. :content on :title_content, or
:left_content/:right_content on :two_content. Addressed by the
{type, idx} pair Podium.Template.inspect/1 reports, it works on any
template. Position and size are taken from the layout.
@spec set_core_properties( Podium.Presentation.t(), keyword() ) :: Podium.Presentation.t()
Sets core document properties (Dublin Core metadata).
Options
:title- document title:author- document author:subject- document subject:keywords- keywords:category- category:comments- comments/description:last_modified_by- last modified by:created- creation timestamp (DateTime):modified- modification timestamp (DateTime):last_printed- last printed timestamp (DateTime):revision- revision number (integer):content_status- content status string:language- language code string:version- version string
@spec set_notes(Podium.Slide.t(), String.t()) :: Podium.Slide.t()
Sets speaker notes on a slide.
@spec set_picture_placeholder( Podium.Slide.t(), atom() | {String.t(), non_neg_integer()}, binary() ) :: Podium.Slide.t()
Sets a picture placeholder on a slide. Returns the updated slide.
With a bundled-template name, this only works on layouts that have a picture
placeholder (e.g. :picture_caption). A {type, idx} pair from
Podium.Template.inspect/1 works on any template, including dropping a
picture into a content placeholder such as {"obj", 14}.
@spec set_placeholder( Podium.Slide.t(), atom() | {String.t(), non_neg_integer()}, rich_text() ) :: Podium.Slide.t()
Sets a placeholder's text content on a slide.
The slide must have been created with a layout that has the given placeholder.
Available placeholders by layout
| Layout | Placeholders |
|---|---|
:title_slide | :title, :subtitle |
:title_content | :title, :content |
:section_header | :title, :body |
:two_content | :title, :left_content, :right_content |
:comparison | :title, :left_heading, :left_content, :right_heading, :right_content |
:title_only | :title |
:blank | (none) |
:content_caption | :title, :content, :caption |
:picture_caption | :title, :caption (use set_picture_placeholder/3 for :picture) |
:title_vertical_text | :title, :body |
:vertical_title_text | :title, :body |
By type and idx
On a presentation built with from_template/2, address the placeholder by the
{type, idx} pair Podium.Template.inspect/1 reports for it:
Podium.set_placeholder(slide, {"title", 0}, "Category performance")
Podium.set_placeholder(slide, {"obj", 14}, "Volume grew 4.2%")The type is always the layout placeholder's own, so a content placeholder is
{"obj", idx} even when it holds a picture or a chart.
@spec set_table_placeholder( Podium.Presentation.t(), Podium.Slide.t(), atom() | {String.t(), non_neg_integer()}, [[term()]], keyword() ) :: Podium.Slide.t()
Places a table into a content placeholder. Returns the updated slide.
Addressed by a bundled-template name, the placeholder must be a content
placeholder (type: nil). Addressed by the {type, idx} pair
Podium.Template.inspect/1 reports, it works on any template. Position and
size are taken from the layout.