node-pptx-templater 1.0.1 β†’ 1.0.3

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.
Files changed (37) hide show
  1. package/README.md +336 -281
  2. package/package.json +6 -6
  3. package/src/cli/commands/build.js +32 -31
  4. package/src/cli/commands/debug.js +25 -24
  5. package/src/cli/commands/extract.js +23 -21
  6. package/src/cli/commands/inspect.js +25 -23
  7. package/src/cli/commands/validate.js +19 -17
  8. package/src/cli/index.js +45 -43
  9. package/src/core/OutputWriter.js +81 -78
  10. package/src/core/PPTXTemplater.js +859 -274
  11. package/src/core/TemplateEngine.js +69 -71
  12. package/src/core/ValidationEngine.js +246 -0
  13. package/src/index.js +51 -15
  14. package/src/managers/ChartManager.js +197 -70
  15. package/src/managers/ContentTypesManager.js +51 -45
  16. package/src/managers/HyperlinkManager.js +148 -142
  17. package/src/managers/ImageManager.js +336 -0
  18. package/src/managers/MediaManager.js +64 -81
  19. package/src/managers/RelationshipManager.js +102 -96
  20. package/src/managers/ShapeManager.js +340 -0
  21. package/src/managers/SlideManager.js +410 -311
  22. package/src/managers/TableManager.js +981 -262
  23. package/src/managers/TextManager.js +197 -0
  24. package/src/managers/ZipManager.js +71 -69
  25. package/src/managers/charts/ChartCacheGenerator.js +77 -58
  26. package/src/managers/charts/ChartParser.js +11 -13
  27. package/src/managers/charts/ChartRelationshipManager.js +14 -10
  28. package/src/managers/charts/ChartWorkbookUpdater.js +61 -56
  29. package/src/parsers/XMLParser.js +50 -49
  30. package/src/templates/blankPptx.js +3 -1
  31. package/src/templates/slideTemplate.js +31 -32
  32. package/src/utils/contentTypesHelper.js +41 -53
  33. package/src/utils/errors.js +33 -23
  34. package/src/utils/idUtils.js +23 -15
  35. package/src/utils/logger.js +21 -15
  36. package/src/utils/relationshipUtils.js +28 -22
  37. package/src/utils/xmlUtils.js +37 -29
