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.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
SlideProxyfor 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,
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.
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:
- Create a PyPI API token at https://pypi.org/manage/account/token/
- Add it to your repository secrets as
PYPI_API_TOKEN - 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.pptxinto memory. Exposesfiles(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 withTEMPLATE__<name>and return aSlideProxybound 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 (returnsslide_numberandslide_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.- 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 returnchart_partand 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xmlppt-0.1.1.tar.gz.
File metadata
- Download URL: xmlppt-0.1.1.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b083225bc4c64fd463255063b141989257601ea46354f97ebd6165cb22ee79f0
|
|
| MD5 |
aeb7c0b6516f2111f10f6d06d8dc2801
|
|
| BLAKE2b-256 |
ee23debae1ef87ee5b23ec6d4c8369903cdf186971899f0482f94730be8f497f
|
File details
Details for the file xmlppt-0.1.1-py3-none-any.whl.
File metadata
- Download URL: xmlppt-0.1.1-py3-none-any.whl
- Upload date:
- Size: 22.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
483dfc43748b5a091034d26639ea1802aa03f8954a74faa5673cfb505f6fe542
|
|
| MD5 |
738294f17361a601532d48611f23115c
|
|
| BLAKE2b-256 |
9ba4dd9a5aad4a47d9623cb93406fba539a0be81fbef9d31f3e5576794dccbf7
|