xmlppt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xmlppt-0.1.0/PKG-INFO +102 -0
- xmlppt-0.1.0/README.md +82 -0
- xmlppt-0.1.0/pyproject.toml +31 -0
- xmlppt-0.1.0/setup.cfg +4 -0
- xmlppt-0.1.0/tests/test_editor.py +274 -0
- xmlppt-0.1.0/xmlppt/__init__.py +3 -0
- xmlppt-0.1.0/xmlppt/editor.py +1085 -0
- xmlppt-0.1.0/xmlppt.egg-info/PKG-INFO +102 -0
- xmlppt-0.1.0/xmlppt.egg-info/SOURCES.txt +10 -0
- xmlppt-0.1.0/xmlppt.egg-info/dependency_links.txt +1 -0
- xmlppt-0.1.0/xmlppt.egg-info/requires.txt +5 -0
- xmlppt-0.1.0/xmlppt.egg-info/top_level.txt +1 -0
xmlppt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xmlppt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PowerPoint editing via Open XML package manipulation
|
|
5
|
+
Author: Your Name
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jlondon626/powerpoint_editor
|
|
8
|
+
Project-URL: Repository, https://github.com/jlondon626/powerpoint_editor
|
|
9
|
+
Keywords: pptx,powerpoint,openxml,slides,excel
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: lxml>=4.0.0
|
|
18
|
+
Requires-Dist: openpyxl>=3.0.0
|
|
19
|
+
Requires-Dist: pywin32>=306; sys_platform == "win32"
|
|
20
|
+
|
|
21
|
+
# xmlppt
|
|
22
|
+
|
|
23
|
+
A small utility package for editing PowerPoint (.pptx) files by
|
|
24
|
+
manipulating the Open XML package directly. It supports duplicating
|
|
25
|
+
template slides, editing named textboxes, updating embedded Excel
|
|
26
|
+
workbooks for charts, and basic listing/debug utilities.
|
|
27
|
+
|
|
28
|
+
This repository contains a single package `xmlppt` with the main
|
|
29
|
+
implementation in `xmlppt/editor.py` and a minimal `main.py` example
|
|
30
|
+
entrypoint.
|
|
31
|
+
|
|
32
|
+
Requirements
|
|
33
|
+
- Python 3.10+
|
|
34
|
+
- lxml
|
|
35
|
+
- openpyxl
|
|
36
|
+
- pywin32 (only required for `refresh_chart()` on Windows)
|
|
37
|
+
|
|
38
|
+
Install
|
|
39
|
+
|
|
40
|
+
Create a virtual environment and install dependencies:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python -m venv .venv
|
|
44
|
+
.venv\Scripts\Activate.ps1 # Windows PowerShell
|
|
45
|
+
pip install -r requirements.txt
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Install the package locally for development:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install -e .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Quick usage (programmatic)
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from xmlppt import PowerPointEditor
|
|
58
|
+
|
|
59
|
+
editor = PowerPointEditor('input.pptx')
|
|
60
|
+
editor.duplicate_template_slide('RESERVE_WATERFALL')
|
|
61
|
+
editor.edit_textbox_html('AOM Text', '<b>Updated</b>')
|
|
62
|
+
editor.save('output.pptx')
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
CLI example
|
|
66
|
+
|
|
67
|
+
Run the example entrypoint which shows basic diagnostics and attempts
|
|
68
|
+
to run a sample flow (expects a marker template slide):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python main.py --input example.pptx --run-example
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Run tests
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pytest -q
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
CI
|
|
81
|
+
|
|
82
|
+
A GitHub Actions workflow is included at `.github/workflows/python-package.yml`
|
|
83
|
+
that installs the package and runs the test suite on Windows.
|
|
84
|
+
|
|
85
|
+
Publishing
|
|
86
|
+
|
|
87
|
+
A release workflow is included at `.github/workflows/publish.yml`.
|
|
88
|
+
When you create a GitHub release and publish it, the workflow will build
|
|
89
|
+
and upload the package to PyPI using the `PYPI_API_TOKEN` secret.
|
|
90
|
+
|
|
91
|
+
To configure publishing:
|
|
92
|
+
|
|
93
|
+
1. Create a PyPI API token at https://pypi.org/manage/account/token/
|
|
94
|
+
2. Add it to your repository secrets as `PYPI_API_TOKEN`
|
|
95
|
+
3. Create a release on GitHub
|
|
96
|
+
|
|
97
|
+
Notes
|
|
98
|
+
- The `refresh_chart()` function uses COM automation to refresh chart
|
|
99
|
+
visuals inside PowerPoint; this only works on Windows with PowerPoint
|
|
100
|
+
installed.
|
|
101
|
+
- The package manipulates the raw PPTX (zip) contents; always test on
|
|
102
|
+
copies of presentations before running on production files.
|
xmlppt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# xmlppt
|
|
2
|
+
|
|
3
|
+
A small utility package for editing PowerPoint (.pptx) files by
|
|
4
|
+
manipulating the Open XML package directly. It supports duplicating
|
|
5
|
+
template slides, editing named textboxes, updating embedded Excel
|
|
6
|
+
workbooks for charts, and basic listing/debug utilities.
|
|
7
|
+
|
|
8
|
+
This repository contains a single package `xmlppt` with the main
|
|
9
|
+
implementation in `xmlppt/editor.py` and a minimal `main.py` example
|
|
10
|
+
entrypoint.
|
|
11
|
+
|
|
12
|
+
Requirements
|
|
13
|
+
- Python 3.10+
|
|
14
|
+
- lxml
|
|
15
|
+
- openpyxl
|
|
16
|
+
- pywin32 (only required for `refresh_chart()` on Windows)
|
|
17
|
+
|
|
18
|
+
Install
|
|
19
|
+
|
|
20
|
+
Create a virtual environment and install dependencies:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m venv .venv
|
|
24
|
+
.venv\Scripts\Activate.ps1 # Windows PowerShell
|
|
25
|
+
pip install -r requirements.txt
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Install the package locally for development:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install -e .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Quick usage (programmatic)
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from xmlppt import PowerPointEditor
|
|
38
|
+
|
|
39
|
+
editor = PowerPointEditor('input.pptx')
|
|
40
|
+
editor.duplicate_template_slide('RESERVE_WATERFALL')
|
|
41
|
+
editor.edit_textbox_html('AOM Text', '<b>Updated</b>')
|
|
42
|
+
editor.save('output.pptx')
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
CLI example
|
|
46
|
+
|
|
47
|
+
Run the example entrypoint which shows basic diagnostics and attempts
|
|
48
|
+
to run a sample flow (expects a marker template slide):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python main.py --input example.pptx --run-example
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Run tests
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pytest -q
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
CI
|
|
61
|
+
|
|
62
|
+
A GitHub Actions workflow is included at `.github/workflows/python-package.yml`
|
|
63
|
+
that installs the package and runs the test suite on Windows.
|
|
64
|
+
|
|
65
|
+
Publishing
|
|
66
|
+
|
|
67
|
+
A release workflow is included at `.github/workflows/publish.yml`.
|
|
68
|
+
When you create a GitHub release and publish it, the workflow will build
|
|
69
|
+
and upload the package to PyPI using the `PYPI_API_TOKEN` secret.
|
|
70
|
+
|
|
71
|
+
To configure publishing:
|
|
72
|
+
|
|
73
|
+
1. Create a PyPI API token at https://pypi.org/manage/account/token/
|
|
74
|
+
2. Add it to your repository secrets as `PYPI_API_TOKEN`
|
|
75
|
+
3. Create a release on GitHub
|
|
76
|
+
|
|
77
|
+
Notes
|
|
78
|
+
- The `refresh_chart()` function uses COM automation to refresh chart
|
|
79
|
+
visuals inside PowerPoint; this only works on Windows with PowerPoint
|
|
80
|
+
installed.
|
|
81
|
+
- The package manipulates the raw PPTX (zip) contents; always test on
|
|
82
|
+
copies of presentations before running on production files.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "xmlppt"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "PowerPoint editing via Open XML package manipulation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Your Name" },
|
|
14
|
+
]
|
|
15
|
+
urls = { "Homepage" = "https://github.com/jlondon626/powerpoint_editor", "Repository" = "https://github.com/jlondon626/powerpoint_editor" }
|
|
16
|
+
dependencies = [
|
|
17
|
+
"lxml>=4.0.0",
|
|
18
|
+
"openpyxl>=3.0.0",
|
|
19
|
+
"pywin32>=306; sys_platform == \"win32\"",
|
|
20
|
+
]
|
|
21
|
+
keywords = ["pptx", "powerpoint", "openxml", "slides", "excel"]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Operating System :: Microsoft :: Windows",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.setuptools]
|
|
31
|
+
packages = ["xmlppt"]
|
xmlppt-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
import zipfile
|
|
3
|
+
import os
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
from openpyxl import Workbook, load_workbook
|
|
6
|
+
from xmlppt import editor as edmod
|
|
7
|
+
from xmlppt import PowerPointEditor
|
|
8
|
+
from lxml import etree
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
P_NS = edmod.P_NS
|
|
12
|
+
A_NS = edmod.A_NS
|
|
13
|
+
R_NS = edmod.R_NS
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _make_minimal_pptx(path: str) -> None:
|
|
17
|
+
"""Create a minimal pptx zip with one slide containing a textbox and a table."""
|
|
18
|
+
# Content types
|
|
19
|
+
content_types = (
|
|
20
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
21
|
+
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
|
|
22
|
+
'<Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>'
|
|
23
|
+
'</Types>'
|
|
24
|
+
).encode("utf-8")
|
|
25
|
+
|
|
26
|
+
# presentation.xml
|
|
27
|
+
presentation = (
|
|
28
|
+
f'<?xml version="1.0" encoding="UTF-8"?>'
|
|
29
|
+
f'<p:presentation xmlns:p="{P_NS}" xmlns:r="{R_NS}">'
|
|
30
|
+
f'<p:sldIdLst>'
|
|
31
|
+
f'<p:sldId id="256" r:id="rId1"/>'
|
|
32
|
+
f'</p:sldIdLst>'
|
|
33
|
+
f'</p:presentation>'
|
|
34
|
+
).encode("utf-8")
|
|
35
|
+
|
|
36
|
+
presentation_rels = (
|
|
37
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
38
|
+
f'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
39
|
+
f'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/>'
|
|
40
|
+
'</Relationships>'
|
|
41
|
+
).encode("utf-8")
|
|
42
|
+
|
|
43
|
+
# slide1.xml with a textbox named TestBox and a table named MyTable
|
|
44
|
+
slide = (
|
|
45
|
+
f'<?xml version="1.0" encoding="UTF-8"?>'
|
|
46
|
+
f'<p:sld xmlns:p="{P_NS}" xmlns:a="{A_NS}" xmlns:r="{R_NS}">'
|
|
47
|
+
f'<p:cSld><p:spTree>'
|
|
48
|
+
# Textbox
|
|
49
|
+
f'<p:sp>'
|
|
50
|
+
f' <p:nvSpPr><p:cNvPr id="2" name="TestBox"/></p:nvSpPr>'
|
|
51
|
+
f' <p:txBody><a:bodyPr/><a:lstStyle/>'
|
|
52
|
+
f' <a:p><a:r><a:rPr/><a:t>Hello</a:t></a:r></a:p>'
|
|
53
|
+
f' </p:txBody>'
|
|
54
|
+
f'</p:sp>'
|
|
55
|
+
# Table in graphicFrame
|
|
56
|
+
f'<p:graphicFrame>'
|
|
57
|
+
f' <p:nvGraphicFramePr><p:cNvPr id="5" name="MyTable"/></p:nvGraphicFramePr>'
|
|
58
|
+
f' <a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/table">'
|
|
59
|
+
f' <a:tbl>'
|
|
60
|
+
f' <a:tr>'
|
|
61
|
+
f' <a:tc><a:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>R1C1</a:t></a:r></a:p></a:txBody></a:tc>'
|
|
62
|
+
f' <a:tc><a:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>R1C2</a:t></a:r></a:p></a:txBody></a:tc>'
|
|
63
|
+
f' </a:tr>'
|
|
64
|
+
f' <a:tr>'
|
|
65
|
+
f' <a:tc><a:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>R2C1</a:t></a:r></a:p></a:txBody></a:tc>'
|
|
66
|
+
f' <a:tc><a:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>R2C2</a:t></a:r></a:p></a:txBody></a:tc>'
|
|
67
|
+
f' </a:tr>'
|
|
68
|
+
f' </a:tbl>'
|
|
69
|
+
f' </a:graphicData></a:graphic>'
|
|
70
|
+
f'</p:graphicFrame>'
|
|
71
|
+
f'</p:spTree></p:cSld></p:sld>'
|
|
72
|
+
).encode("utf-8")
|
|
73
|
+
|
|
74
|
+
# create zip
|
|
75
|
+
with zipfile.ZipFile(path, 'w') as z:
|
|
76
|
+
z.writestr('[Content_Types].xml', content_types)
|
|
77
|
+
z.writestr('ppt/presentation.xml', presentation)
|
|
78
|
+
z.writestr('ppt/_rels/presentation.xml.rels', presentation_rels)
|
|
79
|
+
z.writestr('ppt/slides/slide1.xml', slide)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _make_minimal_pptx_with_chart(path: str) -> None:
|
|
83
|
+
content_types = (
|
|
84
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
85
|
+
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
|
|
86
|
+
'<Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>'
|
|
87
|
+
'<Override PartName="/ppt/charts/chart1.xml" ContentType="application/vnd.openxmlformats-officedocument.drawingml.chart+xml"/>'
|
|
88
|
+
'<Override PartName="/ppt/embeddings/Microsoft_Excel_Worksheet1.xlsx" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"/>'
|
|
89
|
+
'</Types>'
|
|
90
|
+
).encode('utf-8')
|
|
91
|
+
|
|
92
|
+
presentation = (
|
|
93
|
+
f'<?xml version="1.0" encoding="UTF-8"?>'
|
|
94
|
+
f'<p:presentation xmlns:p="{P_NS}" xmlns:r="{R_NS}">'
|
|
95
|
+
f'<p:sldIdLst>'
|
|
96
|
+
f'<p:sldId id="256" r:id="rId1"/>'
|
|
97
|
+
f'</p:sldIdLst>'
|
|
98
|
+
f'</p:presentation>'
|
|
99
|
+
).encode('utf-8')
|
|
100
|
+
|
|
101
|
+
presentation_rels = (
|
|
102
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
103
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
104
|
+
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/>'
|
|
105
|
+
'</Relationships>'
|
|
106
|
+
).encode('utf-8')
|
|
107
|
+
|
|
108
|
+
slide = (
|
|
109
|
+
f'<?xml version="1.0" encoding="UTF-8"?>'
|
|
110
|
+
f'<p:sld xmlns:p="{P_NS}" xmlns:a="{A_NS}" xmlns:r="{R_NS}">'
|
|
111
|
+
f'<p:cSld><p:spTree>'
|
|
112
|
+
f'<p:graphicFrame>'
|
|
113
|
+
f' <p:nvGraphicFramePr><p:cNvPr id="5" name="TestChart"/></p:nvGraphicFramePr>'
|
|
114
|
+
f' <a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart">'
|
|
115
|
+
f' <c:chart xmlns:c="{edmod.C_NS}" xmlns:r="{R_NS}" r:id="rId1"/>'
|
|
116
|
+
f' </a:graphicData></a:graphic>'
|
|
117
|
+
f'</p:graphicFrame>'
|
|
118
|
+
f'</p:spTree></p:cSld></p:sld>'
|
|
119
|
+
).encode('utf-8')
|
|
120
|
+
|
|
121
|
+
chart_xml = (
|
|
122
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
123
|
+
f'<c:chartSpace xmlns:c="{edmod.C_NS}" xmlns:a="{A_NS}" xmlns:r="{R_NS}">'
|
|
124
|
+
f' <c:chart>'
|
|
125
|
+
f' <c:plotArea>'
|
|
126
|
+
f' <c:barChart>'
|
|
127
|
+
f' <c:ser>'
|
|
128
|
+
f' <c:cat>'
|
|
129
|
+
f' <c:strRef>'
|
|
130
|
+
f' <c:strCache>'
|
|
131
|
+
f' <c:ptCount val="2"/>'
|
|
132
|
+
f' <c:pt idx="0"><c:v>One</c:v></c:pt>'
|
|
133
|
+
f' <c:pt idx="1"><c:v>Two</c:v></c:pt>'
|
|
134
|
+
f' </c:strCache>'
|
|
135
|
+
f' </c:strRef>'
|
|
136
|
+
f' </c:cat>'
|
|
137
|
+
f' <c:val>'
|
|
138
|
+
f' <c:numRef>'
|
|
139
|
+
f' <c:numCache>'
|
|
140
|
+
f' <c:ptCount val="2"/>'
|
|
141
|
+
f' <c:pt idx="0"><c:v>10</c:v></c:pt>'
|
|
142
|
+
f' <c:pt idx="1"><c:v>20</c:v></c:pt>'
|
|
143
|
+
f' </c:numCache>'
|
|
144
|
+
f' </c:numRef>'
|
|
145
|
+
f' </c:val>'
|
|
146
|
+
f' </c:ser>'
|
|
147
|
+
f' </c:barChart>'
|
|
148
|
+
f' </c:plotArea>'
|
|
149
|
+
f' </c:chart>'
|
|
150
|
+
f'</c:chartSpace>'
|
|
151
|
+
).encode('utf-8')
|
|
152
|
+
|
|
153
|
+
chart_rels = (
|
|
154
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
155
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
156
|
+
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/package" Target="../embeddings/Microsoft_Excel_Worksheet1.xlsx"/>'
|
|
157
|
+
'</Relationships>'
|
|
158
|
+
).encode('utf-8')
|
|
159
|
+
|
|
160
|
+
wb = Workbook()
|
|
161
|
+
ws = wb.active
|
|
162
|
+
ws.title = 'Sheet1'
|
|
163
|
+
ws['A1'] = 'Category'
|
|
164
|
+
ws['B1'] = 'Value'
|
|
165
|
+
ws['A2'] = 'One'
|
|
166
|
+
ws['B2'] = 10
|
|
167
|
+
out = BytesIO()
|
|
168
|
+
wb.save(out)
|
|
169
|
+
workbook_bytes = out.getvalue()
|
|
170
|
+
|
|
171
|
+
with zipfile.ZipFile(path, 'w') as z:
|
|
172
|
+
z.writestr('[Content_Types].xml', content_types)
|
|
173
|
+
z.writestr('ppt/presentation.xml', presentation)
|
|
174
|
+
z.writestr('ppt/_rels/presentation.xml.rels', presentation_rels)
|
|
175
|
+
z.writestr('ppt/slides/slide1.xml', slide)
|
|
176
|
+
z.writestr('ppt/slides/_rels/slide1.xml.rels', (
|
|
177
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
178
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
179
|
+
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" Target="../charts/chart1.xml"/>'
|
|
180
|
+
'</Relationships>'
|
|
181
|
+
).encode('utf-8'))
|
|
182
|
+
z.writestr('ppt/charts/chart1.xml', chart_xml)
|
|
183
|
+
z.writestr('ppt/charts/_rels/chart1.xml.rels', chart_rels)
|
|
184
|
+
z.writestr('ppt/embeddings/Microsoft_Excel_Worksheet1.xlsx', workbook_bytes)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_import():
|
|
188
|
+
import xmlppt
|
|
189
|
+
|
|
190
|
+
assert hasattr(xmlppt, 'PowerPointEditor')
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_textbox_find_and_edit(tmp_path):
|
|
194
|
+
pptx = tmp_path / 'test.pptx'
|
|
195
|
+
_make_minimal_pptx(str(pptx))
|
|
196
|
+
|
|
197
|
+
editor = PowerPointEditor(str(pptx))
|
|
198
|
+
|
|
199
|
+
found = editor.find_textbox_anywhere('TestBox')
|
|
200
|
+
assert found['slide_number'] == 1
|
|
201
|
+
|
|
202
|
+
editor.edit_textbox_on_slide(1, 'TestBox', 'New\nLine')
|
|
203
|
+
|
|
204
|
+
slide_xml = editor.files['ppt/slides/slide1.xml'].decode('utf-8')
|
|
205
|
+
assert 'New' in slide_xml
|
|
206
|
+
assert 'Line' in slide_xml
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_table_find_and_edit_cell(tmp_path):
|
|
210
|
+
pptx = tmp_path / 'test_table.pptx'
|
|
211
|
+
_make_minimal_pptx(str(pptx))
|
|
212
|
+
|
|
213
|
+
editor = PowerPointEditor(str(pptx))
|
|
214
|
+
|
|
215
|
+
found = editor.find_table_anywhere('MyTable')
|
|
216
|
+
assert found['slide_number'] == 1
|
|
217
|
+
|
|
218
|
+
editor.edit_table_cell_on_slide(1, 'MyTable', 0, 1, 'NEWVAL')
|
|
219
|
+
|
|
220
|
+
slide_xml = editor.files['ppt/slides/slide1.xml'].decode('utf-8')
|
|
221
|
+
assert 'NEWVAL' in slide_xml
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def test_table_edit_range(tmp_path):
|
|
225
|
+
pptx = tmp_path / 'test_table2.pptx'
|
|
226
|
+
_make_minimal_pptx(str(pptx))
|
|
227
|
+
|
|
228
|
+
editor = PowerPointEditor(str(pptx))
|
|
229
|
+
|
|
230
|
+
data = [['A1', 'A2'], ['B1', 'B2']]
|
|
231
|
+
editor.edit_table_range_on_slide(1, 'MyTable', data)
|
|
232
|
+
|
|
233
|
+
slide_xml = editor.files['ppt/slides/slide1.xml'].decode('utf-8')
|
|
234
|
+
assert 'A1' in slide_xml and 'B2' in slide_xml
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_waterfall_data_edit(tmp_path):
|
|
238
|
+
pptx = tmp_path / 'test_chart.pptx'
|
|
239
|
+
_make_minimal_pptx_with_chart(str(pptx))
|
|
240
|
+
|
|
241
|
+
editor = PowerPointEditor(str(pptx))
|
|
242
|
+
editor.edit_waterfall_data_on_slide(1, 'TestChart', ['NewOne', 'NewTwo'], [11, 22])
|
|
243
|
+
|
|
244
|
+
chart_xml = editor.files['ppt/charts/chart1.xml'].decode('utf-8')
|
|
245
|
+
assert 'NewOne' in chart_xml
|
|
246
|
+
assert '22' in chart_xml
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def test_embedded_workbook_for_chart_on_slide(tmp_path):
|
|
250
|
+
pptx = tmp_path / 'test_embedded_chart.pptx'
|
|
251
|
+
_make_minimal_pptx_with_chart(str(pptx))
|
|
252
|
+
|
|
253
|
+
editor = PowerPointEditor(str(pptx))
|
|
254
|
+
editor.edit_embedded_workbook_for_chart_on_slide(
|
|
255
|
+
slide_number=1,
|
|
256
|
+
chart_name='TestChart',
|
|
257
|
+
categories=['CatA', 'CatB'],
|
|
258
|
+
values=[100, 200],
|
|
259
|
+
sheet_name='Sheet1',
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
chart_xml = editor.files['ppt/charts/chart1.xml'].decode('utf-8')
|
|
263
|
+
assert 'CatA' in chart_xml
|
|
264
|
+
assert '200' in chart_xml
|
|
265
|
+
|
|
266
|
+
rels = etree.fromstring(editor.files['ppt/charts/_rels/chart1.xml.rels'])
|
|
267
|
+
workbook_target = rels.xpath('./pr:Relationship', namespaces=edmod.NS)[0].get('Target')
|
|
268
|
+
workbook_path = PowerPointEditor._normalize_relationship_target('ppt/charts/chart1.xml', workbook_target)
|
|
269
|
+
workbook_data = editor.files[workbook_path]
|
|
270
|
+
|
|
271
|
+
loaded_wb = load_workbook(BytesIO(workbook_data))
|
|
272
|
+
sheet = loaded_wb['Sheet1']
|
|
273
|
+
assert sheet['A2'].value == 'CatA'
|
|
274
|
+
assert sheet['B3'].value == 200
|