sitemap.xml llms.txt
Skip to main content

PowerPoint

Epicenter can generate slide decks on the server by merging simulation data into a prebuilt PowerPoint template.

Generating slide decks

Generation is a server-side operation.

How it works

When your application calls the PowerPoint adapter, Epicenter:

  1. Locates the template file in your project's file system.
  2. Opens the template and iterates over every slide.
  3. Replaces named shapes (text placeholders, charts, tables, and pictures) with the data you supply in the request body.
  4. Returns the finished slide deck, either as Base64-encoded binary data or as a streaming download.
Learn more

To generate PowerPoint files from your application code, use the PowerPoint adapter.

Use-case example

A use-case example is to produce debrief slide decks at the end of a simulation round, populated with each team's run variables, leaderboard results, charts, and images.

Template files

The template file is a standard .pptx file that you upload to your project's template directory in advance. The file contains named shapes that get replaced with data from you application. Epicenter creates the new slide-deck in memory using the Aspose Slides library and never modifies the original template.

Preparing a template

Before uploading the template file, give each replaceable shape a unique name using PowerPoint's Selection Pane.

Important

Epicenter identifies shapes by name when merging data, so naming is required for every shape you want to populate.

The four replaceable shape types are:

  • Text frames: Any text box or auto-shape containing {{fieldName}} tokens. Epicenter replaces each token with the corresponding value from the parameters map.
  • Chart shapes: Epicenter replaces the chart's series data and category labels in place, preserving the chart type and visual formatting from the template.
  • Table shapes: Epicenter populates the table row by row. The template must contain at least one data-template row (in addition to an optional header row), which Epicenter clones for each data row you supply.
  • Picture frames: Epicenter replaces the image content of a picture frame shape with the binary image data you provide.

Template directory

Templates are stored in the Epicenter project file system. Epicenter looks for templates in one of two directories, specified by the templateDirectory parameter you pass to the adapter functions:

  • 'DATA': The project's Data folder. This is the preferred location for new templates.
  • 'MODEL': The project's Model folder. This option is retained for backwards compatibility.

Upload the template file to the appropriate folder in your project before calling the adapter.

The document model

The adapter functions take a DocumentShadow object in the document parameter. The object has three parts:

  • output: The filename for the generated file (for example, 'debrief-slides.pptx'). You can make this dynamic, such as appending a date: 'results-2024-11-01.pptx'.
  • environment: A document-wide EnvironmentShadow object whose substitutions apply to all slides unless overridden by a slide-level environment. This field is optional if all your data is slide-specific.
  • slides: An array of SlideShadow objects, each targeting a specific slide by its 1-based slide number and carrying its own environment.

Scope resolution

For every named shape, Epicenter checks the slide-level environment first. If a matching shadow is found there, it is used. If not, Epicenter falls back to the document-level environment. This means you can set defaults once at the document level and override only the shapes that differ per slide.

Substitution types

Text parameters

Place {{fieldName}} tokens in text boxes in your template. Epicenter replaces each token with the corresponding value from the parameters map. Parameters can live at the document level (applied to all slides) or at the slide level (applied only to that slide). When both levels define the same field, the slide-level value wins.

Important

Each {{fieldName}} token must be the entire text content of its text box. A text box that mixes a token with surrounding text (for example, Hello {{name}}!) will not substitute correctly. Compose any surrounding text in your application code and pass the full string as a single parameter.

A SlideShadow object example
{
number: 1,
environment: {
parameters: {
title: 'Bike Shop Challenge',
subtitle: 'Facilitator Results Debrief',
// Composed in code so the full line is one token:
summary: `${groupName}${episodeLabel}${participantCount} players`,
generated: `Generated ${new Date().toLocaleString()}`,
},
},
}
Note

Token substitution applies to the text portion of the shape, preserving the font, size, and color settings of the original text run in the template.

Charts

To populate a chart, provide a ChartShadow object with the chart's shape name, an array of SeriesShadow objects, and an optional array of category labels. Epicenter removes the chart's existing series and replaces them with the ones you supply. The chart type (bar, line, pie, scatter, etc.) and visual formatting properties from the template (such as gap width, overlap, and color variation) are preserved.

