Skip to main content

PowerPoint editing via Open XML package manipulation

Project description

xmlppt

A small utility package for editing PowerPoint (.pptx) files by manipulating the Open XML package directly. It supports duplicating template slides, editing named textboxes, updating embedded Excel workbooks for charts, and basic listing/debug utilities.

This repository contains a single package xmlppt with the main implementation in xmlppt/editor.py and a minimal main.py example entrypoint.

Requirements

  • Python 3.10+
  • lxml
  • openpyxl
  • pywin32 (only required for refresh_chart() on Windows)

Install

Create a virtual environment and install dependencies:

python -m venv .venv
.venv\Scripts\Activate.ps1   # Windows PowerShell
pip install -r requirements.txt

Install the package locally for development:

pip install -e .

Quick usage (programmatic)

from xmlppt import PowerPointEditor

editor = PowerPointEditor('input.pptx')

# One-step duplication: returns a SlideProxy bound to the new slide
slide = editor.duplicate_template_slide('RESERVE_WATERFALL')
slide.edit_textbox_html('AOM Text', '<b>Updated</b>')
slide.edit_embedded_workbook_for_chart('Waterfall chart', ['A','B'], [1,2])
slide.edit_chart_data_multi_series(
    'Multi-series chart',
    ['Q1', 'Q2', 'Q3', 'Q4'],
    {
        'Revenue': [10, 20, 30, 40],
        'Cost': [4, 8, 12, 16],
    },
)

# Alternatively obtain a proxy for an existing slide number
proxy = editor.get_slide(5)
proxy.edit_textbox('Title', 'Hello')

editor.replace_text_variables({'Quarter': 'Q1 2026'})

editor.drop_section('template_slides')
editor.save('output.pptx')

Examples

  • Duplicate a template and edit it in one step:
slide = editor.duplicate_template_slide('RESERVE_WATERFALL')
slide.edit_textbox_html('AOM Text', '<b>Generated</b>')
  • Get a SlideProxy for an existing slide:
slide = editor.get_slide(3)
slide.edit_table_cell('SummaryTable', 0, 1, 'Updated')

The SlideProxy exposes convenience methods delegating to the editor for common per-slide operations: edit_textbox, edit_textbox_html, edit_textbox_runs, find_text_variables, replace_text_variables, remove_shape, edit_table_cell, edit_table_range, edit_chart_data, edit_chart_data_multi_series, edit_waterfall_data, edit_embedded_workbook_for_chart, and refresh_chart.

  • Find and replace text variables:
variables = editor.find_text_variables()
editor.replace_text_variables({'Quarter': 'Q1 2026', 'Region': 'EMEA'})

slide = editor.get_slide(3)
slide.replace_text_variables({'Quarter': 'Q2 2026'})

CLI example

Run the example entrypoint which shows basic diagnostics and attempts to run a sample flow (expects a marker template slide):

python main.py --input example.pptx --run-example

Run tests

pytest -q

CI

A GitHub Actions workflow is included at .github/workflows/python-package.yml that installs the package and runs the test suite on Windows.

Publishing

A release workflow is included at .github/workflows/publish.yml. When you create a GitHub release and publish it, the workflow will build and upload the package to PyPI using the PYPI_API_TOKEN secret.

To configure publishing:

  1. Create a PyPI API token at https://pypi.org/manage/account/token/
  2. Add it to your repository secrets as PYPI_API_TOKEN
  3. Create a release on GitHub

Notes

  • The refresh_chart() function uses COM automation to refresh chart visuals inside PowerPoint; this only works on Windows with PowerPoint installed.
  • The package manipulates the raw PPTX (zip) contents; always test on copies of presentations before running on production files.

API Reference (summary)

