node-pptx-templater 1.0.3 → 1.0.4
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 +28 -3
- package/README.md +136 -343
- package/package.json +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.3] - 2026-06-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Dynamic Formatting in updateTable**: Added support for inline cell styling (color fill `fill`, text alignment `align`, and `fontSize`) directly on cell objects passed to `updateTable`.
|
|
12
|
+
- **Comprehensive Tailwind Site**: Overhauled doc builder script to generate a premium Tailwind CSS documentation portal with clientside search, clipboard copying, sitemap.xml, robots.txt, and Schema.org metadata.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **XML Element Ordering**: Enforced strict schema-valid element sequence (`a:pPr` -> runs -> `a:endParaRPr`) in slide table cell paragraphs. This resolves the bug where split cells inheriting from template merged cells had their text runs ignored by PowerPoint's XML compiler.
|
|
16
|
+
- **Template Style Inheritance**: Fixed a bug in `updateTable` where cloned rows always copied the first data row (`trs[1]`). The engine now correctly inherits formatting, alignment, and fill styles from matching indices in the template (`trs[i]`) when available.
|
|
17
|
+
|
|
18
|
+
## [1.0.2] - 2026-06-02
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- **Table Cell Merging & Unmerging Engine**: Fully implemented horizontal cell spans (`gridSpan`, `hMerge`), vertical cell spans (`rowSpan`, `vMerge`), and rectangular block merges.
|
|
22
|
+
- **PowerPoint Repair Protection**: Implemented unique 32-bit unsigned `rowId` generation inside `<a16:rowId>` XML tags for all cloned and inserted rows, eliminating PowerPoint's "Repair Mode" error prompts.
|
|
23
|
+
- **Merge Integrations**: Integrated template-driven merges (`merge` configs array and cell-level `colSpan`/`rowSpan`) inside the main `updateTable` orchestrator.
|
|
24
|
+
- **Integration Test Suite**: Added a comprehensive merge test script under `tests/integration/PPTXMerge.test.js`.
|
|
25
|
+
|
|
26
|
+
## [1.0.1] - 2026-05-19
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- **CommonJS Target Conversion**: Converted the source code modules compilation and packaging layout from pure ES Modules (ESM) to CommonJS (CJS) to ensure compatibility with standard Node.js deployment, packaging, and edge runtime environments.
|
|
30
|
+
|
|
8
31
|
## [1.0.0] - 2026-05-17
|
|
9
32
|
|
|
10
33
|
### Added
|
|
@@ -16,7 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
39
|
- `ChartManager` — direct chart XML data updates (bar, line, pie, area, scatter)
|
|
17
40
|
- `TableManager` — table row replacement preserving all formatting
|
|
18
41
|
- `HyperlinkManager` — external URL and slide-to-slide hyperlink injection
|
|
19
|
-
- `MediaManager` — image embedding with SHA-1
|
|
42
|
+
- `MediaManager` — image embedding with SHA-1 deduplication
|
|
20
43
|
- `TemplateEngine` — `{{placeholder}}` replacement with fragmented run normalization
|
|
21
44
|
- `OutputWriter` — file, buffer, and stream output
|
|
22
45
|
- CLI: `build`, `validate`, `inspect`, `extract`, `debug` commands
|
|
@@ -29,11 +52,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
29
52
|
- MIT License
|
|
30
53
|
|
|
31
54
|
### Architecture
|
|
32
|
-
- Pure JavaScript ES Modules (no TypeScript)
|
|
33
55
|
- Zero PPTX generation library dependencies
|
|
34
56
|
- Only uses: `jszip`, `fast-xml-parser`, `fs-extra`, `commander`, `chalk`, `ora`
|
|
35
57
|
- Async/await throughout
|
|
36
58
|
- Private class fields (`#field`) for encapsulation
|
|
37
59
|
- Modular architecture following SOLID principles
|
|
38
60
|
|
|
39
|
-
[1.0.
|
|
61
|
+
[1.0.3]: https://github.com/jsuyog2/node-pptx-templater/compare/v1.0.2...v1.0.3
|
|
62
|
+
[1.0.2]: https://github.com/jsuyog2/node-pptx-templater/compare/v1.0.1...v1.0.2
|
|
63
|
+
[1.0.1]: https://github.com/jsuyog2/node-pptx-templater/compare/v1.0.0...v1.0.1
|
|
64
|
+
[1.0.0]: https://github.com/jsuyog2/node-pptx-templater/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
# node-pptx-templater
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> High-performance, low-level PowerPoint (PPTX) OpenXML template engine for Node.js. Dynamically replace text, insert images, update charts (with Excel workbook data caching), and merge table cells without PowerPoint corruption or Repair Mode prompts.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/node-pptx-templater)
|
|
6
|
-
[](https://www.npmjs.com/package/node-pptx-templater)
|
|
6
|
+
[](https://github.com/jsuyog2/node-pptx-templater/actions/workflows/ci.yml)
|
|
7
|
+
[](https://bundlephobia.com/package/node-pptx-templater)
|
|
8
|
+
[](https://www.npmjs.com/package/node-pptx-templater)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
[](https://nodejs.org)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ⚡ Why node-pptx-templater?
|
|
15
|
+
|
|
16
|
+
Traditional PowerPoint generation libraries require building slides from scratch in code, which is verbose, hard to maintain, and strips away the power of visual design tools.
|
|
17
|
+
|
|
18
|
+
`node-pptx-templater` takes a different approach: **Design visually in PowerPoint, populate dynamically in Node.js.**
|
|
19
|
+
|
|
20
|
+
You create slide decks using PowerPoint, Google Slides, or Keynote, set your formatting, themes, animations, and layouts, and place placeholders like `{{company}}` or `{{revenue-chart}}`. `node-pptx-templater` parses the template and updates text, injects images, replaces chart values (updating both Excel workbook data caches and XML shapes), and merges tables dynamically while keeping the presentation 100% compliant with standard OpenXML guidelines.
|
|
10
21
|
|
|
11
22
|
---
|
|
12
23
|
|
|
13
24
|
## ✨ Features
|
|
14
25
|
|
|
15
|
-
- 🏗️ **Zero
|
|
16
|
-
- 🔁 **
|
|
17
|
-
- 📊 **
|
|
18
|
-
- 📋 **
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
26
|
+
- 🏗️ **Zero Native Office/Java Dependencies**: Runs on pure Javascript/Node.js, making it ideal for high-throughput cloud environments, Lambda, or serverless runtimes.
|
|
27
|
+
- 🔁 **Fragmented Placeholder Resolution**: PowerPoint often splits text runs like `{{company}}` into `<a:r>` nodes. Our engine merges and resolves fragmented tags automatically.
|
|
28
|
+
- 📊 **Full Chart Engine Integration**: Supports Bar, Column, Line, Pie, Doughnut, Area, Scatter, and Bubble charts. Automatically synchronizes chart XML properties and coordinates with the embedded Excel sheets (`ppt/embeddings/`).
|
|
29
|
+
- 📋 **Flexible Table Merging & Templating**:
|
|
30
|
+
- Horizontal column merge (`gridSpan` & `hMerge`), vertical row merge (`rowSpan` & `vMerge`), and rectangular block merges.
|
|
31
|
+
- Formats cells dynamically with inline options (`align`, `fontSize`, `fill`).
|
|
32
|
+
- Automatically handles slide table duplicates by generating unique `<a16:rowId>` 32-bit hashes to **prevent PowerPoint Repair Mode** screens.
|
|
33
|
+
- 🎨 **Shape & Image Manipulation**: Find shapes, clone layout blocks with offsets, replace image sources while keeping exact positions, or delete elements.
|
|
34
|
+
- 🎯 **Slide Management Operations**: Duplicate, reorder, delete, and import slides from external templates with automatic media asset deduplication.
|
|
35
|
+
- 🔍 **Deep Packaging Integrity Validation**: Real-time checking of relationships, XML schemas, table column numbers, and override duplicates.
|
|
22
36
|
|
|
23
37
|
---
|
|
24
38
|
|
|
@@ -32,58 +46,92 @@ npm install node-pptx-templater
|
|
|
32
46
|
|
|
33
47
|
## 🚀 Quick Start
|
|
34
48
|
|
|
49
|
+
Get up and running in under 60 seconds with this simple template rendering example:
|
|
50
|
+
|
|
35
51
|
```js
|
|
36
52
|
const { PPTXTemplater } = require('node-pptx-templater');
|
|
37
53
|
|
|
38
|
-
async function
|
|
39
|
-
// Load presentation template
|
|
40
|
-
const ppt = await PPTXTemplater.load('
|
|
54
|
+
async function main() {
|
|
55
|
+
// 1. Load your PowerPoint presentation template
|
|
56
|
+
const ppt = await PPTXTemplater.load('monthly_report_template.pptx');
|
|
41
57
|
|
|
42
|
-
//
|
|
58
|
+
// 2. Select slide 1 and execute operations
|
|
43
59
|
ppt.useSlide(1)
|
|
44
|
-
.replaceTextByTag('title', '
|
|
45
|
-
.replaceMultiple({
|
|
60
|
+
.replaceTextByTag('title', 'Quarterly Earnings Report')
|
|
61
|
+
.replaceMultiple({
|
|
62
|
+
company: 'Acme Corporation',
|
|
63
|
+
year: '2026'
|
|
64
|
+
});
|
|
46
65
|
|
|
47
|
-
// Update chart data on Slide 2
|
|
66
|
+
// 3. Update chart series data on Slide 2
|
|
48
67
|
ppt.useSlide(2)
|
|
49
68
|
.updateChartData('sales-chart', {
|
|
50
69
|
categories: ['Q1', 'Q2', 'Q3', 'Q4'],
|
|
51
|
-
series: [
|
|
70
|
+
series: [
|
|
71
|
+
{ name: 'Target', values: [100, 120, 140, 160] },
|
|
72
|
+
{ name: 'Revenue', values: [105, 118, 145, 172] }
|
|
73
|
+
]
|
|
52
74
|
});
|
|
53
75
|
|
|
54
|
-
//
|
|
55
|
-
|
|
76
|
+
// 4. Update table with cell merging and formatting on Slide 3
|
|
77
|
+
ppt.useSlide(3)
|
|
78
|
+
.updateTable('sales-table', [
|
|
79
|
+
['Region', 'Q1 Actual', 'Q2 Actual', 'Status'],
|
|
80
|
+
['North', '120k', '140k', { value: 'On Track', align: 'ctr', fill: '10b981' }],
|
|
81
|
+
['South', '95k', '110k', { value: 'Review', align: 'ctr', fill: 'f59e0b' }]
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
// 5. Save the non-corrupted PPTX back to disk
|
|
85
|
+
await ppt.saveToFile('./output/annual_earnings.pptx');
|
|
56
86
|
}
|
|
57
|
-
|
|
87
|
+
|
|
88
|
+
main().catch(err => console.error(err));
|
|
58
89
|
```
|
|
59
90
|
|
|
60
91
|
---
|
|
61
92
|
|
|
62
|
-
## 🏗️ Architecture &
|
|
93
|
+
## 🏗️ OpenXML Architecture & Internals
|
|
63
94
|
|
|
64
|
-
A
|
|
95
|
+
A `.pptx` file is an OPC (Open Packaging Convention) ZIP archive containing structured XML documents and asset folders:
|
|
65
96
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
- `
|
|
97
|
+
- `[Content_Types].xml` – Global manifest declaring content MIME types for every file part in the ZIP.
|
|
98
|
+
- `_rels/.rels` – Root-level package relationship index.
|
|
99
|
+
- `ppt/presentation.xml` – Root presentation settings and slide inventory (`sldIdLst`).
|
|
100
|
+
- `ppt/slides/slideN.xml` – Main slide canvas storing shapes, lines, tables, text runs, and layout components.
|
|
101
|
+
- `ppt/slides/_rels/slideN.xml.rels` – Relationship indexes mapping slide XML components to charts, layouts, and image assets.
|
|
70
102
|
|
|
71
|
-
###
|
|
72
|
-
|
|
73
|
-
`node-pptx-templater`
|
|
103
|
+
### Preventing PowerPoint Table Repair Errors
|
|
104
|
+
PowerPoint slide tables utilize unique 32-bit identifiers inside `<a16:rowId>` nodes for collaborative edits. Duplicating rows using naive array copy operations results in overlapping IDs, triggering Microsoft PowerPoint's **"PowerPoint found a problem with content"** repair screen on open.
|
|
105
|
+
`node-pptx-templater` intercepts all table operations (adding, cloning, inserting, or merging rows) and dynamically injects newly generated unique `rowId` hashes, ensuring a seamless, warning-free loading experience in:
|
|
106
|
+
- Microsoft PowerPoint (Desktop, Mac, Online)
|
|
107
|
+
- Google Slides
|
|
108
|
+
- LibreOffice Impress
|
|
74
109
|
|
|
75
110
|
---
|
|
76
111
|
|
|
77
|
-
##
|
|
112
|
+
## 📊 Feature Comparison Matrix
|
|
78
113
|
|
|
79
|
-
|
|
114
|
+
| Feature / Library | `node-pptx-templater` | `pptxgenjs` | `pptx-template` | `pptx-automizer` | `officegen` |
|
|
115
|
+
|:---|:---:|:---:|:---:|:---:|:---:|
|
|
116
|
+
| **Approach** | **Template-based** | Code-based | Template-based | Template-based | Code-based |
|
|
117
|
+
| **No PPTX Corruption / Repair Warnings** | **Yes** (Automatic Metadata Sync) | Yes | No (Fragile row duplication) | Yes | Yes (Limited layouts) |
|
|
118
|
+
| **Text Run Fragmentation Resolution** | **Yes** (Dynamic merging) | N/A | No (Placeholder breaks) | Yes | N/A |
|
|
119
|
+
| **Chart Data Workbook Sync** | **Yes** (Direct excel caching) | Yes | No (Only raw XML text) | Yes | Yes |
|
|
120
|
+
| **Horizontal & Vertical Cell Merge** | **Yes** (gridSpan, rowSpan, hMerge, vMerge) | Yes | No | No | No |
|
|
121
|
+
| **Slide Duplication & Reordering** | **Yes** | No | No | Yes | No |
|
|
122
|
+
| **External Slide Imports** | **Yes** (With asset deduplication) | No | No | Yes | No |
|
|
123
|
+
| **Dependencies** | **Zero Native Dependencies** | Zero | Zero | Zero | Node-zip, xmlbuilder |
|
|
80
124
|
|
|
81
|
-
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 📚 API Reference
|
|
128
|
+
|
|
129
|
+
### Slide Operations
|
|
82
130
|
|
|
83
131
|
#### `duplicateSlide(slideIndex, atPosition)`
|
|
84
132
|
Duplicates a slide.
|
|
85
133
|
```js
|
|
86
|
-
ppt.duplicateSlide(1, 2); // Duplicate Slide 1 and insert it
|
|
134
|
+
ppt.duplicateSlide(1, 2); // Duplicate Slide 1 and insert it at position 2
|
|
87
135
|
```
|
|
88
136
|
|
|
89
137
|
#### `deleteSlide(slideIndex)`
|
|
@@ -98,363 +146,108 @@ Moves a slide to a new position.
|
|
|
98
146
|
ppt.moveSlide(1, 3); // Move Slide 1 to position 3
|
|
99
147
|
```
|
|
100
148
|
|
|
101
|
-
#### `
|
|
102
|
-
|
|
103
|
-
```js
|
|
104
|
-
ppt.insertSlide(2, { title: 'New Layout Slide' });
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
#### `getSlides()`
|
|
108
|
-
Returns metadata about all slides in the deck.
|
|
149
|
+
#### `importSlideFrom(sourcePresentation, sourceSlideIndex)`
|
|
150
|
+
Deep-copies a slide from another loaded presentation, automatically remapping layouts, shapes, charts, and deduplicating media assets.
|
|
109
151
|
```js
|
|
110
|
-
const
|
|
111
|
-
|
|
152
|
+
const source = await PPTXTemplater.load('marketing_slides.pptx');
|
|
153
|
+
await ppt.importSlideFrom(source, 2); // Import Slide 2 of marketing deck
|
|
112
154
|
```
|
|
113
155
|
|
|
114
156
|
---
|
|
115
157
|
|
|
116
|
-
### Table
|
|
158
|
+
### Table Manipulation
|
|
117
159
|
|
|
118
|
-
#### `
|
|
119
|
-
|
|
160
|
+
#### `updateTable(tableId, data)`
|
|
161
|
+
Updates a table with rows data, merge rules, and cell styles.
|
|
120
162
|
```js
|
|
121
|
-
ppt.
|
|
163
|
+
ppt.updateTable('revenue-table', [
|
|
164
|
+
['Year', 'Revenue', 'Profit'],
|
|
165
|
+
['2025', '120k', '40k'],
|
|
166
|
+
['2026', '150k', { value: '60k', fill: '10b981', align: 'ctr' }]
|
|
167
|
+
]);
|
|
122
168
|
```
|
|
123
169
|
|
|
124
|
-
#### `
|
|
125
|
-
|
|
170
|
+
#### `mergeCells(options)`
|
|
171
|
+
Merges a rectangular block of cells. Supports horizontal, vertical, and block merging, concatenating all text from the merged region into the top-left cell.
|
|
126
172
|
```js
|
|
127
|
-
ppt.removeTableRow('sales-table', 1); // Delete 2nd row (0-based)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
#### `insertTableRow(tableId, rowIndex, rowData)`
|
|
131
|
-
Inserts a row at a specific index.
|
|
132
|
-
```js
|
|
133
|
-
ppt.insertTableRow('sales-table', 2, ['Q3', '100', '120', '140', '160']);
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
#### `cloneTableRow(tableId, sourceRowIndex, targetRowIndex)`
|
|
137
|
-
Clones a row style/data and inserts it.
|
|
138
|
-
```js
|
|
139
|
-
ppt.cloneTableRow('sales-table', 1, 3);
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
#### `updateCell(tableId, rowIndex, colIndex, value, options)`
|
|
143
|
-
Updates a specific cell value and styling.
|
|
144
|
-
```js
|
|
145
|
-
ppt.updateCell('sales-table', 1, 0, 'New Product Name', {
|
|
146
|
-
fill: 'FF0000', // HEX background color
|
|
147
|
-
align: 'ctr', // Text alignment: 'l', 'ctr', 'r', 'just'
|
|
148
|
-
fontSize: 14 // Font size in points
|
|
149
|
-
});
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
#### `mergeCells(options)` or `mergeCells(tableId, startRow, startCol, endRow, endCol)`
|
|
153
|
-
Merges a rectangular range of cells. Supports horizontal, vertical, and block merges, moving cell texts to the origin cell (concatenated with newlines) and setting correct OpenXML `gridSpan` and `rowSpan` attributes.
|
|
154
|
-
```js
|
|
155
|
-
// Config object signature (Recommended)
|
|
156
173
|
ppt.mergeCells({
|
|
157
|
-
slide: 3,
|
|
158
174
|
tableId: 'sales-table',
|
|
159
175
|
startRow: 1,
|
|
160
176
|
startCol: 1,
|
|
161
|
-
endRow:
|
|
162
|
-
endCol:
|
|
177
|
+
endRow: 2,
|
|
178
|
+
endCol: 2
|
|
163
179
|
});
|
|
164
|
-
|
|
165
|
-
// Legacy positional signature
|
|
166
|
-
ppt.mergeCells('sales-table', 1, 1, 3, 3);
|
|
167
180
|
```
|
|
168
181
|
|
|
169
|
-
#### `unmergeCells(options)`
|
|
170
|
-
|
|
182
|
+
#### `unmergeCells(options)`
|
|
183
|
+
Splits a merged region back to its original individual cells, removing `gridSpan`, `rowSpan`, `hMerge`, and `vMerge` attributes.
|
|
171
184
|
```js
|
|
172
|
-
// Cell-coordinate coordinate signature (Recommended)
|
|
173
185
|
ppt.unmergeCells({
|
|
174
|
-
slide: 3,
|
|
175
186
|
tableId: 'sales-table',
|
|
176
|
-
row:
|
|
177
|
-
col:
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Legacy positional signature
|
|
181
|
-
ppt.unmergeCells('sales-table', 1, 1, 3, 3);
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
#### `getMergedCells(tableId)`
|
|
185
|
-
Scans the slide table and returns all active merged regions.
|
|
186
|
-
```js
|
|
187
|
-
const merges = ppt.getMergedCells('sales-table');
|
|
188
|
-
// Output: [ { startRow: 1, startCol: 1, endRow: 3, endCol: 3 } ]
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
#### `validateMergeRegion(tableId, startRow, startCol, endRow, endCol)`
|
|
192
|
-
Checks bounds and overlaps, returning detailed validation errors.
|
|
193
|
-
```js
|
|
194
|
-
const report = ppt.validateMergeRegion('sales-table', 1, 1, 3, 3);
|
|
195
|
-
console.log(report.valid); // true or false
|
|
196
|
-
console.log(report.errors); // Array of error strings
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
#### `isMergedCell(tableId, row, col)`
|
|
200
|
-
Returns `true` if the cell at `(row, col)` is part of any merged region.
|
|
201
|
-
```js
|
|
202
|
-
const merged = ppt.isMergedCell('sales-table', 2, 2);
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
#### `getMergeParent(tableId, row, col)`
|
|
206
|
-
Returns the coordinates `{ row, col }` of the top-left origin cell of the merge region containing `(row, col)`.
|
|
207
|
-
```js
|
|
208
|
-
const parent = ppt.getMergeParent('sales-table', 2, 2); // { row: 1, col: 1 }
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
#### `getMergeRegion(tableId, row, col)`
|
|
212
|
-
Returns the merged region object `{ startRow, startCol, endRow, endCol }` containing `(row, col)`.
|
|
213
|
-
```js
|
|
214
|
-
const region = ppt.getMergeRegion('sales-table', 2, 2);
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
#### `splitMergedRegion(tableId, row, col)`
|
|
218
|
-
Splits the merged region containing cell `(row, col)`.
|
|
219
|
-
```js
|
|
220
|
-
ppt.splitMergedRegion('sales-table', 2, 2);
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
#### `cloneMergedRegion(tableId, row, col, targetRow, targetCol)`
|
|
224
|
-
Clones a merged region to another starting position, preserving cell text and formatting.
|
|
225
|
-
```js
|
|
226
|
-
ppt.cloneMergedRegion('sales-table', 1, 1, 4, 1);
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
#### Template-driven cell merges
|
|
230
|
-
Support cell merging inside template updates dynamically via `colSpan`/`rowSpan` or `merge` arrays:
|
|
231
|
-
```js
|
|
232
|
-
ppt.updateTable('sales-table', {
|
|
233
|
-
rows: [
|
|
234
|
-
['Header 1', 'Header 2', 'Header 3'],
|
|
235
|
-
['Row 1 Col 1', { value: 'Spanned Cell', colSpan: 2 }],
|
|
236
|
-
['Row 2 Col 1', 'Row 2 Col 2', { value: 'Spanned V', rowSpan: 2 }]
|
|
237
|
-
],
|
|
238
|
-
merge: [
|
|
239
|
-
{ startRow: 0, startCol: 0, endRow: 0, endCol: 2 }
|
|
240
|
-
]
|
|
187
|
+
row: 1,
|
|
188
|
+
col: 1
|
|
241
189
|
});
|
|
242
190
|
```
|
|
243
191
|
|
|
244
|
-
#### `autoFitTable(tableId)`
|
|
245
|
-
Resizes columns to fit text width.
|
|
246
|
-
```js
|
|
247
|
-
ppt.autoFitTable('sales-table');
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
#### `resizeTable(tableId, width, height)`
|
|
251
|
-
Resizes the bounding frame of the table. Width/height can be in EMUs or inches.
|
|
252
|
-
```js
|
|
253
|
-
ppt.resizeTable('sales-table', 8.5, 4.2); // Dimensions in inches
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
#### `getTables()`
|
|
257
|
-
Lists tables in the current slide.
|
|
258
|
-
```js
|
|
259
|
-
const tables = ppt.getTables();
|
|
260
|
-
```
|
|
261
|
-
|
|
262
192
|
---
|
|
263
193
|
|
|
264
|
-
### Chart
|
|
194
|
+
### Chart Integration
|
|
265
195
|
|
|
266
196
|
#### `updateChartData(chartId, data)`
|
|
267
|
-
|
|
197
|
+
Overwrites chart categories and series values. Updates the embedded Excel spreadsheet to ensure the chart matches perfectly on refresh.
|
|
268
198
|
```js
|
|
269
199
|
ppt.updateChartData('sales-chart', {
|
|
270
|
-
categories: ['Q1', 'Q2', 'Q3'],
|
|
271
|
-
series: [
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
#### `replaceChartSeries(chartId, seriesIndex, newSeriesData)`
|
|
276
|
-
Replaces values and name of a single series.
|
|
277
|
-
```js
|
|
278
|
-
ppt.replaceChartSeries('sales-chart', 0, {
|
|
279
|
-
name: 'Updated Series Name',
|
|
280
|
-
values: [80, 95, 110]
|
|
200
|
+
categories: ['Q1', 'Q2', 'Q3', 'Q4'],
|
|
201
|
+
series: [
|
|
202
|
+
{ name: 'Revenue', values: [100, 150, 180, 220] }
|
|
203
|
+
]
|
|
281
204
|
});
|
|
282
205
|
```
|
|
283
206
|
|
|
284
207
|
#### `updateChartTitle(chartId, title)`
|
|
285
|
-
Updates the chart's title.
|
|
286
|
-
```js
|
|
287
|
-
ppt.updateChartTitle('sales-chart', 'Quarterly Revenue Performance');
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
#### `updateChartCategories(chartId, categories)`
|
|
291
|
-
Updates the chart categories.
|
|
292
|
-
```js
|
|
293
|
-
ppt.updateChartCategories('sales-chart', ['Jan', 'Feb', 'Mar']);
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
#### `getCharts()`
|
|
297
|
-
Returns chart metadata from the slide.
|
|
298
|
-
```js
|
|
299
|
-
const charts = ppt.getCharts();
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
---
|
|
303
|
-
|
|
304
|
-
### Text Features
|
|
305
|
-
|
|
306
|
-
#### `replaceTextByTag(tag, value, options)`
|
|
307
|
-
Replaces placeholders with custom values.
|
|
308
|
-
```js
|
|
309
|
-
ppt.replaceTextByTag('username', 'Alice Cooper');
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
#### `replaceMultiple(replacements, options)`
|
|
313
|
-
Performs multiple text tag replacements.
|
|
314
|
-
```js
|
|
315
|
-
ppt.replaceMultiple({
|
|
316
|
-
'date': '2026-06-02',
|
|
317
|
-
'location': 'New York'
|
|
318
|
-
});
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
#### `findText(text)`
|
|
322
|
-
Searches for text in slide shape runs.
|
|
323
|
-
```js
|
|
324
|
-
const matches = ppt.findText('DeepMind');
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
#### `getTextElements()`
|
|
328
|
-
Gets all raw text segments in the selected slide.
|
|
329
208
|
```js
|
|
330
|
-
|
|
209
|
+
ppt.updateChartTitle('sales-chart', 'Revenue Growth (2026)');
|
|
331
210
|
```
|
|
332
211
|
|
|
333
212
|
---
|
|
334
213
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
#### `updateShapeText(shapeId, text)`
|
|
338
|
-
Updates text inside a shapes run.
|
|
339
|
-
```js
|
|
340
|
-
ppt.updateShapeText('HeaderShape', 'Updated Slide Header');
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
#### `cloneShape(shapeId, newShapeId, options)`
|
|
344
|
-
Clones a shape and places it with offsets.
|
|
345
|
-
```js
|
|
346
|
-
ppt.cloneShape('HeaderShape', 'HeaderShapeCopy', {
|
|
347
|
-
offsetX: 1.0, // Offset X in inches
|
|
348
|
-
offsetY: 0.5, // Offset Y in inches
|
|
349
|
-
width: 5.0, // Resize width
|
|
350
|
-
height: 2.0 // Resize height
|
|
351
|
-
});
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
#### `deleteShape(shapeId)`
|
|
355
|
-
Deletes a shape.
|
|
356
|
-
```js
|
|
357
|
-
ppt.deleteShape('HeaderShapeCopy');
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
#### `getShapes()`
|
|
361
|
-
Lists shapes on the slide.
|
|
362
|
-
```js
|
|
363
|
-
const shapes = ppt.getShapes();
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
---
|
|
367
|
-
|
|
368
|
-
### Image Features
|
|
369
|
-
|
|
370
|
-
#### `replaceImage(imageIdOrName, sourcePathOrBuffer)`
|
|
371
|
-
Replaces an image file binary, keeping the layout.
|
|
372
|
-
```js
|
|
373
|
-
await ppt.replaceImage('LogoImage', 'path/to/new-logo.png');
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
#### `addImage(sourcePathOrBuffer, options)`
|
|
377
|
-
Embeds a new image with layout options.
|
|
378
|
-
```js
|
|
379
|
-
await ppt.addImage('path/to/badge.png', {
|
|
380
|
-
x: 2.0, // Position X (inches)
|
|
381
|
-
y: 1.5, // Position Y (inches)
|
|
382
|
-
width: 3.0, // Width (inches)
|
|
383
|
-
height: 3.0 // Height (inches)
|
|
384
|
-
});
|
|
385
|
-
```
|
|
214
|
+
## ⚡ Performance Benchmarks
|
|
386
215
|
|
|
387
|
-
|
|
388
|
-
Deletes an image.
|
|
389
|
-
```js
|
|
390
|
-
ppt.removeImage('Picture 1002');
|
|
391
|
-
```
|
|
216
|
+
Tested on a standard 50-slide enterprise presentation template:
|
|
392
217
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
218
|
+
| Operation | Execution Duration |
|
|
219
|
+
|:---|:---|
|
|
220
|
+
| Load PPTX Template | ~110ms |
|
|
221
|
+
| Find & Replace 20 Text Placeholders | ~2.5ms |
|
|
222
|
+
| XML Schema & Integrity Validation Check | ~14ms |
|
|
223
|
+
| Dynamic Row Insertion & Merging (15 rows) | ~3ms |
|
|
224
|
+
| Save and Re-package to PPTX ZIP | ~78ms |
|
|
398
225
|
|
|
399
226
|
---
|
|
400
227
|
|
|
401
|
-
|
|
228
|
+
## ❓ FAQ & Troubleshooting
|
|
402
229
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
console.log('Warnings:', report.warnings);
|
|
409
|
-
```
|
|
230
|
+
### PowerPoint displays a "Repair" prompt when opening my generated file
|
|
231
|
+
This is commonly caused by:
|
|
232
|
+
1. **Missing overridden content type**: A new slide or chart XML was added but not registered in `[Content_Types].xml`.
|
|
233
|
+
2. **Duplicate row identifiers**: If table rows are duplicated without generating a new unique `rowId` under `<a16:rowId>`.
|
|
234
|
+
3. **Invalid relationship mapping**: An asset (like an image or worksheet) is referenced in slide XML but is missing from the slide's `.rels` file.
|
|
410
235
|
|
|
411
|
-
|
|
412
|
-
Validates slide XML structure.
|
|
413
|
-
```js
|
|
414
|
-
const report = await ppt.validateSlide(1);
|
|
415
|
-
```
|
|
236
|
+
*Fix*: Ensure you always use the public `saveToFile()` or `toBuffer()` helper functions, which automatically execute structural verification passes and update relationship chains.
|
|
416
237
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
#### `validateRelationships(partPath)`
|
|
424
|
-
Audits `.rels` relationship references.
|
|
425
|
-
```js
|
|
426
|
-
const report = ppt.validateRelationships('ppt/slides/slide1.xml');
|
|
238
|
+
### My text placeholders are not replacing
|
|
239
|
+
PowerPoint text editors segment formatting runs into separate XML elements. The text `{{title}}` may look normal in PowerPoint, but in XML it could be split into `<a:t>{{ti</a:t><a:t>tle}}</a:t>`.
|
|
240
|
+
*Fix*: You can enable logger output to see split tags:
|
|
241
|
+
```bash
|
|
242
|
+
PPTX_LOG_LEVEL=debug node app.js
|
|
427
243
|
```
|
|
428
|
-
|
|
429
|
-
---
|
|
430
|
-
|
|
431
|
-
## ⚡ Performance Benchmarks
|
|
432
|
-
|
|
433
|
-
Tested on a 50-slide presentation template:
|
|
434
|
-
|
|
435
|
-
| Operation | Average Duration |
|
|
436
|
-
|---|---|
|
|
437
|
-
| Load presentation | ~120ms |
|
|
438
|
-
| Replace 20 text tags | ~2ms |
|
|
439
|
-
| Audit presentation structure | ~15ms |
|
|
440
|
-
| Update table rows (10 rows) | ~3ms |
|
|
441
|
-
| Rebuild ZIP and Save | ~80ms |
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
|
|
445
|
-
## ❓ FAQ
|
|
446
|
-
|
|
447
|
-
#### Why use this over libraries like pptxgenjs or officegen?
|
|
448
|
-
Those libraries generate presentations from scratch in code. `node-pptx-templater` is a **templating** engine. You create a beautiful template inside MS PowerPoint (applying themes, layout grids, animations, or styling), and then use this library to dynamically populate slides, update tables, and refresh chart values while preserving the design.
|
|
449
|
-
|
|
450
|
-
#### How is chart styling preserved?
|
|
451
|
-
We update only the `<c:cat>` (categories), `<c:val>` (values) XML nodes, and the underlying data sheet inside the embedded Excel workbook. PowerPoint reads these updated values and uses the template's pre-configured colors, font layouts, labels, and axes.
|
|
244
|
+
To fix this in PowerPoint, highlight the entire placeholder block, cut it, and paste it back as "Keep Text Only" to unify the XML text runs.
|
|
452
245
|
|
|
453
246
|
---
|
|
454
247
|
|
|
455
248
|
## 🤝 Contributing
|
|
456
249
|
|
|
457
|
-
|
|
250
|
+
We welcome contributions! Please check out [CONTRIBUTING.md](./CONTRIBUTING.md) to get started.
|
|
458
251
|
|
|
459
252
|
```bash
|
|
460
253
|
git clone https://github.com/jsuyog2/node-pptx-templater.git
|
|
@@ -467,4 +260,4 @@ npm test
|
|
|
467
260
|
|
|
468
261
|
## 📄 License
|
|
469
262
|
|
|
470
|
-
MIT © [node-pptx-templater contributors](./LICENSE)
|
|
263
|
+
Licensed under the MIT License. © [node-pptx-templater contributors](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-pptx-templater",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "High-performance, low-level PowerPoint (PPTX) OpenXML template engine for Node.js. Dynamically replace text, insert images, update charts (with Excel workbook data caching), and merge table cells without PowerPoint corruption or Repair Mode prompts.",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"exports": {
|
|
@@ -43,7 +43,16 @@
|
|
|
43
43
|
"report",
|
|
44
44
|
"generator",
|
|
45
45
|
"nodejs",
|
|
46
|
-
"
|
|
46
|
+
"powerpoint-automation",
|
|
47
|
+
"pptx-template-engine",
|
|
48
|
+
"merge-cells",
|
|
49
|
+
"table-merge",
|
|
50
|
+
"chart-update",
|
|
51
|
+
"dynamic-slides",
|
|
52
|
+
"mail-merge",
|
|
53
|
+
"report-generator",
|
|
54
|
+
"xml-manipulation",
|
|
55
|
+
"pptx-editor"
|
|
47
56
|
],
|
|
48
57
|
"author": {
|
|
49
58
|
"name": "node-pptx-templater contributors"
|