The objectType field on each series must match the chart's actual type in the template. Supported types are 'bar', 'area', 'line', 'pie', 'scatter', 'y', and 'xy'.

A SlideShadow object with a ChartShadow
// Slide 4: bar chart showing total profit per participant
{
number: 4,
environment: {
charts: [
{
name: 'ProfitByParticipant',
categories: standings.map((row) => row.name),
series: [
{
objectType: 'bar',
name: 'Total Profit',
data: standings.map((row) => ({ n: row.totalProfit })),
},
],
},
],
},
}

Multiple charts on a single slide are supported. Each chart is matched by its shape name and replaced independently:

A SlideShadow with multiple ChartShadow objects
// Slide 5: a line chart and a pie chart on the same slide
{
number: 5,
environment: {
charts: [
{
name: 'ProfitOverTime',
categories: years,
series: standings.map((row) => ({
objectType: 'line',
name: row.name,
data: row.profitByYear.map((val) => ({ n: val })),
})),
},
{
name: 'RevenueShare',
categories: standings.map((row) => row.name),
series: [
{
objectType: 'pie',
name: 'Total Revenue',
data: standings.map((row) => ({ n: row.totalRevenue })),
},
],
},
],
},
}

Chart title text frames also undergo {{fieldName}} token substitution, subject to the same one-token-per-text-box constraint described above.

Tables

To populate a table, provide a TableShadow object with the table's shape name, an optional header array, and a data array of rows. Each row is itself an array of cell values.

Important

The table must have at least a header row and one template data row. A table with only one row will throw a configuration error.

Epicenter clones the template data row once for each row in your data array, applying cell values in column order. Null values are written as empty strings. Booleans, numbers, and strings are coerced to text.

A SlideShadow object with a TableShadow
// Slide 3: leaderboard table
{
number: 3,
environment: {
parameters: {
topPerformer: topPerformer?.name ?? '',
topProfit: topPerformer ? usd(topPerformer.totalProfit) : '',
averageProfit: usd(averageProfit),
},
tables: [
{
name: 'FinalStandings',
header: ['Rank', 'Player', 'Rounds', 'Avg. Price', 'Total Revenue', 'Total Profit'],
data: standings.map((row) => [
row.rank,
row.name,
row.yearsPlayed,
usd(row.avgPrice),
usd(row.totalRevenue),
usd(row.totalProfit),
]),
},
],
},
}

Table overflow

If a table grows too tall to fit on its slide, Epicenter automatically clones the slide and continues populating the table on the new slide. The clone inherits all shapes from the original slide, so headers and surrounding content are preserved. You do not need to plan for a maximum number of rows in your template.

Pictures

To replace an image in a picture frame, provide a PictureShadow object with the frame's shape name and a BinaryData object containing the encoded image. The replacement preserves the frame's position and dimensions from the template. Only the image content changes.

Important

The image must be provided as either Base64 ('BASE_64') or hexadecimal ('HEX') encoded data.

A PictureShadow object example
pictures: [
{
name: 'TeamLogoFrame',
data: {
encoding: 'BASE_64',
data: '<base64-encoded-image-bytes>',
content_type: 'image/png',
},
},
],

Delivery modes

The PowerPoint adapter exposes two functions with different return types, suited to different use cases:

  • The generate() function sends a PUT request and returns the finished file as a BinaryData object encoded in Base64. Use this when you need to store the result, re-encode it, attach it to an email, or pass it to another service.
  • The stream() function sends a POST request and returns the raw HTTP Response. Call .blob() on the response to obtain the file as a Blob and trigger a browser download. Use this when you want to hand the file directly to the browser.
Triggering a browser download
import { powerpointAdapter } from 'epicenter-libs';

const response = await powerpointAdapter.stream('DATA', 'debrief-template.pptx', document);

if (!response.ok) {
throw new Error(`PowerPoint generation failed (${response.status})`);
}

const blob = await response.blob();
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = document.output ?? 'results.pptx';
link.click();
URL.revokeObjectURL(url);
Important

Both generate() and stream() require a FACILITATOR-level role or higher, enforced on the server-side. Participant-facing pages that need to trigger a download should do so through a privileged function call, or the file should be generated by the facilitator and distributed as an asset.