This release is a pre-release and may not be stable for production use.
gopptx
A PowerPoint (.pptx) engine written in Go, with a first-class Python API and a stable JSON command bridge.
gopptx creates, reads, edits and exports OOXML presentations. The engine is pure Go — no Microsoft Office, no LibreOffice and no headless browser is required to produce a valid deck. The Python package is a thin, typed layer over the same engine through a C shared library, so Python code runs at Go speed without reimplementing any logic.
from gopptx import Presentation
with Presentation.new("Quarterly Update") as pres:
pres.add_bullet_slide("Highlights", ["Revenue +12%", "Retention +4%"])
pres.save("deck.pptx")
Table of contents
- Why gopptx
- What it can do
- Installation
- Quickstart — Python
- Quickstart — Go
- Editing an existing deck
- Exporting to PDF and HTML
- Throughput and batching
- Architecture
- Documentation map
- Development
- License
Why gopptx
| One engine, two languages | The Go package and the Python package are the same code. Behaviour, validation and output bytes are identical. |
| No Office dependency for authoring | Decks are generated from OOXML primitives directly. Office is only needed if you choose the PowerPoint PDF driver. |
| Reads and writes | Most libraries do one or the other. gopptx opens an existing deck, walks its shape tree, edits in place and writes it back. |
| Batching for throughput | Write-heavy Python workloads cross the FFI boundary once per batch, not once per operation. |
| Validated output | validate() checks OPC package rules, part relationships and content types before you ship a file. repair() fixes what it can. |
What it can do
| Area | Capability |
|---|---|
| Slides | Add, insert, remove, move, duplicate, hide, copy between decks, merge decks, rebind layouts |
| Text | Runs with bold/italic/underline/strike/caps/sub/superscript, fonts, sizes, colours, highlight, paragraph alignment/indent/spacing, autofit, anchors, RTL and complex-script handling |
| Shapes | ~200 preset geometries, freeforms, groups, connectors (straight/elbow/curved, auto-routing), solid/gradient/pattern/picture fills, lines, shadows, glow, soft edges, rotation, flips, z-order |
| Tables | Row/column insert and remove, cell merge and split, widths and heights, borders, banding flags, built-in and custom table styles, load from rows or dicts |
| Charts | 24 chart types, combo charts, multiple charts per slide, an embedded Excel workbook so the data stays editable in PowerPoint, axes, legends, data labels, trendlines, error bars, per-point formatting |
| Images & media | Local files, bytes, base64, URLs, natural-size placement, crops, effects, video, audio, online video, OLE objects, media extraction |
| Diagrams | SmartArt (every layout PowerPoint offers, node-level edits, quick styles, colour styles) and native Mermaid rendering |
| Layouts & themes | Slide masters, layouts, placeholders and overrides, notes master, handout master, colour schemes, font schemes, built-in theme presets, grid/stack/distribute layout helpers |
| Deck metadata | Core and app document properties, sections, comments and authors, speaker notes, headers and footers, custom XML parts, VBA projects, digital-signature detection, mark-as-final, modify password |
| Motion | Slide transitions (including morph), entrance/emphasis/exit animations, slide-show settings and custom shows |
| Import | Markdown → slides, HTML → slides, URL fetch → slides, Jinja2-style templating |
| Export | PDF (native Go renderer, LibreOffice or PowerPoint COM), HTML, flat XML, grayscale conversion, package compression and size analysis |
Every area above has a runnable example under examples/ — 96 of them.
Installation
Prerequisites
| Requirement | |
|---|---|
| Go | 1.25 or later |
| Python | 3.10 or later (only for the Python package) |
| C toolchain | Required to build the shared library for Python (cgo) |
| Platforms | Windows, Linux, macOS |
Go
go get github.com/djinn-soul/gopptx
That is all — the Go package has no cgo requirement and no native dependency.
Python
The Python package calls into a Go shared library, which must be built first.
# Windows (PowerShell)
.\scripts\build_python.ps1
pip install -e .
# Linux / macOS
./scripts/build_python.sh
pip install -e .
Optionally install orjson for faster JSON encoding on the bridge:
pip install orjson
The library filename is platform-specific — gopptx.dll, libgopptx.so or libgopptx.dylib.
Set GOPPTX_LIB_PATH if you keep it outside the package directory. See
Installation for details and
Troubleshooting if the library is not found.
Quickstart — Python
Create a deck
from gopptx import ChartType, Inches, Presentation
with Presentation.new("Quarterly Update") as pres:
pres.add_bullet_slide("Highlights", ["Revenue +12%", "Retention +4%"])
slide = pres.add_slide("Numbers")
pres.add_table_from_rows(
slide.index,
[["Region", "Revenue"], ["EMEA", "4.1M"], ["APAC", "2.8M"]],
bounds=(Inches(0.5), Inches(1.5), Inches(4.0), Inches(2.0)),
)
pres.add_chart(
slide.index,
ChartType.COLUMN,
["Q1", "Q2", "Q3", "Q4"],
[12.0, 15.5, 18.0, 21.0],
bounds=(Inches(5.0), Inches(1.5), Inches(4.0), Inches(3.0)),
title="Quarterly trend",
)
pres.save("quarterly.pptx")
Four things worth knowing straight away:
- All geometry is in EMU (English Metric Units, 914 400 per inch). Raw numbers like
(40, 120, 600, 220)are 600 EMU wide — smaller than a pixel, and the shape will appear to be missing. Always wrap coordinates inInches(),Point()orEmu(). Presentationis a context manager. Leaving thewithblock releases the native handle;save()writes the file. Both are explicit — nothing is written implicitly.Presentation.new(title)already contains a title slide at index0. Your firstadd_slide()becomes slide1.- Pass a
ChartTypemember, not a bare string. Strings still work but are deprecated and will be rejected in a future release.
The object layer
Alongside the flat pres.* methods there is a navigable object layer:
with Presentation("deck.pptx") as pres:
for slide in pres.slides:
print(slide.index, slide.title, len(slide.list_shapes()))
first = pres.slides[0]
first.set_transition("fade")
first.notes = "Open with the revenue number."
Quickstart — Go
Generate a deck in one call
package main
import (
"os"
"github.com/djinn-soul/gopptx/pkg/pptx"
)
func main() {
slides := []pptx.SlideContent{
pptx.NewSlide("Hello from gopptx").AddBullet("Created with gopptx"),
}
data, err := pptx.CreateWithSlides("My Deck", slides)
if err != nil {
panic(err)
}
if err := os.WriteFile("output.pptx", data, 0o600); err != nil {
panic(err)
}
}
Build a deck fluently
SlideBuilder mutates in place, so a dropped return value cannot silently discard content —
unlike SlideContent's value-receiver methods.
package main
import "github.com/djinn-soul/gopptx/pkg/pptx"
func main() {
intro := pptx.NewSlideBuilder("Agenda")
intro.AddBullet("Results")
intro.AddBullet("Outlook")
intro.WithNotes("Keep this to two minutes.")
err := pptx.NewPresentationBuilder("Quarterly Update").
WithTheme(pptx.ThemeCorporate).
WithSlideSize(pptx.SlideSize16x9()).
AddTitleSlide("FY26 Q3").
AddSlide(intro.Build()).
WriteToFile("quarterly.pptx")
if err != nil {
panic(err)
}
}
See the Go library guide for shapes, charts, tables and layout helpers.
Editing an existing deck
Python
from gopptx import Presentation
with Presentation("input.pptx") as pres:
pres.set_slide_title(0, "Updated Title")
pres.find_and_replace("Draft", "Final")
pres.update_chart_data_by_index(
2,
0,
{"categories": ["Q1", "Q2"], "series": [{"name": "Revenue", "values": [10.0, 14.0]}]},
)
pres.save("edited.pptx")
Go
p, err := pptx.Open("input.pptx")
if err != nil {
panic(err)
}
defer p.Close()
p.SetTitle("Updated Title")
p.SetAuthor("Reporting Bot")
if err := p.SaveAs("edited.pptx"); err != nil {
panic(err)
}
pptx.Open returns a metadata-and-charts facade. For full shape-level editing use
pptx.OpenEditor(path), which returns the PresentationEditor that also backs the JSON bridge.
Exporting to PDF and HTML
from gopptx import HTMLOptions, PDFOptions, Presentation
with Presentation("deck.pptx") as pres:
pres.export_pdf("deck.pdf", PDFOptions(driver="auto"))
pres.export_html("deck.html", HTMLOptions(embed_images=True))
export_pdf is the current name; save_as_pdf remains as a deprecated alias. There are
three real drivers, plus auto which
picks between them:
| Driver | Requires | Notes |
|---|---|---|
auto (default) |
— | Tries LibreOffice, then PowerPoint, then falls back to native. Use this in production. |
native |
nothing | Pure Go renderer. No external process, works in containers. Still maturing on layout-heavy decks — it emits a warning. |
libreoffice |
soffice on PATH |
Highest general fidelity without Office. On Windows add C:\Program Files\LibreOffice\program to PATH. |
powerpoint |
Microsoft PowerPoint + PowerShell (Windows) | Ground truth for fidelity; used to grade the native renderer. |
Full detail in the export guide.
Throughput and batching
Every Python call crosses a Python → C → Go boundary. For write-heavy loops, batch them so the crossing happens once:
from gopptx import Presentation
with Presentation.new("Batch Demo") as pres:
with pres.batch(stop_on_error=True) as batch:
for i in range(500):
batch.add_slide(f"Slide {i}")
pres.save("batch.pptx")
Read operations are rejected inside a batch() block by design — a buffered write has not
executed yet, so a read would see stale state. Use execute_batch() with an explicit command
list when you need reads and writes interleaved. See
Batch execution.
Architecture
┌──────────────────────────────┐
│ python/gopptx │ typed Python API — Presentation, Slide, Shape, Table, Chart
│ (ctypes → JSON envelopes) │
└───────────────┬──────────────┘
│ JSON command envelope, 179 operations
┌───────────────▼──────────────┐
│ bindings/c │ handle-based C ABI (cgo, c-shared)
└───────────────┬──────────────┘
│
┌───────────────▼──────────────┐
│ pkg/pptx/editor │ command dispatch over a loaded presentation
│ pkg/pptx │ high-level Go API — builders, shapes, charts, tables, text
│ internal/pptxxml │ OOXML serialisation
└──────────────────────────────┘
The JSON envelope is a stable contract, so any language that can call a C function or shell out
to the CLI can drive the engine. Operation identifiers and payload shapes are defined in
pkg/pptx/editor/opspec.go; the Python constants in python/gopptx/_ops_constants.py are
generated from it. See Bridge operations.
Documentation map
Start here
| Page | What it covers |
|---|---|
| Installation | Go module, Python shared library, environment variables |
| Quickstart | First deck in both languages |
| Core concepts | Handles, sessions, the command envelope, batching, units |
Guides
| Page | What it covers |
|---|---|
| Python library | The full Python workflow, method by area |
| Go library | Builders, the editor, shapes, charts, layout helpers |
| Batch execution | Throughput patterns and their limits |
| Export | PDF drivers, HTML options, fidelity notes |
Reference
| Page | What it covers |
|---|---|
| API overview | The three surfaces and when to use each |
Python Presentation API |
All 208 methods, grouped |
| Go API | Packages, types and constructors |
| Bridge operations | All 179 JSON operations |
| Feature matrix | Honest comparison against python-pptx |
Examples
examples/— 96 runnable Go and Python programs- Showcase — 30 annotated recipes, simple → complex, with screenshots
Development
task build:go # build the C shared library for Python
task test # Go + Python test suites
task lint # golangci-lint, ruff, basedpyright, generated-code drift
task generate # regenerate everything derived from Go declarations
task docs:serve # MkDocs Material on http://localhost:8000
Parts of the Python surface — operation constants, chart-type and shape-type enums, the slide
builder — are generated from the Go declarations. Edit the Go source and run task generate;
never hand-edit the generated files. task check:generated fails the build if they drift.
See CONTRIBUTING.md for the full setup, code standards and PR checklist.
License
Apache License 2.0 — see LICENSE.
Metadata
Release files for gopptx 0.2.0a5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gopptx-0.2.0a5-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| gopptx-0.2.0a5-py3-none-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64, Linux glibc 2.5+ x86-64 | Details |
| gopptx-0.2.0a5-py3-none-macosx_10_13_universal2.whl | Python 3 | none | macOS 10.13+ universal2 (ARM64, x86-64) | Details |
Total release size: 25.0 MB
Release files / gopptx-0.2.0a5-py3-none-win_amd64.whl
| Download URL | gopptx-0.2.0a5-py3-none-win_amd64.whl |
|---|---|
| Size | 8.4 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
16a0c0162cc60b559c28ebc9c3d1e24c3b8140450185a21eb5e91835e556723d
|
|
BLAKE2b-256 checksum How to use checksums |
a89b86fe06f985c3d9e61590647a7f96649c62bf69ef27aaebbddb92cf685162
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Release files / gopptx-0.2.0a5-py3-none-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl
| Download URL | gopptx-0.2.0a5-py3-none-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 8.8 MB |
| Tags | Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
54f27668e308916e9ab636ee288448d8500ca3e448e741fb50e6208570e27d70
|
|
BLAKE2b-256 checksum How to use checksums |
ae2c0a15a017ef03dbe28fa31ceb0c17533892d415b1fc95d86641fe3731ca23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Release files / gopptx-0.2.0a5-py3-none-macosx_10_13_universal2.whl
| Download URL | gopptx-0.2.0a5-py3-none-macosx_10_13_universal2.whl |
|---|---|
| Size | 7.7 MB |
| Tags | Python 3 macOS 10.13+ universal2 (ARM64, x86-64) |
|
SHA-256 checksum How to use checksums |
0061bf9269341dbcc3d535fd2e331b61267682975779851ff6a47a3c22105d72
|
|
BLAKE2b-256 checksum How to use checksums |
d7a9487dc8a22fee3d47bb812bf3d7b00e72f16eddfaffa0befc4fe7b8166bc8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|