package/README.md CHANGED
@@ -7,25 +7,18 @@
7
7
  [![Coverage](https://img.shields.io/codecov/c/github/jsuyog2/node-pptx-templater)](https://codecov.io/gh/jsuyog2/node-pptx-templater)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)
9
9
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org)
10
- [![ES Modules](https://img.shields.io/badge/ESM-only-blueviolet)](https://nodejs.org/api/esm.html)
11
10
 
12
11
  ---
13
12
 
14
13
  ## ✨ Features
15
14
 
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 |
15
+ - πŸ—οΈ **Zero PPTX Library Dependencies**: Operates entirely via low-level XML/ZIP manipulation.
16
+ - πŸ” **Advanced Text Replacement**: Handles fragmented runs seamlessly (`{{placeholders}}`).
17
+ - πŸ“Š **Comprehensive Chart Support**: Bar, Column, Line, Pie, Doughnut, Area, Scatterβ€”preserves original themes.
18
+ - πŸ“‹ **Safe Table Updates**: Fully manages row insertion, deletion, merging, resizing, and cell formatting while maintaining PowerPoint integrity (prevents repair warnings by generating unique row IDs).
19
+ - 🎨 **Shape & Image Manipulation**: Update text, clone, position, replace, or delete slide components.
20
+ - 🎯 **Slide Operations**: Duplicate, reorder, insert, or delete slide parts on the fly.
21
+ - πŸ” **Deep Integrity Validation**: Live validation of relationships, XML schema consistency, table columns, and duplicates prior to saving.
29
22
 
30
23
  ---
31
24
 
@@ -35,372 +28,434 @@
35
28
  npm install node-pptx-templater
36
29
  ```
37
30
 
38
- **Requirements:** Node.js β‰₯ 18.0.0, ES Modules (`"type": "module"`)
39
-
40
31
  ---
41
32
 
42
33
  ## πŸš€ Quick Start
43
34
 
44
35
  ```js
45
- import { PPTXTemplater } from 'node-pptx-templater';
46
-
47
- // Load a template PPTX
48
- const ppt = await PPTXTemplater.load('template.pptx');
36
+ const { PPTXTemplater } = require('node-pptx-templater');
37
+
38
+ async function run() {
39
+ // Load presentation template
40
+ const ppt = await PPTXTemplater.load('template.pptx');
41
+
42
+ // Use Slide 1 and replace text placeholders
43
+ ppt.useSlide(1)
44
+ .replaceTextByTag('title', 'Annual Sales Report')
45
+ .replaceMultiple({ company: 'Google DeepMind', year: '2026' });
46
+
47
+ // Update chart data on Slide 2
48
+ ppt.useSlide(2)
49
+ .updateChartData('sales-chart', {
50
+ categories: ['Q1', 'Q2', 'Q3', 'Q4'],
51
+ series: [{ name: 'Revenue', values: [100, 150, 180, 220] }]
52
+ });
53
+
54
+ // Save the result (validates structural integrity automatically)
55
+ await ppt.saveToFile('output.pptx');
56
+ }
57
+ run();
58
+ ```
49
59
 
50
- // Select slide(s) to work on (omit to work on all)
51
- ppt.useSlide(1);
60
+ ---
52
61
 
53
- // Replace {{placeholder}} text
54
- ppt.replaceText({
55
- '{{title}}': 'Quarterly Report',
56
- '{{year}}': '2026',
57
- '{{company}}': 'Acme Corp',
58
- });
62
+ ## πŸ—οΈ Architecture & OpenXML Internals
59
63
 
60
- // Update chart data
61
- ppt.updateChart('sales-chart', {
62
- categories: ['Jan', 'Feb', 'Mar'],
63
- series: [{ name: 'Revenue', values: [120, 150, 180] }],
64
- });
64
+ A PPTX file is an OPC (Open Packaging Convention) ZIP containing XML parts:
65
65
 
66
- // Update table rows
67
- ppt.updateTable('employees-table', [
68
- ['Name', 'Role', 'Department'],
69
- ['Alice', 'Engineer', 'Platform'],
70
- ['Bob', 'Designer', 'Product'],
71
- ]);
66
+ - `/ppt/presentation.xml` – The slides inventory (`sldIdLst`) and masters.
67
+ - `/ppt/slides/slideN.xml` – Slide elements (shapes, images, tables).
68
+ - `/ppt/slides/_rels/slideN.xml.rels` – Relationship links (`rId`).
69
+ - `[Content_Types].xml` – Declares MIME types for slide parts, charts, and media.
72
70
 
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
- ```
71
+ ### The rowId Table Corruption Bug
72
+ Microsoft PowerPoint assigns an `<a16:rowId>` identifier to each row to facilitate collaborative editing. Duplicate row IDs trigger the PowerPoint **Repair Mode**, causing slide elements and styles to break.
73
+ `node-pptx-templater` generates unique 32-bit unsigned integers for `<a16:rowId>` whenever a table row is cloned, appended, or inserted, ensuring output documents open flawlessly in MS PowerPoint, Google Slides, and LibreOffice.
79
74
 
80
75
  ---
81
76
 
82
77
  ## πŸ“š API Reference
83
78
 
84
- ### `PPTXTemplater`
79
+ Here is the complete reference of all public APIs.
85
80
 
86
- #### Static Methods
81
+ ### Slide Features
87
82
 
88
- | Method | Description |
89
- |---|---|
90
- | `PPTXTemplater.load(source)` | Load from file path or Buffer |
91
- | `PPTXTemplater.create()` | Create a blank presentation |
83
+ #### `duplicateSlide(slideIndex, atPosition)`
84
+ Duplicates a slide.
85
+ ```js
86
+ ppt.duplicateSlide(1, 2); // Duplicate Slide 1 and insert it as Slide 2
87
+ ```
92
88
 
93
- #### Slide Selection
89
+ #### `deleteSlide(slideIndex)`
90
+ Deletes a slide from the deck.
91
+ ```js
92
+ ppt.deleteSlide(3); // Delete Slide 3
93
+ ```
94
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 |
95
+ #### `moveSlide(fromIndex, toIndex)`
96
+ Moves a slide to a new position.
97
+ ```js
98
+ ppt.moveSlide(1, 3); // Move Slide 1 to position 3
99
+ ```
100
100
 
101
- #### Content Manipulation
101
+ #### `insertSlide(slideIndex, options)`
102
+ Inserts a new blank slide at a specific position.
103
+ ```js
104
+ ppt.insertSlide(2, { title: 'New Layout Slide' });
105
+ ```
102
106
 
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 |
107
+ #### `getSlides()`
108
+ Returns metadata about all slides in the deck.
109
+ ```js
110
+ const slides = ppt.getSlides();
111
+ console.log(slides);
112
+ ```
110
113
 
111
- #### Slide Management
114
+ ---
112
115
 
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 |
116
+ ### Table Features
120
117
 
121
- #### Output
118
+ #### `addTableRow(tableId, rowData)`
119
+ Appends a new row to the table.
120
+ ```js
121
+ ppt.addTableRow('sales-table', ['Q4', '150', '210', '190', '250']);
122
+ ```
122
123
 
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 |
124
+ #### `removeTableRow(tableId, rowIndex)`
125
+ Removes a table row.
126
+ ```js
127
+ ppt.removeTableRow('sales-table', 1); // Delete 2nd row (0-based)
128
+ ```
128
129
 
129
- #### Utilities
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
+ ```
130
135
 
131
- | Method | Description |
132
- |---|---|
133
- | `.getInfo()` | Presentation metadata |
134
- | `.validate()` | Structure validation |
135
- | `.slideCount` | Total slide count (getter) |
136
+ #### `cloneTableRow(tableId, sourceRowIndex, targetRowIndex)`
137
+ Clones a row style/data and inserts it.
138
+ ```js
139
+ ppt.cloneTableRow('sales-table', 1, 3);
140
+ ```
136
141
 
137
- ---
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
+ ```
138
151
 
139
- ## πŸ—οΈ Architecture
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
+ ppt.mergeCells({
157
+ slide: 3,
158
+ tableId: 'sales-table',
159
+ startRow: 1,
160
+ startCol: 1,
161
+ endRow: 3,
162
+ endCol: 3
163
+ });
140
164
 
165
+ // Legacy positional signature
166
+ ppt.mergeCells('sales-table', 1, 1, 3, 3);
141
167
  ```
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
168
 
158
- A `.pptx` file is a ZIP archive (Open Packaging Convention) containing XML files:
169
+ #### `unmergeCells(options)` or `unmergeCells(tableId, startRow, startCol, endRow, endCol)`
170
+ Unmerges cells, restoring original XML cell structures.
171
+ ```js
172
+ // Cell-coordinate coordinate signature (Recommended)
173
+ ppt.unmergeCells({
174
+ slide: 3,
175
+ tableId: 'sales-table',
176
+ row: 2,
177
+ col: 2
178
+ });
159
179
 
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
180
+ // Legacy positional signature
181
+ ppt.unmergeCells('sales-table', 1, 1, 3, 3);
179
182
  ```
180
183
 
181
- ### Relationship Flow
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
+ ```
182
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
183
197
  ```
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)
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);
191
203
  ```
192
204
 
193
- ### Text Fragmentation Problem & Solution
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
+ ```
194
210
 
195
- PowerPoint sometimes splits `{{placeholder}}` across multiple XML text runs:
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
+ ```
196
216
 
197
- ```xml
198
- <!-- What you write in PowerPoint: -->
199
- {{title}}
217
+ #### `splitMergedRegion(tableId, row, col)`
218
+ Splits the merged region containing cell `(row, col)`.
219
+ ```js
220
+ ppt.splitMergedRegion('sales-table', 2, 2);
221
+ ```
200
222
 
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>
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);
204
227
  ```
205
228
 
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
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
+ ]
241
+ });
242
+ ```
211
243
 
212
- ---
244
+ #### `autoFitTable(tableId)`
245
+ Resizes columns to fit text width.
246
+ ```js
247
+ ppt.autoFitTable('sales-table');
248
+ ```
213
249
 
214
- ## πŸ–₯️ CLI Usage
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
+ ```
215
255
 
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
- }
256
+ #### `getTables()`
257
+ Lists tables in the current slide.
258
+ ```js
259
+ const tables = ppt.getTables();
260
260
  ```
