node-pptx-templater 1.0.0
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.
- package/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +415 -0
- package/package.json +83 -0
- package/src/cli/commands/build.js +79 -0
- package/src/cli/commands/debug.js +46 -0
- package/src/cli/commands/extract.js +42 -0
- package/src/cli/commands/inspect.js +39 -0
- package/src/cli/commands/validate.js +36 -0
- package/src/cli/index.js +132 -0
- package/src/core/OutputWriter.js +181 -0
- package/src/core/PPTXTemplater.js +961 -0
- package/src/core/TemplateEngine.js +321 -0
- package/src/index.js +43 -0
- package/src/managers/ChartManager.js +317 -0
- package/src/managers/ContentTypesManager.js +160 -0
- package/src/managers/HyperlinkManager.js +451 -0
- package/src/managers/MediaManager.js +307 -0
- package/src/managers/RelationshipManager.js +401 -0
- package/src/managers/SlideManager.js +950 -0
- package/src/managers/TableManager.js +416 -0
- package/src/managers/ZipManager.js +298 -0
- package/src/managers/charts/ChartCacheGenerator.js +156 -0
- package/src/managers/charts/ChartParser.js +43 -0
- package/src/managers/charts/ChartRelationshipManager.js +33 -0
- package/src/managers/charts/ChartWorkbookUpdater.js +130 -0
- package/src/parsers/XMLParser.js +291 -0
- package/src/templates/blankPptx.js +1 -0
- package/src/templates/slideTemplate.js +314 -0
- package/src/utils/contentTypesHelper.js +149 -0
- package/src/utils/errors.js +129 -0
- package/src/utils/idUtils.js +54 -0
- package/src/utils/logger.js +113 -0
- package/src/utils/relationshipUtils.js +89 -0
- package/src/utils/xmlUtils.js +115 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-05-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `PPTXTemplater` — main orchestrator class with fluent chainable API
|
|
12
|
+
- `ZipManager` — PPTX ZIP archive loading, reading, writing, and re-packaging
|
|
13
|
+
- `XMLParser` — high-performance XML parsing/building via `fast-xml-parser`
|
|
14
|
+
- `RelationshipManager` — OpenXML `.rels` file parsing and management
|
|
15
|
+
- `SlideManager` — slide discovery, ordering, addition, cloning, and removal
|
|
16
|
+
- `ChartManager` — direct chart XML data updates (bar, line, pie, area, scatter)
|
|
17
|
+
- `TableManager` — table row replacement preserving all formatting
|
|
18
|
+
- `HyperlinkManager` — external URL and slide-to-slide hyperlink injection
|
|
19
|
+
- `MediaManager` — image embedding with SHA-1 content deduplication
|
|
20
|
+
- `TemplateEngine` — `{{placeholder}}` replacement with fragmented run normalization
|
|
21
|
+
- `OutputWriter` — file, buffer, and stream output
|
|
22
|
+
- CLI: `build`, `validate`, `inspect`, `extract`, `debug` commands
|
|
23
|
+
- Full JSDoc documentation throughout codebase
|
|
24
|
+
- Unit tests for all core components (Vitest)
|
|
25
|
+
- Integration tests with fixture-based testing
|
|
26
|
+
- Performance benchmarks
|
|
27
|
+
- GitHub Actions: CI, release, docs workflows
|
|
28
|
+
- ESLint + Prettier configuration
|
|
29
|
+
- MIT License
|
|
30
|
+
|
|
31
|
+
### Architecture
|
|
32
|
+
- Pure JavaScript ES Modules (no TypeScript)
|
|
33
|
+
- Zero PPTX generation library dependencies
|
|
34
|
+
- Only uses: `jszip`, `fast-xml-parser`, `fs-extra`, `commander`, `chalk`, `ora`
|
|
35
|
+
- Async/await throughout
|
|
36
|
+
- Private class fields (`#field`) for encapsulation
|
|
37
|
+
- Modular architecture following SOLID principles
|
|
38
|
+
|
|
39
|
+
[1.0.0]: https://github.com/your-org/pptx-templater/releases/tag/v1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pptx-template-engine contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# node-pptx-templater
|
|
2
|
+
|
|
3
|
+
> A low-level PowerPoint OpenXML templating engine for Node.js that generates and edits PPTX files directly through XML manipulation without relying on PowerPoint generation libraries.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/node-pptx-templater)
|
|
6
|
+
[](https://github.com/your-org/node-pptx-templater/actions/workflows/ci.yml)
|
|
7
|
+
[](https://codecov.io/gh/your-org/node-pptx-templater)
|
|
8
|
+
[](./LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://nodejs.org/api/esm.html)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ✨ Features
|
|
15
|
+
|
|
16
|
+
| Feature | Description |
|
|
17
|
+
|---|---|
|
|
18
|
+
| 🏗️ **Zero PPTX library dependencies** | Direct OpenXML/ZIP manipulation only |
|
|
19
|
+
| 🔁 **Text replacement** | Handles fragmented runs (`{{placeholders}}`) |
|
|
20
|
+
| 📊 **Chart updates** | Bar, Line, Pie, Area, Scatter — data only, style preserved |
|
|
21
|
+
| 📋 **Table updates** | Replace rows while preserving all formatting |
|
|
22
|
+
| 🔗 **Hyperlinks** | External URLs, shape links, slide-to-slide navigation |
|
|
23
|
+
| ➕ **Add new slides** | Text, images, shapes with auto-generated XML |
|
|
24
|
+
| 🎯 **Slide selection** | By number, ID, or custom tags |
|
|
25
|
+
| 📤 **Multiple outputs** | `saveToFile()`, `toBuffer()`, `toStream()` |
|
|
26
|
+
| 🔍 **Validation** | Structure validation with error reporting |
|
|
27
|
+
| 🛠️ **CLI** | `build`, `validate`, `inspect`, `extract`, `debug` |
|
|
28
|
+
| ⚡ **Performance** | Lazy loading, media deduplication, async/await |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 📦 Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install node-pptx-templater
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Requirements:** Node.js ≥ 18.0.0, ES Modules (`"type": "module"`)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🚀 Quick Start
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import { PPTXTemplater } from 'node-pptx-templater';
|
|
46
|
+
|
|
47
|
+
// Load a template PPTX
|
|
48
|
+
const ppt = await PPTXTemplater.load('template.pptx');
|
|
49
|
+
|
|
50
|
+
// Select slide(s) to work on (omit to work on all)
|
|
51
|
+
ppt.useSlide(1);
|
|
52
|
+
|
|
53
|
+
// Replace {{placeholder}} text
|
|
54
|
+
ppt.replaceText({
|
|
55
|
+
'{{title}}': 'Quarterly Report',
|
|
56
|
+
'{{year}}': '2026',
|
|
57
|
+
'{{company}}': 'Acme Corp',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Update chart data
|
|
61
|
+
ppt.updateChart('sales-chart', {
|
|
62
|
+
categories: ['Jan', 'Feb', 'Mar'],
|
|
63
|
+
series: [{ name: 'Revenue', values: [120, 150, 180] }],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Update table rows
|
|
67
|
+
ppt.updateTable('employees-table', [
|
|
68
|
+
['Name', 'Role', 'Department'],
|
|
69
|
+
['Alice', 'Engineer', 'Platform'],
|
|
70
|
+
['Bob', 'Designer', 'Product'],
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
// Save output
|
|
74
|
+
await ppt.saveToFile('./output/report.pptx');
|
|
75
|
+
|
|
76
|
+
// Or get a Buffer (for HTTP responses, emails, etc.)
|
|
77
|
+
const buffer = await ppt.toBuffer();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 📚 API Reference
|
|
83
|
+
|
|
84
|
+
### `PPTXTemplater`
|
|
85
|
+
|
|
86
|
+
#### Static Methods
|
|
87
|
+
|
|
88
|
+
| Method | Description |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `PPTXTemplater.load(source)` | Load from file path or Buffer |
|
|
91
|
+
| `PPTXTemplater.create()` | Create a blank presentation |
|
|
92
|
+
|
|
93
|
+
#### Slide Selection
|
|
94
|
+
|
|
95
|
+
| Method | Description |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `.useSlide(...refs)` | Select slides by number/tag to apply operations |
|
|
98
|
+
| `.useAllSlides()` | Reset to all slides |
|
|
99
|
+
| `.tagSlide(num, tag)` | Assign custom tag for later selection |
|
|
100
|
+
|
|
101
|
+
#### Content Manipulation
|
|
102
|
+
|
|
103
|
+
| Method | Description |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `.replaceText(replacements)` | Replace `{{key}}` placeholders |
|
|
106
|
+
| `.updateChart(chartId, data)` | Update chart categories/series/values |
|
|
107
|
+
| `.updateTable(tableId, rows)` | Replace table row data |
|
|
108
|
+
| `.addHyperlink(options)` | Add/replace external hyperlink on text |
|
|
109
|
+
| `.linkSlideNumber(options)` | Make slide reference navigate to another slide |
|
|
110
|
+
|
|
111
|
+
#### Slide Management
|
|
112
|
+
|
|
113
|
+
| Method | Description |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `.addSlide(options)` | Add a new slide with elements |
|
|
116
|
+
| `.cloneSlide(num, atPos?)` | Duplicate an existing slide |
|
|
117
|
+
| `.removeSlide(num)` | Delete a slide |
|
|
118
|
+
| `.reorderSlides(order)` | Reorder slides |
|
|
119
|
+
| `.exportSlides(...nums)` | Export subset to new engine |
|
|
120
|
+
|
|
121
|
+
#### Output
|
|
122
|
+
|
|
123
|
+
| Method | Description |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `.saveToFile(path)` | Write PPTX to disk |
|
|
126
|
+
| `.toBuffer()` | Get PPTX as Node.js Buffer |
|
|
127
|
+
| `.toStream()` | Get PPTX as readable stream |
|
|
128
|
+
|
|
129
|
+
#### Utilities
|
|
130
|
+
|
|
131
|
+
| Method | Description |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `.getInfo()` | Presentation metadata |
|
|
134
|
+
| `.validate()` | Structure validation |
|
|
135
|
+
| `.slideCount` | Total slide count (getter) |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 🏗️ Architecture
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
node-pptx-templater/
|
|
143
|
+
├── PPTXTemplater ← Main orchestrator / public API
|
|
144
|
+
│ ├── ZipManager ← ZIP archive read/write (JSZip)
|
|
145
|
+
│ ├── XMLParser ← XML parse/build (fast-xml-parser)
|
|
146
|
+
│ ├── RelationshipManager ← .rels file management
|
|
147
|
+
│ ├── SlideManager ← Slide discovery, ordering, CRUD
|
|
148
|
+
│ ├── ChartManager ← Chart XML data updates
|
|
149
|
+
│ ├── TableManager ← Table row replacement
|
|
150
|
+
│ ├── HyperlinkManager ← Hyperlink injection
|
|
151
|
+
│ ├── MediaManager ← Image embedding + deduplication
|
|
152
|
+
│ ├── TemplateEngine ← {{placeholder}} replacement
|
|
153
|
+
│ └── OutputWriter ← File/Buffer/Stream output
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### OpenXML PPTX Structure
|
|
157
|
+
|
|
158
|
+
A `.pptx` file is a ZIP archive (Open Packaging Convention) containing XML files:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
presentation.pptx (ZIP)
|
|
162
|
+
├── [Content_Types].xml # MIME types for all parts
|
|
163
|
+
├── _rels/.rels # Root relationships
|
|
164
|
+
├── ppt/
|
|
165
|
+
│ ├── presentation.xml # Slide list, master references
|
|
166
|
+
│ ├── _rels/presentation.xml.rels
|
|
167
|
+
│ ├── slides/
|
|
168
|
+
│ │ ├── slide1.xml # Slide content XML
|
|
169
|
+
│ │ ├── slide2.xml
|
|
170
|
+
│ │ └── _rels/slide1.xml.rels
|
|
171
|
+
│ ├── slideLayouts/ # Layout templates
|
|
172
|
+
│ ├── slideMasters/ # Master slide designs
|
|
173
|
+
│ ├── theme/ # Color & font themes
|
|
174
|
+
│ ├── charts/ # Embedded chart XML
|
|
175
|
+
│ └── media/ # Images, videos
|
|
176
|
+
└── docProps/
|
|
177
|
+
├── core.xml # Title, author, dates
|
|
178
|
+
└── app.xml # App metadata
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Relationship Flow
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
presentation.xml
|
|
185
|
+
└─[rId2]──► slides/slide1.xml
|
|
186
|
+
└─[rId1]──► slideLayouts/slideLayout1.xml
|
|
187
|
+
└─[rId2]──► charts/chart1.xml
|
|
188
|
+
└─ (chart data XML)
|
|
189
|
+
└─[rId3]──► media/image1.png
|
|
190
|
+
└─[rId4]──► https://example.com (external)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Text Fragmentation Problem & Solution
|
|
194
|
+
|
|
195
|
+
PowerPoint sometimes splits `{{placeholder}}` across multiple XML text runs:
|
|
196
|
+
|
|
197
|
+
```xml
|
|
198
|
+
<!-- What you write in PowerPoint: -->
|
|
199
|
+
{{title}}
|
|
200
|
+
|
|
201
|
+
<!-- What the XML actually contains: -->
|
|
202
|
+
<a:r><a:t>{{ti</a:t></a:r>
|
|
203
|
+
<a:r><a:t>tle}}</a:t></a:r>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Our solution:** The `TemplateEngine` normalizes runs within each paragraph by:
|
|
207
|
+
1. Concatenating all run text content
|
|
208
|
+
2. Detecting placeholders in the combined text
|
|
209
|
+
3. Merging affected runs into one (preserving the first run's formatting)
|
|
210
|
+
4. Injecting the replacement value
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 🖥️ CLI Usage
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Install globally
|
|
218
|
+
npm install -g node-pptx-templater
|
|
219
|
+
|
|
220
|
+
# Build a PPTX from template + JSON data
|
|
221
|
+
node-pptx-templater build template.pptx output.pptx --data data.json
|
|
222
|
+
|
|
223
|
+
# Validate a PPTX structure
|
|
224
|
+
node-pptx-templater validate presentation.pptx
|
|
225
|
+
|
|
226
|
+
# Inspect internal structure
|
|
227
|
+
node-pptx-templater inspect presentation.pptx --all
|
|
228
|
+
|
|
229
|
+
# Extract a slide's XML
|
|
230
|
+
node-pptx-templater extract presentation.pptx --slide 1 --out slide1.xml
|
|
231
|
+
|
|
232
|
+
# Debug a corrupted PPTX
|
|
233
|
+
node-pptx-templater debug broken.pptx --fix --out repaired.pptx
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Data JSON format for `build` command
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"text": {
|
|
241
|
+
"{{title}}": "Annual Report 2026",
|
|
242
|
+
"{{company}}": "Acme Corp",
|
|
243
|
+
"{{date}}": "January 2026"
|
|
244
|
+
},
|
|
245
|
+
"charts": {
|
|
246
|
+
"sales-chart": {
|
|
247
|
+
"categories": ["Q1", "Q2", "Q3", "Q4"],
|
|
248
|
+
"series": [
|
|
249
|
+
{ "name": "Revenue", "values": [145, 210, 190, 250] }
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"tables": {
|
|
254
|
+
"data-table": [
|
|
255
|
+
["Name", "Role", "Dept"],
|
|
256
|
+
["Alice", "Engineer", "Platform"]
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## 📊 Supported Chart Types
|
|
265
|
+
|
|
266
|
+
| OpenXML Element | Chart Type |
|
|
267
|
+
|---|---|
|
|
268
|
+
| `c:barChart` | Bar / Column |
|
|
269
|
+
| `c:lineChart` | Line |
|
|
270
|
+
| `c:pieChart` | Pie |
|
|
271
|
+
| `c:areaChart` | Area |
|
|
272
|
+
| `c:scatterChart` | Scatter / XY |
|
|
273
|
+
| `c:doughnutChart` | Doughnut |
|
|
274
|
+
| `c:radarChart` | Radar / Spider |
|
|
275
|
+
| `c:bubbleChart` | Bubble |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## ⚡ Performance
|
|
280
|
+
|
|
281
|
+
| Operation | Benchmark (avg) |
|
|
282
|
+
|---|---|
|
|
283
|
+
| Load 50-slide PPTX | ~120ms |
|
|
284
|
+
| Text replacement (20 placeholders) | ~2ms |
|
|
285
|
+
| Buffer generation | ~80ms |
|
|
286
|
+
| Chart update | ~5ms |
|
|
287
|
+
| Table update | ~3ms |
|
|
288
|
+
|
|
289
|
+
> Run your own: `npm run benchmark`
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 🐛 Troubleshooting
|
|
294
|
+
|
|
295
|
+
### Placeholders not being replaced
|
|
296
|
+
|
|
297
|
+
If `{{placeholder}}` is not replaced, the text is likely fragmented across runs.
|
|
298
|
+
Use the `PPTX_LOG_LEVEL=debug` environment variable to see detailed logs:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
PPTX_LOG_LEVEL=debug node your-script.js
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Extract the slide XML to inspect:
|
|
305
|
+
```bash
|
|
306
|
+
node-pptx-templater extract template.pptx --slide 1 --out slide1.xml
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Look for `<a:t>` elements and check if the placeholder is split.
|
|
310
|
+
|
|
311
|
+
### Chart not updating
|
|
312
|
+
|
|
313
|
+
Chart names must match the shape's `cNvPr name` attribute exactly.
|
|
314
|
+
Use `--inspect --charts` to see all chart names:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
node-pptx-templater inspect template.pptx --charts
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Generated PPTX fails to open
|
|
321
|
+
|
|
322
|
+
Run the debug command to check for structural issues:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
node-pptx-templater debug output.pptx
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Common causes:
|
|
329
|
+
- Missing content type entries for new slides
|
|
330
|
+
- Broken relationship IDs
|
|
331
|
+
- Invalid XML characters in replacement values
|
|
332
|
+
|
|
333
|
+
### File is corrupted
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
node-pptx-templater debug corrupted.pptx --fix --out repaired.pptx
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The debug command attempts:
|
|
340
|
+
- Removing invalid XML control characters
|
|
341
|
+
- Fixing unescaped `&` in text content
|
|
342
|
+
- Repairing broken relationship IDs
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 🔌 Plugin System
|
|
347
|
+
|
|
348
|
+
Extend the engine by subclassing `PPTXTemplater`:
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
import { PPTXTemplater } from 'pptx-templater';
|
|
352
|
+
|
|
353
|
+
class MyEngine extends PPTXTemplater {
|
|
354
|
+
/**
|
|
355
|
+
* Custom method: fills a slide from a data object.
|
|
356
|
+
*/
|
|
357
|
+
async fillFromData(slideNum, data) {
|
|
358
|
+
this.useSlide(slideNum);
|
|
359
|
+
|
|
360
|
+
const textReplacements = {};
|
|
361
|
+
for (const [key, val] of Object.entries(data.text || {})) {
|
|
362
|
+
textReplacements[`{{${key}}}`] = String(val);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
this.replaceText(textReplacements);
|
|
366
|
+
|
|
367
|
+
if (data.chart) {
|
|
368
|
+
this.updateChart(data.chart.id, data.chart);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return this;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Usage
|
|
376
|
+
const ppt = await MyEngine.load('template.pptx');
|
|
377
|
+
await ppt.fillFromData(1, {
|
|
378
|
+
text: { title: 'My Report', date: '2026-01-01' },
|
|
379
|
+
chart: { id: 'sales', categories: ['Q1'], series: [{ name: 'Rev', values: [100] }] },
|
|
380
|
+
});
|
|
381
|
+
await ppt.saveToFile('output.pptx');
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## 🛣️ Roadmap
|
|
387
|
+
|
|
388
|
+
- [ ] SmartArt data update
|
|
389
|
+
- [ ] Speaker notes modification
|
|
390
|
+
- [ ] Slide transitions and animation metadata editing
|
|
391
|
+
- [ ] PPTX → HTML export (read-only)
|
|
392
|
+
- [ ] Password-protected PPTX support
|
|
393
|
+
- [ ] Native chart creation from scratch (without template)
|
|
394
|
+
- [ ] Watch mode for development
|
|
395
|
+
- [ ] Browser/WASM support (via jszip already)
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## 🤝 Contributing
|
|
400
|
+
|
|
401
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
|
|
402
|
+
|
|
403
|
+
Quick steps:
|
|
404
|
+
```bash
|
|
405
|
+
git clone https://github.com/your-org/node-pptx-templater.git
|
|
406
|
+
cd node-pptx-templater
|
|
407
|
+
npm install
|
|
408
|
+
npm test
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 📄 License
|
|
414
|
+
|
|
415
|
+
MIT — see [LICENSE](./LICENSE)
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-pptx-templater",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Low-level PowerPoint OpenXML template engine for Node.js — generate and edit PPTX files directly through XML manipulation without relying on PowerPoint generation libraries.",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./src/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"node-pptx-templater": "./src/cli/index.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"test:coverage": "vitest run --coverage",
|
|
19
|
+
"lint": "eslint src tests",
|
|
20
|
+
"lint:fix": "eslint src tests --fix",
|
|
21
|
+
"format": "prettier --write \"src/**/*.js\" \"tests/**/*.js\" \"examples/**/*.js\"",
|
|
22
|
+
"format:check": "prettier --check \"src/**/*.js\" \"tests/**/*.js\"",
|
|
23
|
+
"benchmark": "node benchmarks/run.js",
|
|
24
|
+
"docs:build": "node scripts/generate-docs.js",
|
|
25
|
+
"validate": "node scripts/validate-setup.js",
|
|
26
|
+
"example:basic": "node examples/basic-usage.js",
|
|
27
|
+
"example:charts": "node examples/chart-update.js",
|
|
28
|
+
"example:tables": "node examples/table-update.js",
|
|
29
|
+
"example:slides": "node examples/slide-selection.js",
|
|
30
|
+
"prepublishOnly": "npm run lint && npm run test"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"pptx",
|
|
34
|
+
"powerpoint",
|
|
35
|
+
"openxml",
|
|
36
|
+
"template",
|
|
37
|
+
"presentation",
|
|
38
|
+
"slides",
|
|
39
|
+
"office",
|
|
40
|
+
"xml",
|
|
41
|
+
"chart",
|
|
42
|
+
"table",
|
|
43
|
+
"report",
|
|
44
|
+
"generator",
|
|
45
|
+
"nodejs",
|
|
46
|
+
"esm"
|
|
47
|
+
],
|
|
48
|
+
"author": {
|
|
49
|
+
"name": "node-pptx-templater contributors"
|
|
50
|
+
},
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "https://github.com/your-org/node-pptx-templater.git"
|
|
55
|
+
},
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/your-org/node-pptx-templater/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://your-org.github.io/node-pptx-templater",
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=18.0.0"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"fast-xml-parser": "^4.3.6",
|
|
65
|
+
"jszip": "^3.10.1",
|
|
66
|
+
"fs-extra": "^11.2.0",
|
|
67
|
+
"commander": "^12.0.0",
|
|
68
|
+
"chalk": "^5.3.0",
|
|
69
|
+
"ora": "^8.0.1"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"vitest": "^1.6.0",
|
|
73
|
+
"@vitest/coverage-v8": "^1.6.0",
|
|
74
|
+
"eslint": "^8.57.0",
|
|
75
|
+
"prettier": "^3.2.5"
|
|
76
|
+
},
|
|
77
|
+
"files": [
|
|
78
|
+
"src/",
|
|
79
|
+
"README.md",
|
|
80
|
+
"LICENSE",
|
|
81
|
+
"CHANGELOG.md"
|
|
82
|
+
]
|
|
83
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `build` CLI command — builds a PPTX from a template + JSON data.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { resolve } from 'path';
|
|
9
|
+
import { PPTXTemplater } from '../../index.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Executes the `build` CLI command.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} templatePath - Path to the template PPTX.
|
|
15
|
+
* @param {string} outputPath - Path for the generated PPTX.
|
|
16
|
+
* @param {Object} opts - CLI options.
|
|
17
|
+
*/
|
|
18
|
+
export async function buildCommand(templatePath, outputPath, opts) {
|
|
19
|
+
const spinner = ora(`Loading template: ${templatePath}`).start();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Load the template
|
|
23
|
+
const ppt = await PPTXTemplater.load(resolve(templatePath));
|
|
24
|
+
spinner.succeed(`Loaded template (${ppt.slideCount} slides)`);
|
|
25
|
+
|
|
26
|
+
// Apply slide filter if provided
|
|
27
|
+
if (opts.slide) {
|
|
28
|
+
const slideNumbers = opts.slide.split(',').map(n => parseInt(n.trim(), 10));
|
|
29
|
+
ppt.useSlide(...slideNumbers);
|
|
30
|
+
console.log(chalk.cyan(` → Using slides: ${slideNumbers.join(', ')}`));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Apply data from JSON file if provided
|
|
34
|
+
if (opts.data) {
|
|
35
|
+
spinner.start(`Loading data: ${opts.data}`);
|
|
36
|
+
const data = JSON.parse(readFileSync(resolve(opts.data), 'utf-8'));
|
|
37
|
+
|
|
38
|
+
// Apply text replacements
|
|
39
|
+
if (data.text) {
|
|
40
|
+
ppt.replaceText(data.text);
|
|
41
|
+
console.log(chalk.cyan(` → Replaced ${Object.keys(data.text).length} text placeholder(s)`));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Apply chart updates
|
|
45
|
+
if (data.charts) {
|
|
46
|
+
for (const [chartId, chartData] of Object.entries(data.charts)) {
|
|
47
|
+
ppt.updateChart(chartId, chartData);
|
|
48
|
+
console.log(chalk.cyan(` → Updated chart: ${chartId}`));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Apply table updates
|
|
53
|
+
if (data.tables) {
|
|
54
|
+
for (const [tableId, tableData] of Object.entries(data.tables)) {
|
|
55
|
+
ppt.updateTable(tableId, tableData);
|
|
56
|
+
console.log(chalk.cyan(` → Updated table: ${tableId}`));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
spinner.succeed('Data applied');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Save output
|
|
64
|
+
spinner.start(`Saving to: ${outputPath}`);
|
|
65
|
+
await ppt.saveToFile(resolve(outputPath));
|
|
66
|
+
spinner.succeed(chalk.green(`✓ Saved: ${outputPath}`));
|
|
67
|
+
|
|
68
|
+
console.log(chalk.dim(`\n Slides: ${ppt.slideCount}`));
|
|
69
|
+
console.log(chalk.dim(` Template: ${templatePath}`));
|
|
70
|
+
console.log(chalk.dim(` Output: ${outputPath}\n`));
|
|
71
|
+
|
|
72
|
+
} catch (err) {
|
|
73
|
+
spinner.fail(chalk.red(`Build failed: ${err.message}`));
|
|
74
|
+
if (process.env.PPTX_LOG_LEVEL === 'debug') {
|
|
75
|
+
console.error(err.stack);
|
|
76
|
+
}
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `debug` CLI command — diagnoses and repairs corrupted PPTX files.
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
import { repairXML } from '../../utils/xmlUtils.js';
|
|
8
|
+
import { PPTXTemplater } from '../../index.js';
|
|
9
|
+
|
|
10
|
+
export async function debugCommand(filePath, opts) {
|
|
11
|
+
const spinner = ora(`Loading PPTX for debug: ${filePath}`).start();
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const ppt = await PPTXTemplater.load(resolve(filePath));
|
|
15
|
+
spinner.succeed('Loaded successfully');
|
|
16
|
+
|
|
17
|
+
const result = ppt.validate();
|
|
18
|
+
|
|
19
|
+
console.log(chalk.bold.cyan('\n═══ Debug Report ═══\n'));
|
|
20
|
+
console.log(chalk.bold('Validation:'));
|
|
21
|
+
|
|
22
|
+
if (result.valid) {
|
|
23
|
+
console.log(chalk.green(' ✓ Structure is valid'));
|
|
24
|
+
} else {
|
|
25
|
+
console.log(chalk.red(` ✗ ${result.errors.length} error(s) found`));
|
|
26
|
+
result.errors.forEach(e => console.log(chalk.red(` • ${e}`)));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (result.warnings.length > 0) {
|
|
30
|
+
console.log(chalk.yellow(`\n ${result.warnings.length} warning(s):`));
|
|
31
|
+
result.warnings.forEach(w => console.log(chalk.yellow(` • ${w}`)));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (opts.fix && opts.out) {
|
|
35
|
+
spinner.start('Attempting repairs...');
|
|
36
|
+
await ppt.saveToFile(resolve(opts.out));
|
|
37
|
+
spinner.succeed(chalk.green(`✓ Repaired PPTX saved to: ${opts.out}`));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log('');
|
|
41
|
+
} catch (err) {
|
|
42
|
+
spinner.fail(chalk.red(`Debug failed: ${err.message}`));
|
|
43
|
+
if (process.env.PPTX_LOG_LEVEL === 'debug') console.error(err.stack);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|