PHPackages                             vielhuber/ppthelper - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. vielhuber/ppthelper

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

vielhuber/ppthelper
===================

MCP server that turns Markdown into editable PowerPoint decks via Pandoc with a dynamically themed reference.pptx.

1.2.3(2w ago)191↓50%MITPHPPHP ^8.3

Since May 21Pushed 2w agoCompare

[ Source](https://github.com/vielhuber/ppthelper)[ Packagist](https://packagist.org/packages/vielhuber/ppthelper)[ RSS](/packages/vielhuber-ppthelper/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (25)Used By (0)

[![build status](https://github.com/vielhuber/ppthelper/actions/workflows/ci.yml/badge.svg)](https://github.com/vielhuber/ppthelper/actions)[![GitHub Tag](https://camo.githubusercontent.com/edf69849f66aa93724d1ac1cbb569dc097e1beecd5f7933da7b288094fd1bc4d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f7461672f7669656c68756265722f70707468656c706572)](https://github.com/vielhuber/ppthelper/tags)[![Code Style](https://camo.githubusercontent.com/1540f8ce219727155ab62506c77b818b720421c22c4cf0b18a5f160942132e2d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64655f7374796c652d7073722d2d31322d6666363962342e737667)](https://www.php-fig.org/psr/psr-12/)[![License](https://camo.githubusercontent.com/c3eadaa259ba691f9c3cf01985b5bd2277c1d02810df0f3d92e8be1a4e66cdb0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f7669656c68756265722f70707468656c706572)](https://github.com/vielhuber/ppthelper/blob/main/LICENSE.md)[![Last Commit](https://camo.githubusercontent.com/49c11e9991cc4f9f76f032c02dc9baacd65b9322df8b13f1cdf5ee9b912368cb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f7669656c68756265722f70707468656c706572)](https://github.com/vielhuber/ppthelper/commits)[![PHP Version Support](https://camo.githubusercontent.com/259e57006a02481d4583d75c92efb8dbe3ed9254941ee58878d36c926e94c6b4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f7669656c68756265722f70707468656c706572)](https://packagist.org/packages/vielhuber/ppthelper)[![Packagist Downloads](https://camo.githubusercontent.com/0e709271e62d57b4a99cb5ecc41552829e3b592f6663d55d019234cb5306948d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7669656c68756265722f70707468656c706572)](https://packagist.org/packages/vielhuber/ppthelper)

📢 ppthelper 📢
=============

[](#-ppthelper-)

ppthelper is a helper for powerpoint.

with its help you can render pptx decks from markdown in php in a very simple, webdev-friendly way. every render dynamically themes the reference deck (colors, fonts) so the output looks distinct per call while every text element stays fully editable in powerpoint.

installation
------------

[](#installation)

install once with [composer](https://getcomposer.org/):

```
composer require vielhuber/ppthelper

```

then add this to your files:

```
require __DIR__ . '/vendor/autoload.php';
use vielhuber\ppthelper\ppthelper;
```

[pandoc](https://pandoc.org/) must be installed and reachable on `$PATH` (or pass an explicit `pandoc_path`).

usage
-----

[](#usage)

### rendering

[](#rendering)

```
$path = ppthelper::render([
    'output' => 'deck.pptx',
    'content_markdown' => "% Quarterly Update\n% acme corp\n\n# Highlights\n\n- Revenue up 23%\n- 4 new markets",
    'content_file' => null,
    'style_template' => null,             // null | 'default' | 'ion' | 'facet' | …
    'colors_primary' => '#1F4E79',
    'colors_secondary' => '#F59E0B',
    'colors_background' => '#FFFFFF',
    'colors_text' => '#111827',
    'fonts_heading' => 'Aptos Display',
    'fonts_text' => 'Aptos',
    'transitions' => false, // false|'fade'|'slide'
    'animations' => false, // false|true
    'pandoc_path' => 'pandoc' // optional
]);
```

read markdown from a file instead of inline:

```
$path = ppthelper::render([
    'output' => 'deck.pptx',
    'content_file' => 'slides.md'
]);
```

### styles

[](#styles)

`style_template` selects the deck's look:

- `null` or `'default'` → bundled `assets/default.pptx` (Office)
- a **slug** like `'ion'`, `'facet'`, `'circuit'` → the bundled `assets/.pptx`
- an **absolute path** to a `.pptx` → your own custom reference deck

Bundled slugs (all derived from the standard PowerPoint theme set, full slideLayouts retained):

sluglooksluglook`default`Office (clean, neutral)`mesh`grey grid texture`badge`bold yellow accent`metropolitan`teal corporate`banded`bright bands`office-classic`Office 2013/2022`basis`green/yellow grid`office-classic-2`(variant)`berlin`warm orange`organic`earthy green/brown`circuit`tech green`parcel`warm orange tan`damask`green damask pattern`quotable`turquoise minimal`dividend`deep wine`retrospect`orange retro`droplet`blue water-drop`segment`navy segmented`facet`bright lime green`slate`dark slate`frame`grey framed`slice`red sliced`frame-2`(variant)`vapor-trail`red contrail`frame-3`(variant)`wisp`grey vapor`gallery`wine red museum`wood-type`rustic woodcut`headlines`bold red headline`ion`red modern`integral`cyan minimal`ion-boardroom`magenta boardroom### image paths

[](#image-paths)

`![alt](path)` references in the markdown resolve as follows:

- **absolute paths** (`/abs/path/to/img.png`) work out of the box.
- **relative paths** (`logo.png`, `images/hero.jpg`) are resolved against the **caller's current working directory** at the moment `render()` is called. either `chdir()` into the right place before calling, or pass absolute paths.

### markdown syntax

[](#markdown-syntax)

ppthelper accepts any [pandoc-flavored markdown](https://pandoc.org/MANUAL.html#slide-shows):

```
% Deck title
% author
% 2026-05-21

# first slide

- bullet one
- bullet two

# two-column layout

:::: {.columns}
::: {.column}
**left**

- item a
  :::
  ::: {.column}
  **right**

- item b
  :::
  ::::

# table

| points | grade |
| ------ | ----- |
| 37-40  | 1     |
| 33-36  | 2     |

# image

![hero](hero.png)

::: notes
speaker notes for this slide
:::
```

- title slide: the leading `% Title` / `% Author` / `% Date` lines (all optional).
- each `# Heading` starts a new slide.
- bullets, ordered lists, tables, fenced code, math (`$E=mc^2$`), images: all standard pandoc-markdown.
- two-column layouts via `::: {.columns}` / `::: {.column}` fences. **note:** pandoc's pptx writer caps at two columns — additional `::: {.column}` blocks are silently dropped. for 3+ "columns" use a markdown table (`| A | B | C |`) instead, which pandoc renders as a native side-by-side pptx table.
- speaker notes via `::: notes` / `:::` blocks.

### slide-layout cookbook

[](#slide-layout-cookbook)

pandoc's pptx writer only ever picks ~3 layouts out of the typical 17 a skeleton ships with (title, title+content, two-content). ppthelper widens this with a post-process pass that remaps "section-header"-shaped slides onto the skeleton's `secHead` layout. the patterns below cover every layout actually reachable from markdown.

#### title slide

[](#title-slide)

three leading `%` lines. all optional; pass only what you need.

```
% Q3 Strategy Review
% Acme GmbH · Board Meeting
% 22.05.2026
```

date format follows the locale of your skeleton's ``. for narrow rotated date boxes use the short `DD.MM.YYYY` form — ppthelper rewrites pandoc's static text into a live field so powerpoint formats it correctly.

#### content slide (title + bullets)

[](#content-slide-title--bullets)

the default for any `# heading` followed by body text.

```
# Why this matters now

- AI is moving from experiment to operating layer
- Value emerges from process redesign, not from tools alone
- Governance, data, and skills decide who scales
- Window of differentiation is closing within 12–18 months
```

keep it to 3–5 bullets, ~80 chars each. pandoc rendering doesn't reflow, and `` (ppthelper adds it automatically) only shrinks down to ~70% before things turn unreadable.

#### quote slide

[](#quote-slide)

a `# heading` followed by a body that consists entirely of markdown blockquote (`> …`) lines. ppthelper detects this (via the marL="1270000" + no-bullet marker pandoc emits) and remaps the slide onto the skeleton's quote/zitat layout (larger italic body, accent caption).

```
# Worth quoting

> Künstliche Intelligenz wird nicht jeden Job zerstören.
> Aber jeder Job wird sich verändern.
>
> — Andrew Ng, Stanford 2024
```

best used sparingly — 1 quote per ~15–20 content slides reads as a rhetorical anchor; more often than that feels gimmicky.

#### section-header slide

[](#section-header-slide)

write a `# heading` with **no body** between it and the next `# heading`. ppthelper detects the empty body and remaps the slide onto the skeleton's section-header layout (larger title, accent background).

```
# Part 1 — Market dynamics

# Market trends 2026
- Adoption breadth keeps rising, skaling lags
- Investment hits record highs

# Part 2 — Value levers

# Operational efficiency
- ...
```

excellent for breaking up decks of 15+ slides — drop one in every 5–7 slides.

#### two-column: image + bullets

[](#two-column-image--bullets)

the most useful "mixed media" layout. never put `![]()` and bullets in the same plain block — pandoc stacks them awkwardly. use the column fence:

```
# Reference architecture

:::: {.columns}
::: {.column}
![architecture](architecture.png)
:::
::: {.column}
- Ingestion: streaming + batch
- Model layer: shared inference, per-team adapters
- Governance: policy-as-code at the gateway
- Observability: prompt + cost + drift
:::
::::
```

ppthelper remaps the image's `` geometry to fit the column box. for a single full-width hero image use the next pattern instead.

#### image-only slide

[](#image-only-slide)

bare `![alt](path)` without bullets:

```
# Architecture overview

![architecture](architecture.png)
```

pandoc still picks the `slideLayout2` (title+content) layout under the hood — the image just occupies the content placeholder. absolute paths work everywhere; relative paths resolve against the caller's `cwd` at render time.

#### comparison table (3+ columns)

[](#comparison-table-3-columns)

pandoc's two-column fence stops at two `::: {.column}` blocks. for side-by-side 3+ use a markdown table — pandoc emits a native pptx table that ppthelper auto-grows vertically so it doesn't clip:

```
# Build vs. buy vs. hybrid

| Approach | Strength                  | Trade-off          |
| -------- | ------------------------- | ------------------ |
| Build    | Differentiation, IP       | Talent + runway    |
| Buy      | Time-to-value, support    | Lock-in            |
| Hybrid   | Best-of-both, optionality | Integration cost   |
```

5+ rows are fine; ppthelper bumps the table-frame `cy` so auto-sized rows fit. if your table approaches ~8 rows split it across two slides (`# Roadmap (1/2)` / `# Roadmap (2/2)`).

#### speaker notes

[](#speaker-notes)

attach context that should not appear on the slide itself:

```
# Strategic options

- Option A: defensive — modernize existing stack
- Option B: offensive — agentic workflows in core ops
- Option C: hybrid — agentic in 2 functions, modernize the rest

::: notes
Board is leaning toward C based on last week's risk workshop.
McKinsey '25 puts agentic-first orgs at 2x revenue growth vs peers.
:::
```

notes show up only in presenter view / printed handouts.

#### transitions &amp; animations

[](#transitions--animations)

pass at render-time, not in the markdown:

```
ppthelper::render([
    'content_markdown' => $md,
    'output' => 'deck.pptx',
    'transitions' => 'fade',   // false | 'fade' | 'slide'
    'animations'  => true,     // body bullets appear one click at a time
]);
```

a fade transition with no bullet-animations is the sane default for business decks.

#### putting it all together

[](#putting-it-all-together)

a small end-to-end deck mixing every pattern above:

```
% Quarterly Strategy
% Acme GmbH
% 22.05.2026

# Part 1 — Where we stand

# Adoption is uneven
- 78% of teams use AI weekly
- Only 12% have a measured ROI loop
- Governance still owned by IT, not by product

# Reference architecture

:::: {.columns}
::: {.column}
![architecture](architecture.png)
:::
::: {.column}
- Shared inference layer
- Per-team adapters
- Policy-as-code gateway
:::
::::

# Part 2 — Where we go

# Three options on the table

| Approach | Strength               | Trade-off       |
| -------- | ---------------------- | --------------- |
| Build    | Differentiation, IP    | Talent + runway |
| Buy      | Time-to-value          | Lock-in         |
| Hybrid   | Best-of-both           | Integration     |

::: notes
Board is leaning toward Hybrid. McKinsey '25 supports this.
:::

# Recommendation

![roadmap](roadmap.png)
```

this single markdown blob renders into a 7-slide deck (title · section · content · two-content · section · content · image-as-content) that hits all four pandoc-reachable skeleton layouts: `slideLayout1` (title), `slideLayout2` (title+content — also used for bare image slides), `slideLayout3` (section header — picked up via ppthelper's empty-body remap), and `slideLayout4` (two content).

### custom template

[](#custom-template)

bring your own corporate `reference.pptx` (slide masters, default layouts, logo, fonts) and ppthelper themes a copy of it on every render — just pass an absolute path as `style_template`:

```
$path = ppthelper::render([
    'output' => 'deck.pptx',
    'content_markdown' => $md,
    'style_template' => __DIR__ . '/templates/corporate.pptx',
    'colors_primary' => '#003366'
]);
```

### mcp server

[](#mcp-server)

ppthelper ships as a standalone [mcp](https://modelcontextprotocol.io/) server for ai-agent workflows, exposing a single tool `render_deck` that wraps `ppthelper::render`.

```
cp vendor/vielhuber/ppthelper/src/.env.example vendor/vielhuber/ppthelper/src/.env
# edit the .env and set MCP_TOKEN to a private value
vendor/bin/mcp-server.php

```

the server speaks both stdio (CLI invocation) and HTTP via [simplemcp](https://github.com/vielhuber/simplemcp). `auth: 'static'` mode expects the bearer token in `MCP_TOKEN`.

the tool exposes `render_deck(markdown, style_template?, transitions?, animations?, output?)`. `style_template` accepts any of the bundled slugs (`'ion'`, `'facet'`, `'circuit'`, …; see "styles" above) — let the LLM pick a look that matches the deck topic. color/font theme overrides are intentionally **not** part of the mcp surface — llms otherwise reflexively replace the curated theme with a generic "modern" palette. for hard-forced theming use `ppthelper::render(...)` directly from php or the cli.

`output` accepts any absolute or relative path; relative paths resolve against the working directory the server was launched from. omit it to get a tempfile back.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance97

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity58

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

24

Last Release

15d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3183737?v=4)[David Vielhuber](/maintainers/vielhuber)[@vielhuber](https://github.com/vielhuber)

---

Top Contributors

[![vielhuber](https://avatars.githubusercontent.com/u/3183737?v=4)](https://github.com/vielhuber "vielhuber (24 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/vielhuber-ppthelper/health.svg)

```
[![Health](https://phpackages.com/badges/vielhuber-ppthelper/health.svg)](https://phpackages.com/packages/vielhuber-ppthelper)
```

###  Alternatives

[social-links/social-links

PHP library to generate share buttons

112348.6k2](/packages/social-links-social-links)[astrotomic/php-conditional-proxy

This package provides a trait and class to allow calling methods based on a condition without breaking the method chain.

31166.8k7](/packages/astrotomic-php-conditional-proxy)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