261
261
 
262
262
  ---
263
263
 
264
- ## πŸ“Š Supported Chart Types
264
+ ### Chart Features
265
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 |
266
+ #### `updateChartData(chartId, data)`
267
+ Updates a chart's categories, series, values, and embedded Excel workbook.
268
+ ```js
269
+ ppt.updateChartData('sales-chart', {
270
+ categories: ['Q1', 'Q2', 'Q3'],
271
+ series: [{ name: 'Sales', values: [100, 120, 150] }]
272
+ });
273
+ ```
276
274
 
277
- ---
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]
281
+ });
282
+ ```
278
283
 
279
- ## ⚑ Performance
284
+ #### `updateChartTitle(chartId, title)`
285
+ Updates the chart's title.
286
+ ```js
287
+ ppt.updateChartTitle('sales-chart', 'Quarterly Revenue Performance');
288
+ ```
280
289
 
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 |
290
+ #### `updateChartCategories(chartId, categories)`
291
+ Updates the chart categories.
292
+ ```js
293
+ ppt.updateChartCategories('sales-chart', ['Jan', 'Feb', 'Mar']);
294
+ ```
288
295
 
289
- > Run your own: `npm run benchmark`
296
+ #### `getCharts()`
297
+ Returns chart metadata from the slide.
298
+ ```js
299
+ const charts = ppt.getCharts();
300
+ ```
290
301
 
291
302
  ---
292
303
 
293
- ## πŸ› Troubleshooting
294
-
295
- ### Placeholders not being replaced
304
+ ### Text Features
296
305
 
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:
306
+ #### `replaceTextByTag(tag, value, options)`
307
+ Replaces placeholders with custom values.
308
+ ```js
309
+ ppt.replaceTextByTag('username', 'Alice Cooper');
310
+ ```
299
311
 
300
- ```bash
301
- PPTX_LOG_LEVEL=debug node your-script.js
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
+ });
302
319
  ```
303
320
 
304
- Extract the slide XML to inspect:
305
- ```bash
306
- node-pptx-templater extract template.pptx --slide 1 --out slide1.xml
321
+ #### `findText(text)`
322
+ Searches for text in slide shape runs.
323
+ ```js
324
+ const matches = ppt.findText('DeepMind');
307
325
  ```