PowerPointEditor

  • PowerPointEditor(input_pptx: str): Load a .pptx into memory. Exposes files (part bytes) and _has_changes.
  • save(output_pptx: Optional[str]) -> str: Write the modified package to disk; returns output path.
  • Slide/template handling
    • find_slide_by_shape_name(shape_name: str) -> int: Find a slide containing a named shape (returns slide number).
    • duplicate_slide(template_slide_number: int, before_section_name: Optional[str]) -> int: Duplicate a slide; returns new slide number.
    • duplicate_template_slide(template_name: str, before_section_name: str='template_slides') -> SlideProxy: Duplicate a template slide marked with TEMPLATE__<name> and return a SlideProxy bound to the new slide for convenient edits.
    • drop_section(section_name: str, delete_slide_parts: bool=True) -> list[int]: Remove a section and its slides; by default also deletes now-unreferenced private slide dependencies.
    • remove_shape_on_slide(slide_number: int, shape_name: str) -> None: Remove a named shape or graphic frame from a slide.
  • Text editing
    • find_textbox_anywhere(textbox_name: str) -> dict: Locate a textbox by name (returns slide_number and slide_part).
    • edit_textbox_on_slide(slide_number: int, textbox_name: str, new_text: str) -> None: Replace plain text (newlines -> paragraphs).
    • edit_textbox_html_on_slide(slide_number: int, textbox_name: str, html: str) -> None: Insert simple markup (<b> and <br/>).
    • edit_textbox_runs_on_slide(slide_number: int, textbox_name: str, paragraphs: list[list[tuple[str,bool]]]) -> None: Provide run-level (text, bold) paragraphs.
    • find_text_variables() -> list[dict]: Find {Variable} placeholders in slide text across the presentation.
    • find_text_variables_on_slide(slide_number: int) -> list[dict]: Find {Variable} placeholders on one slide.
    • replace_text_variables(variables: dict[str, object]) -> int: Replace {Variable} placeholders across the presentation; returns replacement count.
    • replace_text_variables_on_slide(slide_number: int, variables: dict[str, object]) -> int: Replace {Variable} placeholders on one slide.
    • Backward-compatible helpers: edit_textbox(...), edit_textbox_html(...) operate anywhere.
  • Chart editing
    • find_chart_on_slide(slide_number: int, chart_name: str) -> dict: Locate a chart frame and return chart_part and rel information.
    • find_chart_anywhere(chart_name: str) -> dict: Search all slides for a chart by name.
    • edit_chart_data_on_slide(slide_number: int, chart_name: str, categories: list[str], values: list[float]) -> None: Update chart caches and the embedded workbook for a regular chart when one is present.
    • edit_chart_data_multi_series_on_slide(slide_number: int, chart_name: str, categories: list[str], series: dict[str, list[float]], sheet_name: Optional[str]=None) -> None: Update a regular chart that already has multiple series; the template chart must contain the same number of series as the provided mapping.
    • edit_embedded_workbook_for_chart_on_slide(slide_number: int, chart_name: str, categories: list[str], values: list[float], sheet_name: Optional[str]=None, subtotal_indices: Optional[list[int]]=None) -> None: Update the embedded Excel file and chart XML (supports chartex subtotal indices).
    • Backward-compatible helpers: edit_chart_data(...), edit_chart_data_multi_series(...), edit_waterfall_data(...), edit_embedded_workbook_for_chart(...).
    • refresh_chart(chart_name: str, output_pptx: str) -> None: Attempt to refresh chart visuals using PowerPoint COM automation (Windows only).
  • Tables
    • find_table_on_slide(slide_number: int, table_name: str) -> dict: Locate a table graphicFrame by shape name.
    • find_table_anywhere(table_name: str) -> dict: Search all slides for a table.
    • edit_table_cell_on_slide(slide_number: int, table_name: str, row: int, col: int, new_text: str) -> None: Edit a single cell (0-based indices).
    • edit_table_range_on_slide(slide_number: int, table_name: str, data: list[list[str]]) -> None: Write a 2D list of strings into the table (must fit table size).
  • Debug/list utilities
    • list_all_textboxes(): Print named textboxes in the presentation.
    • list_graphic_frames(): Print graphic frames and chart/table presence per slide.
    • list_sections(): Print section names and slide counts.
    • dump_chartex_debug(chart_name: str): Print raw chart XML and rels for debugging.

See xmlppt/editor.py for full docstrings and implementation details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xmlppt-0.1.2.tar.gz (25.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xmlppt-0.1.2-py3-none-any.whl (23.7 kB view details)

Uploaded Python 3

File details

Details for the file xmlppt-0.1.2.tar.gz.

File metadata

  • Download URL: xmlppt-0.1.2.tar.gz
  • Upload date:
  • Size: 25.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for xmlppt-0.1.2.tar.gz
Algorithm Hash digest
SHA256 5432ce238cf182a07694b843c1f4049c94f5ae1a7f65d4585fbf0e6e6242b818
MD5 6f077f796cf421ba8af7ad347a6c4d17
BLAKE2b-256 004970e749d89caa258155da15f6ae74d333c480f79bd47fdc73c4f6d81db9ce

See more details on using hashes here.

File details

Details for the file xmlppt-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: xmlppt-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 23.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for xmlppt-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 2cfd81a939ef2ddc96a5524b526d291dbf9c897925f349e204cfb5a8ea57f1aa
MD5 762a9cd654014007087fe0ca980760e0
BLAKE2b-256 116b7504b2586d5e7ae58814e429720a4d3989d0b24dd5c8201f1c1daad2840b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page