308
326
 
309
- Look for `<a:t>` elements and check if the placeholder is split.
327
+ #### `getTextElements()`
328
+ Gets all raw text segments in the selected slide.
329
+ ```js
330
+ const elements = ppt.getTextElements();
331
+ ```
310
332
 
311
- ### Chart not updating
333
+ ---
312
334
 
313
- Chart names must match the shape's `cNvPr name` attribute exactly.
314
- Use `--inspect --charts` to see all chart names:
335
+ ### Shape Features
315
336
 
316
- ```bash
317
- node-pptx-templater inspect template.pptx --charts
337
+ #### `updateShapeText(shapeId, text)`
338
+ Updates text inside a shapes run.
339
+ ```js
340
+ ppt.updateShapeText('HeaderShape', 'Updated Slide Header');
318
341
  ```
319
342
 
320
- ### Generated PPTX fails to open
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
+ ```
321
353
 
322
- Run the debug command to check for structural issues:
354
+ #### `deleteShape(shapeId)`
355
+ Deletes a shape.
356
+ ```js
357
+ ppt.deleteShape('HeaderShapeCopy');
358
+ ```
323
359
 
324
- ```bash
325
- node-pptx-templater debug output.pptx
360
+ #### `getShapes()`
361
+ Lists shapes on the slide.
362
+ ```js
363
+ const shapes = ppt.getShapes();
326
364
  ```
327
365
 
328
- Common causes:
329
- - Missing content type entries for new slides
330
- - Broken relationship IDs
331
- - Invalid XML characters in replacement values
366
+ ---
367
+
368
+ ### Image Features
332
369
 
333
- ### File is corrupted
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
+ ```
334
375
 
335
- ```bash
336
- node-pptx-templater debug corrupted.pptx --fix --out repaired.pptx
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
+ ```
386
+
387
+ #### `removeImage(imageIdOrName)`
388
+ Deletes an image.
389
+ ```js
390
+ ppt.removeImage('Picture 1002');
337
391
  ```
338
392
 
339
- The debug command attempts:
340
- - Removing invalid XML control characters
341
- - Fixing unescaped `&` in text content
342
- - Repairing broken relationship IDs
393
+ #### `getImages()`
394
+ Lists images inside the slide.
395
+ ```js
396
+ const images = ppt.getImages();
397
+ ```
343
398
 
344
399
  ---
345
400
 
346
- ## πŸ”Œ Plugin System
401
+ ### Validation System
347
402
 
348
- Extend the engine by subclassing `PPTXTemplater`:
403
+ #### `validatePresentation()`
404
+ Audits the complete presentation.
405
+ ```js
406
+ const report = await ppt.validatePresentation();
407
+ console.log('Errors:', report.errors);
408
+ console.log('Warnings:', report.warnings);
409
+ ```
349
410
 
411
+ #### `validateSlide(slideIndex)`
412
+ Validates slide XML structure.
350
413
  ```js
351
- import { PPTXTemplater } from 'pptx-templater';
414
+ const report = await ppt.validateSlide(1);
415
+ ```
352
416
 
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);
417
+ #### `validateTable(tableId)`
418
+ Audits columns and duplicate rowIds in a table.
419
+ ```js
420
+ const report = await ppt.validateTable('sales-table');
421
+ ```
359
422
 
360
- const textReplacements = {};
361
- for (const [key, val] of Object.entries(data.text || {})) {
362
- textReplacements[`{{${key}}}`] = String(val);
363
- }
423
+ #### `validateRelationships(partPath)`
424
+ Audits `.rels` relationship references.
425
+ ```js
426
+ const report = ppt.validateRelationships('ppt/slides/slide1.xml');
427
+ ```
364
428
 
365
- this.replaceText(textReplacements);
429
+ ---
366
430
 
367
- if (data.chart) {
368
- this.updateChart(data.chart.id, data.chart);
369
- }
431
+ ## ⚑ Performance Benchmarks
370
432
 
371
- return this;
372
- }
373
- }
433
+ Tested on a 50-slide presentation template:
374
434
 
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
- ```
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 |
383
442
 
384
443
  ---
385
444
 
386
- ## πŸ›£οΈ Roadmap
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.
387
449
 
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)
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.
396
452
 
397
453
  ---
398
454
 
399
455
  ## 🀝 Contributing
400
456
 
401
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
457
+ See [CONTRIBUTING.md](./CONTRIBUTING.md).
402
458
 
403
- Quick steps:
404
459
  ```bash
405
460
  git clone https://github.com/jsuyog2/node-pptx-templater.git
406
461
  cd node-pptx-templater
@@ -412,4 +467,4 @@ npm test
412
467
 
413
468
  ## πŸ“„ License
414
469
 
415
- MIT β€” see [LICENSE](./LICENSE)
470
+ MIT Β© [node-pptx-templater contributors](./LICENSE)