node-pptx-templater 1.0.20 → 1.1.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/README.md CHANGED
@@ -1,49 +1,34 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/jsuyog2/node-pptx-templater/main/docs/logo.svg" alt="PPTXForge Logo" width="550" max-width="100%">
2
+ <img src="https://raw.githubusercontent.com/jsuyog2/node-pptx-templater/main/assets/logo.png" alt="node-pptx-templater logo" width="160">
3
3
  </p>
4
4
 
5
- <h1 align="center">node-pptx-templater (PPTXForge Engine)</h1>
5
+ <h1 align="center">node-pptx-templater</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>The High-Performance, Secure OpenXML PowerPoint Template Engine for Node.js</strong>
8
+ <strong>High-performance OpenXML PowerPoint template engine for Node.js</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
12
  <a href="https://www.npmjs.com/package/node-pptx-templater"><img src="https://img.shields.io/npm/v/node-pptx-templater.svg?style=flat-square&color=6366f1" alt="npm version"></a>
13
- <a href="https://github.com/jsuyog2/node-pptx-templater/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/jsuyog2/node-pptx-templater/ci.yml?branch=main&style=flat-square&color=34d399" alt="CI Build Status"></a>
14
- <a href="https://bundlephobia.com/package/node-pptx-templater"><img src="https://img.shields.io/bundlephobia/min/node-pptx-templater?style=flat-square&color=ec4899" alt="Bundle Size"></a>
13
+ <a href="https://github.com/jsuyog2/node-pptx-templater/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/jsuyog2/node-pptx-templater/ci.yml?branch=main&style=flat-square&color=34d399" alt="CI"></a>
15
14
  <a href="https://www.npmjs.com/package/node-pptx-templater"><img src="https://img.shields.io/npm/dm/node-pptx-templater.svg?style=flat-square&color=a855f7" alt="Downloads"></a>
16
- <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" alt="License: MIT"></a>
17
- <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen?style=flat-square" alt="Node.js Version"></a>
15
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" alt="MIT License"></a>
16
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen?style=flat-square" alt="Node.js 18+"></a>
18
17
  </p>
19
18
 
20
- ---
21
-
22
- ## ⚡ Why PPTXForge (`node-pptx-templater`)?
23
-
24
- Traditional slide deck automation requires assembling slides shape-by-shape in verbose code blocks. This is fragile, difficult to maintain, and strips away the power of visual design tools like Microsoft PowerPoint, Keynote, or Google Slides.
25
-
26
- **PPTXForge takes a visual-first approach: Design visually in PowerPoint, populate dynamically in Node.js.**
27
-
28
- You design your layouts, tables, fonts, brand styles, charts, and animations inside PowerPoint, and insert placeholders like `{{customer_name}}`, `{{revenue_chart}}`, or `{{team_table}}`. The PPTXForge engine parses the OpenXML presentation, heals broken text run segmentations, updates Excel data workbook caches, merges drawing cells, and scales slides securely in pure JavaScript with zero external office dependencies.
19
+ <p align="center">
20
+ <a href="https://jsuyog2.github.io/node-pptx-templater/"><strong>📖 Full Documentation →</strong></a>
21
+ </p>
29
22
 
30
23
  ---
31
24
 
32
- ## Enterprise-Grade Core Features
25
+ Design your presentations visually in PowerPoint. Populate them dynamically in Node.js.
33
26
 
34
- * 🚀 **Zero Native/Office/Java Dependencies**: Pure CommonJS JavaScript implementation. Runs efficiently on AWS Lambda, Vercel Edge, Netlify, and Cloudflare Workers.
35
- * 🛡️ **Hardened XML Security**: Fully immune to Billion Laughs (XML bombs), XXE (XML External Entity Injection), and parser crashes due to oversized expansions.
36
- * 🧩 **Text Run Fragmentation Healing**: Solves the split-tag issue. PowerPoint splits `{{placeholders}}` into fragmented `<a:r>` nodes; our parser unifies and replaces them while keeping original formatting intact.
37
- * 📊 **Excel Cache Synchronized Charting**: Supports Bar, Line, Pie, Doughnut, Area, and Scatter charts. Not only updates the visual XML coordinates but also synchronizes data points inside the underlying embedded Excel spreadsheets (`ppt/embeddings/`) to bypass PowerPoint's "Update Data" warnings.
38
- * 🏷️ **Chart Data Labels & Value From Cells**: Configure custom arrays, dynamic category maps, rich templates, positions (insideEnd, bestFit, center), and custom styles (fonts, colors, weight) and serialize them directly to the underlying Excel backing sheet.
39
- * 📋 **DrawingML Table Cell Merging**: Easily configure horizontal spans (`gridSpan`/`hMerge`), vertical spans (`rowSpan`/`vMerge`), and rectangular blocks. Injects unique `rowId` hashes to maintain relationship integrity.
40
- * 🥞 **Z-Order Layer Stacking**: Reorder shapes, images, charts, and tables programmatically. Simulates PowerPoint's "Bring to Front" and "Send to Back" commands directly in the slide's `<p:spTree>`.
41
- * 🎛️ **Slide Management**: Duplicate, delete, reorder slides, or import slides from external decks with automatic media and theme deduplication.
42
- * 🔍 **OPC Package Verification**: Comprehensive check suite for content type overrides, relationship integrity, and XML structure before final output.
27
+ `node-pptx-templater` parses OpenXML directly — no native office dependencies, no Java, no Electron. Pure JavaScript that runs on AWS Lambda, Vercel Edge, and Cloudflare Workers.
43
28
 
44
29
  ---
45
30
 
46
- ## 📦 Installation
31
+ ## Installation
47
32
 
48
33
  ```bash
49
34
  npm install node-pptx-templater
@@ -51,2134 +36,246 @@ npm install node-pptx-templater
51
36
 
52
37
  ---
53
38
 
54
- ## 🚀 Quick Start Guide
55
-
56
- Generate a formatted presentation report from a visually designed template in under 60 seconds:
39
+ ## Quick Start
57
40
 
58
41
  ```javascript
59
42
  const { PPTXTemplater } = require('node-pptx-templater');
60
43
 
61
- async function generateReport() {
62
- // 1. Load the presentation template
63
- const ppt = await PPTXTemplater.load('monthly_report_template.pptx');
64
-
65
- // 2. Select slide 1 and execute standard text replacements
66
- ppt.useSlide(1)
67
- .replaceTextByTag('title', 'Q2 Business Overview')
68
- .replaceMultiple({
69
- client: 'Acme Corporation',
70
- analyst: 'Sarah Jenkins',
71
- date: 'June 2026'
72
- });
73
-
74
- // 3. Update Excel-backed chart data on Slide 2
75
- ppt.useSlide(2)
76
- .updateChartData('revenue-chart', {
77
- categories: ['April', 'May', 'June'],
78
- series: [
79
- { name: 'Target', values: [120000, 150000, 180000] },
80
- { name: 'Actual', values: [125000, 162000, 195000] }
81
- ]
82
- });
83
-
84
- // 3b. Configure custom chart data labels with templates and styling
85
- ppt.useSlide(2)
86
- .updateDataLabels('revenue-chart', {
87
- series: 0,
88
- template: '{category}: {value}',
89
- position: 'insideEnd',
90
- labelStyle: { fontFamily: 'Arial', fontSize: 10, bold: true }
91
- });
92
-
93
- // 4. Update table structure with cell merges and colors on Slide 3
94
- ppt.useSlide(3)
95
- .updateTable('performance-table', [
96
- ['Region', 'Growth Metric', { value: 'Status / Notes', colSpan: 2 }],
97
- ['North Region', '+12.4%', { value: 'Exceeded Target', align: 'ctr', fill: '10b981' }, ''],
98
- ['South Region', '-3.1%', { value: 'Under Review', align: 'ctr', fill: 'f59e0b' }, '']
99
- ]);
100
-
101
- // 5. Duplicate a layout and bring an overlay element to the front
102
- ppt.duplicateSlide(3, 4);
103
- ppt.useSlide(4)
104
- .bringToFront('OverlayLogo')
105
- .replaceTextByTag('title', 'Duplicate Region Report');
106
-
107
- // 6. Save package without PowerPoint Repair Warnings
108
- await ppt.saveToFile('./output/q2_business_report.pptx');
109
- console.log('Report generated successfully!');
110
- }
111
-
112
- generateReport().catch(console.error);
113
- ```
114
-
115
- ## 📂 PowerPoint XML Folder Templates
116
-
117
- Instead of loading and saving standard compiled `.pptx` ZIP files, the library natively supports working with uncompressed PowerPoint OpenXML directories. This is extremely useful for server environments and development setups, bypassing ZIP compression/decompression overhead and resolving relationships relative to the unzipped structure.
118
-
119
- ### 1. Load from XML Folder Template
120
-
121
- You can load a template directly from a folder directory or `presentation.xml` entry point:
122
-
123
- ```javascript
124
- const { PPTXTemplater, PPTXTemplate } = require('node-pptx-templater');
125
-
126
- // Load using the directory root path (auto-detects ppt/presentation.xml)
127
- const ppt = await PPTXTemplater.load('./monthly-template-folder');
44
+ const ppt = await PPTXTemplater.load('template.pptx');
128
45
 
129
- // Load using fromPresentationXml with a configuration object
130
- const ppt2 = await PPTXTemplate.fromPresentationXml({
131
- presentation: './ppt/presentation.xml',
132
- root: './template'
133
- });
134
- ```
135
-
136
- ### 2. Save/Export directly to XML Folder
137
-
138
- You can export the modified presentation back to an uncompressed folder structure on disk:
139
-
140
- ```javascript
141
- await ppt.saveToFolder('./output-template-folder');
142
- ```
143
-
144
- This generates:
145
- ```text
146
- output-template-folder/
147
- ├── [Content_Types].xml
148
- ├── _rels/
149
- ├── ppt/
150
- │ ├── presentation.xml
151
- │ ├── _rels/
152
- │ ├── slides/
153
- │ ├── slideLayouts/
154
- │ ├── slideMasters/
155
- │ └── theme/
156
- └── docProps/
157
- ```
158
-
159
- ### 3. Folder Mode Performance Benefits
46
+ // Replace text placeholders
47
+ ppt.useSlide(1)
48
+ .replaceMultiple({ '{{title}}': 'Q2 Report', '{{date}}': 'June 2026' });
160
49
 
161
- Our benchmark results compare standard ZIP-based templates with uncompressed XML folder workflows:
162
- * **Concurrency Throughput**: Up to **1.4x faster** under parallel request stress due to eliminated ZIP compression CPU locks.
163
- * **Heap Memory Footprint**: Reduces memory overhead by avoiding full in-memory ZIP archives.
50
+ // Update chart data (syncs the embedded Excel workbook too)
51
+ ppt.useSlide(2)
52
+ .updateChart('revenue-chart', {
53
+ categories: ['April', 'May', 'June'],
54
+ series: [{ name: 'Revenue', values: [125000, 162000, 195000] }]
55
+ });
164
56
 
165
- ### 4. Validation
57
+ // Update table
58
+ ppt.useSlide(3)
59
+ .updateTable('SalesTable', {
60
+ rows: [['North', '$1.2M', '↑15%'], ['South', '$1.8M', '↑22%']]
61
+ });
166
62
 
167
- Ensure XML directory templates are correct and contain no orphan relations:
168
-
169
- ```javascript
170
- const report = await ppt.validatePresentationXml();
171
- if (!report.valid) {
172
- console.error('Errors found:', report.errors);
173
- }
63
+ await ppt.saveToFile('output.pptx');
174
64
  ```
175
65
 
176
66
  ---
177
67
 
178
- ## 📋 OpenXML Presentation Architecture
179
-
180
- A `.pptx` file is an Open Packaging Convention (OPC) ZIP package containing structured XML schemas:
181
-
182
- ```text
183
- PPTX Archive Structure:
184
- ├── [Content_Types].xml # MIME type registry for all zip contents
185
- ├── _rels/
186
- │ └── .rels # Package-level relationship mappings
187
- └── ppt/
188
- ├── presentation.xml # Global presentation slide order and layouts
189
- ├── slides/
190
- │ ├── slide1.xml # DrawingML elements, text runs, and shapes
191
- │ └── _rels/
192
- │ └── slide1.xml.rels # Resource mappings (images, charts, tables)
193
- ├── media/ # Image and SVG database
194
- └── embeddings/ # Embedded Excel books backing charts
195
- ```
196
-
197
- ### Unifying Text Run Segmentations
198
- Under the hood, slide template text is represented as runs of text (`<a:r>`). PowerPoint's parser often breaks a single tag `{{name}}` into separate segments:
199
- ```xml
200
- <!-- Split layout generated by PowerPoint -->
201
- <a:r><a:t>{{n</a:t></a:r>
202
- <a:r><a:t>ame}}</a:t></a:r>
203
- ```
204
- PPTXForge parses the XML structure, identifies layout boundaries, merges text nodes back into a single element, and applies replacements while preserving font sizes, colors, and styling rules.
205
-
206
- ### Preventing PPTX "Repair Presentation" Warnings
207
- Naively duplicating rows in slide tables can leave duplicate `rowId` values or break cell mappings, causing PowerPoint to crash or prompt a repair screen. PPTXForge automatically re-allocates unique `rowId` hashes and formats `gridSpan` / `rowSpan` coordinates in compliance with standard Office OpenXML (OOXML) regulations.
68
+ ## Features
69
+
70
+ | Category | Capabilities |
71
+ |---|---|
72
+ | **Text** | Replace placeholders, search & replace, rich text, bullet lists |
73
+ | **Tables** | Update cells, add/insert/remove rows, merge cells, extract data as JSON |
74
+ | **Charts** | Update data, sync Excel workbook, custom data labels, series management |
75
+ | **Images** | Replace, add, remove images; set position, size, rotation, crop |
76
+ | **Shapes** | Add, update, clone, remove shapes; set fill, text, position |
77
+ | **Cell Shapes** | Add shapes inside table cells with automatic bounds calculation |
78
+ | **Slides** | Duplicate, move, delete, import/export, clone, reorder |
79
+ | **Layer Ordering** | Bring to front, send to back, set Z-index, swap, sort |
80
+ | **Hyperlinks** | URL links, slide navigation, shape and image links |
81
+ | **XML Folder** | Extract to folder, edit XML directly, rebuild to PPTX |
82
+ | **Validation** | Structural validation, relationship checks, auto-repair |
83
+ | **Performance** | Template caching, async streaming, profiling |
208
84
 
209
85
  ---
210
86
 
211
- ## 📊 Feature Comparison Matrix
212
-
213
- Compare PPTXForge with other popular PowerPoint automation libraries:
214
-
215
- | Feature / Metric | **node-pptx-templater** (PPTXForge) | **pptxgenjs** | **pptx-template** | **pptx-automizer** | **officegen** |
216
- | :--- | :---: | :---: | :---: | :---: | :---: |
217
- | **Automation Flow** | **Template-based** | Code-based | Template-based | Template-based | Code-based |
218
- | **No Office/Java Dependency** | **Yes** (Pure JS) | Yes | Yes | Yes | Yes |
219
- | **Bypass Corruption/Repair Alerts** | **Yes** (RowId sync) | Yes | No (Row duplicate breaks) | Yes | Yes |
220
- | **Heal Text Run Fragmentation** | **Yes** | N/A | No (Tags break) | Yes | N/A |
221
- | **Synchronized Excel Chart Updates** | **Yes** (Direct sync) | Yes | No (Text updates only) | Yes | Yes |
222
- | **Horizontal & Vertical Cell Merge** | **Yes** (gridSpan/rowSpan) | Yes | No | No | No |
223
- | **Z-Order Layer Reordering** | **Yes** (Front/Back) | No | No | Yes | No |
224
- | **External Slide Imports** | **Yes** (Deduplicated) | No | No | Yes | No |
225
-
226
- <!-- API_REFERENCE_START -->
227
-
228
- ### Tables API
229
-
230
- #### `updateTable(tableId, rows)`
231
- Replaces table rows with new data in the selected slide(s). Preserves borders, merged cells, fonts, colors, and alignment from the template.
232
-
233
- * **Arguments**:
234
- * `tableId` (`string`): Table name or shape ID.
235
- * `rows` (`string[][]`): 2D array of cell values (row × col).
236
- * **Returns**: `PPTXTemplater` - this (chainable)
237
-
238
- ```javascript
239
- ppt.useSlide(1).updateTable('summary-table', [
240
- ['Item', 'Value'],
241
- ['Widgets', '1,200'],
242
- ['Gadgets', '850']
243
- ]);
244
- ```
245
-
246
- #### `addCellShape(tableId, rowIndex, colIndex, options)`
247
- Dynamically adds a shape inside a table cell based on cell coordinates.
248
-
249
- * **Arguments**:
250
- * `tableId` (`string`): Table name or shape ID.
251
- * `rowIndex` (`number`): 0-based row index.
252
- * `colIndex` (`number`): 0-based column index.
253
- * `options` (`Object`): Shape configuration options.
254
- * **Returns**: `this` - The chainable presentation templater instance.
255
-
256
- ```javascript
257
- await ppt.addCellShape('Table', 1, 2, { type: 'circle', fill: '#10B981' });
258
- ```
259
-
260
- #### `updateCellShape(tableId, rowIndex, colIndex, shapeIndex, options)`
261
- Updates an existing shape inside a table cell.
262
-
263
- * **Arguments**:
264
- * `tableId` (`string`): Table name or shape ID.
265
- * `rowIndex` (`number`): 0-based row index.
266
- * `colIndex` (`number`): 0-based column index.
267
- * `shapeIndex` (`number`): 0-based shape index in the cell.
268
- * `options` (`Object`): Shape configuration properties to update.
269
- * **Returns**: `this` - The chainable presentation templater instance.
270
-
271
- ```javascript
272
- await ppt.updateCellShape('Table', 1, 2, 0, { fill: '#EF4444' });
273
- ```
274
-
275
- #### `removeCellShape(tableId, rowIndex, colIndex, shapeIndex)`
276
- Removes a shape from a table cell.
277
-
278
- * **Arguments**:
279
- * `tableId` (`string`): Table name or shape ID.
280
- * `rowIndex` (`number`): 0-based row index.
281
- * `colIndex` (`number`): 0-based column index.
282
- * `shapeIndex` (`number`): 0-based shape index in the cell.
283
- * **Returns**: `this` - The chainable presentation templater instance.
284
-
285
- ```javascript
286
- await ppt.removeCellShape('Table', 1, 2, 0);
287
- ```
288
-
289
- #### `getCellShape(tableId, rowIndex, colIndex, shapeIndex)`
290
- Discovers and retrieves details of an existing cell shape on the targeted slide.
291
-
292
- * **Arguments**:
293
- * `tableId` (`string`): Table name or shape ID.
294
- * `rowIndex` (`number`): 0-based row index.
295
- * `colIndex` (`number`): 0-based column index.
296
- * `shapeIndex` (`number`): 0-based shape index in the cell.
297
- * **Returns**: `Object|null` - Shape details object, or null if not found.
298
-
299
- ```javascript
300
- const shape = ppt.getCellShape('Table', 1, 2, 0);
301
- ```
302
-
303
- #### `addTableRow(())`
304
- Delegates core actions to slide element sub-managers.
305
-
306
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
307
-
308
- ```javascript
309
- ppt.useSlide(1).addTableRow('data-table', ['John Doe', 'Sales Manager', '$120k']);
310
- ```
311
-
312
- #### `removeTableRow(())`
313
- Delegates core actions to slide element sub-managers.
314
-
315
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
316
-
317
- ```javascript
318
- ppt.useSlide(1).removeTableRow('data-table', 2);
319
- ```
320
-
321
- #### `insertTableRow(())`
322
- Delegates core actions to slide element sub-managers.
323
-
324
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
325
-
326
- ```javascript
327
- ppt.useSlide(1).insertTableRow(());
328
- ```
329
-
330
- #### `cloneTableRow(())`
331
- Delegates core actions to slide element sub-managers.
332
-
333
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
334
-
335
- ```javascript
336
- ppt.useSlide(1).cloneTableRow(());
337
- ```
338
-
339
- #### `updateCell(())`
340
- Delegates core actions to slide element sub-managers.
341
-
342
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
343
-
344
- ```javascript
345
- ppt.useSlide(1).updateCell(());
346
- ```
347
-
348
- #### `mergeCells(())`
349
- Delegates core actions to slide element sub-managers.
350
-
351
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
352
-
353
- ```javascript
354
- ppt.useSlide(1).mergeCells('metrics-table', 1, 1, 2, 2);
355
- ```
356
-
357
- #### `unmergeCells(())`
358
- Delegates core actions to slide element sub-managers.
359
-
360
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
361
-
362
- ```javascript
363
- ppt.useSlide(1).unmergeCells('metrics-table', 1, 1, 2, 2);
364
- ```
365
-
366
- #### `getMergedCells(())`
367
- Delegates core actions to slide element sub-managers.
368
-
369
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
370
-
371
- ```javascript
372
- ppt.useSlide(1).getMergedCells(());
373
- ```
374
-
375
- #### `validateMergeRegion(())`
376
- Delegates core actions to slide element sub-managers.
377
-
378
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
379
-
380
- ```javascript
381
- ppt.useSlide(1).validateMergeRegion(());
382
- ```
383
-
384
- #### `isMergedCell(())`
385
- Delegates core actions to slide element sub-managers.
386
-
387
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
388
-
389
- ```javascript
390
- ppt.useSlide(1).isMergedCell(());
391
- ```
392
-
393
- #### `getMergeParent(())`
394
- Delegates core actions to slide element sub-managers.
395
-
396
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
397
-
398
- ```javascript
399
- ppt.useSlide(1).getMergeParent(());
400
- ```
401
-
402
- #### `getMergeRegion(())`
403
- Delegates core actions to slide element sub-managers.
404
-
405
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
406
-
407
- ```javascript
408
- ppt.useSlide(1).getMergeRegion(());
409
- ```
410
-
411
- #### `splitMergedRegion(())`
412
- Delegates core actions to slide element sub-managers.
413
-
414
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
415
-
416
- ```javascript
417
- ppt.useSlide(1).splitMergedRegion(());
418
- ```
419
-
420
- #### `cloneMergedRegion(())`
421
- Delegates core actions to slide element sub-managers.
422
-
423
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
87
+ ## Logging
424
88
 
425
- ```javascript
426
- ppt.useSlide(1).cloneMergedRegion(());
427
- ```
428
-
429
- #### `autoFitTable(())`
430
- Delegates core actions to slide element sub-managers.
431
-
432
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
89
+ The library is **completely silent by default**. Enable logging when needed:
433
90
 
434
91
  ```javascript
435
- ppt.useSlide(1).autoFitTable(());
436
- ```
92
+ // At load time
93
+ const ppt = await PPTXTemplater.load('template.pptx', { logLevel: 'debug' });
437
94
 
438
- #### `resizeTable(())`
439
- Delegates core actions to slide element sub-managers.
95
+ // Globally (affects all instances)
96
+ PPTXTemplater.setLogLevel('debug');
440
97
 
441
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
98
+ // Instance shortcut
99
+ ppt.enableDebug();
442
100
 
443
- ```javascript
444
- ppt.useSlide(1).resizeTable(());
101
+ // Suppress everything (explicit)
102
+ PPTXTemplater.setLogLevel('silent');
445
103
  ```
446
104
 
447
- #### `getTables(())`
448
- Delegates core actions to slide element sub-managers.
449
-
450
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
451
-
452
- ```javascript
453
- ppt.useSlide(1).getTables(());
454
- ```
105
+ Supported levels: `verbose` | `debug` | `info` | `warn` (default) | `error` | `silent`
455
106
 
456
107
  ---
457
108
 
458
- ### Charts API
459
-
460
- #### `updateChart(chartId, data)`
461
- Updates chart data in the selected slide(s). Finds charts by their name/ID and updates categories, series, and values. Preserves original chart styles, themes, and formatting. Supports inline custom data labels by passing objects in the format `{ data: number, label: string }` instead of numbers.
462
-
463
- * **Arguments**:
464
- * `chartId` (`string`): Chart name or relationship ID.
465
- * `data` (`ChartData`): New chart data.
466
- * `data.categories` (`string[]`): Category labels (X-axis).
467
- * `data.series` (`SeriesData[]`): Data series array.
468
- * `data.series[].name` (`string`): Series name.
469
- * `data.series[].values` (`number[]|object[]`): Data values (numbers or label objects).
470
- * **Returns**: `PPTXTemplater` - this (chainable)
471
-
472
- ```javascript
473
- ppt.useSlide(1).updateChart(chartId, data);
474
- ```
475
-
476
- #### `validateCharts()`
477
- Validates all charts in the presentation to ensure they are not corrupted. Checks XML, caches, and embedded workbook references.
478
-
479
- * **Returns**: `Promise<Object>` - Validation results for charts.
480
-
481
- ```javascript
482
- ppt.useSlide(1).validateCharts();
483
- ```
484
-
485
- #### `repairCharts()`
486
- Repairs common chart corruption issues such as broken caches, missing embedded workbooks, or orphan nodes.
487
-
488
- * **Returns**: `Promise<PPTXTemplater>` - this
489
-
490
- ```javascript
491
- ppt.useSlide(1).repairCharts();
492
- ```
493
-
494
- #### `getChartLabelPositions(chartId)`
495
- Retrieves the exact coordinate positions of all data labels for a chart on the active slide. Calculates absolute layout limits in EMUs (English Metric Units).
496
-
497
- * **Arguments**:
498
- * `chartId` (`string`):
499
- * **Returns**: `Promise<Array<{series: string, category: string, seriesIndex: number, categoryIndex: number, value: number, x: number, y: number, width: number, height: number` -
500
-
501
- ```javascript
502
- const positions = await ppt.useSlide(1).getChartLabelPositions('SalesChart');
503
- ```
504
-
505
- #### `getChartBarPositions(chartId)`
506
- Retrieves the exact coordinate positions of all bars/columns for a chart on the active slide. Calculates absolute layout limits in EMUs (English Metric Units).
507
-
508
- * **Arguments**:
509
- * `chartId` (`string`):
510
- * **Returns**: `Promise<Array<{series: string, category: string, seriesIndex: number, categoryIndex: number, value: number, x: number, y: number, width: number, height: number` -
511
-
512
- ```javascript
513
- const bars = await ppt.useSlide(1).getChartBarPositions('SalesChart');
514
- ```
515
-
516
- #### `addTextAtPosition(options)`
517
- Adds a textbox shape at a specific EMU coordinate position on targeted slides. Supports custom font styling and alignment configuration.
518
-
519
- * **Arguments**:
520
- * `options` (`Object`):
521
- * `options.text` (`string`):
522
- * `options.x` (`number`):
523
- * `options.y` (`number`):
524
- * `[options.width=1200000]` (`number`):
525
- * `[options.height=300000]` (`number`):
526
- * `[options.style]` (`Object`):
527
- * **Returns**: `this` - The chainable presentation engine instance.
528
-
529
- ```javascript
530
- ppt.useSlide(1).addTextAtPosition({
531
- text: 'Label',
532
- x: 1000000,
533
- y: 1000000
534
- });
535
- ```
536
-
537
- #### `addTextNearChartLabel(options)`
538
- Dynamically places textboxes next to a chart's data labels with vertical collision avoidance. Textboxes are positioned either on the left or right of the chart area, vertically aligned with their corresponding label.
539
-
540
- * **Arguments**:
541
- * `options` (`Object`):
542
- * `options.chart` (`string`):
543
- * `options.text` (`string|Function`):
544
- * `[options.position='left']` (`'left'|'right'`):
545
- * `[options.style]` (`Object`):
546
- * **Returns**: `this` - The chainable presentation engine instance.
547
-
548
- ```javascript
549
- ppt.addTextNearChartLabel({
550
- chart: 'SalesChart',
551
- text: 'Series',
552
- position: 'left'
553
- });
554
- ```
555
-
556
- #### `updateChartData(())`
557
- Delegates core actions to slide element sub-managers.
558
-
559
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
560
-
561
- ```javascript
562
- ppt.useSlide(1).updateChartData('sales-chart', {
563
- categories: ['Q1', 'Q2'],
564
- series: [{ name: 'Actual', values: [100, 150] }]
565
- });
109
+ ## Examples
110
+
111
+ The [`examples/`](./examples) directory contains runnable scripts for every major feature:
112
+
113
+ | Script | What it demonstrates |
114
+ |---|---|
115
+ | `basic-usage.js` | Text, charts, tables, links — end-to-end workflow |
116
+ | `chart-update.js` | Multi-series chart updates with Excel sync |
117
+ | `chart-data-labels.js` | Custom data label formatting and positioning |
118
+ | `table-update.js` | Cell updates, row management, merging |
119
+ | `slide-selection.js` | Slide operations — duplicate, move, delete |
120
+ | `image-operations.js` | Add, replace, remove images |
121
+ | `shape-operations.js` | Shape lifecycle — add, update, clone, delete |
122
+ | `z-order.js` | Layer ordering — bring/send, swap, set Z-index |
123
+ | `slide-import.js` | Import/export slides between presentations |
124
+ | `text-search.js` | Find text, replace multiple, update named shapes |
125
+ | `table-extraction.js` | Extract table data as JSON (all 3 modes) |
126
+ | `nested-table-rows.js` | Nested rows with rowspan / auto / none strategies |
127
+ | `xml-folder-workflow.js` | Extract → edit XML → rebuild PPTX |
128
+
129
+ Run any example:
130
+ ```bash
131
+ npm run example:basic
132
+ npm run example:images
133
+ npm run example:shapes
134
+ # ... etc
566
135
  ```
567
136
 
568
- #### `replaceChartSeries(())`
569
- Delegates core actions to slide element sub-managers.
570
-
571
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
572
-
573
- ```javascript
574
- ppt.useSlide(1).replaceChartSeries(());
575
- ```
137
+ ---
576
138
 
577
- #### `updateChartTitle(())`
578
- Delegates core actions to slide element sub-managers.
139
+ ## Core Concepts
579
140
 
580
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
141
+ ### Template Design
142
+ Create your template in PowerPoint. Use `{{placeholder}}` tags in text boxes, name your shapes, tables, and charts clearly — the library finds them by name.
581
143
 
144
+ ### Slide Targeting
582
145
  ```javascript
583
- ppt.useSlide(1).updateChartTitle('sales-chart', 'Quarterly Metrics Overview');
146
+ ppt.useSlide(2) // Target slide 2
147
+ ppt.useAllSlides() // Target all slides
584
148
  ```
585
149
 
586
- #### `updateChartCategories(())`
587
- Delegates core actions to slide element sub-managers.
588
-
589
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
590
-
150
+ ### Chaining
151
+ Most methods return `this`, enabling fluent chains:
591
152
  ```javascript
592
- ppt.useSlide(1).updateChartCategories(());
153
+ ppt.useSlide(1)
154
+ .replaceTextByTag('{{name}}', 'Alice')
155
+ .updateShapePosition('logo', { x: 500000, y: 200000 })
156
+ .bringToFront('logo');
593
157
  ```
594
158
 
595
- #### `updateDataLabels(())`
596
- Delegates core actions to slide element sub-managers.
597
-
598
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
599
-
159
+ ### Table Data Extraction
600
160
  ```javascript
601
- ppt.useSlide(1).updateDataLabels('SalesChart', {
602
- series: 0,
603
- labels: ['Excellent', 'Good', 'Poor']
604
- });
605
- ```
161
+ // Object array (header row = keys)
162
+ const rows = await ppt.getTableRows('SalesTable');
163
+ // → [{ region: 'North', sales: '1200' }, ...]
606
164
 
607
- #### `getDataLabels(())`
608
- Delegates core actions to slide element sub-managers.
165
+ // Raw string arrays
166
+ const raw = await ppt.getTableRows('SalesTable', { raw: true });
167
+ // → [['North', '1200'], ...]
609
168
 
610
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
611
-
612
- ```javascript
613
- const labels = await ppt.useSlide(1).getDataLabels('SalesChart', { series: 0 });
614
- console.log(labels); // [{ point: 0, value: 'Excellent' }, ...]
169
+ // With metadata
170
+ const meta = await ppt.getTableRows('SalesTable', { includeMetadata: true });
171
+ // → { rows: [...], rowCount: 5, columnCount: 3, mergedCells: [] }
615
172
  ```
616
173
 
617
- #### `validateDataLabels(())`
618
- Delegates core actions to slide element sub-managers.
619
-
620
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
174
+ ### Table Cell Shapes
175
+ Cell shapes are overlay graphics anchored within table cells. They are positioned absolutely based on cell bounds, and **never** modify row heights, column widths, cell margins, or trigger table reflow. Offsets (`x`, `y`) are relative to the cell's top-left corner. Oversized shapes are scaled down proportionally to fit inside the cell.
621
176
 
622
177
  ```javascript
623
- const result = await ppt.useSlide(1).validateDataLabels('SalesChart', {
624
- labels: ['High', 'Low']
178
+ // Add a simple indicator
179
+ await ppt.addCellShape('SalesTable', 2, 1, {
180
+ type: 'circle',
181
+ width: 12,
182
+ height: 12,
183
+ fill: '#10B981' // Green status dot
625
184
  });
626
- console.log(result.valid);
627
- ```
628
185
 
629
- #### `validateChartLabels(())`
630
- Delegates core actions to slide element sub-managers.
631
-
632
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
633
-
634
- ```javascript
635
- const result = await ppt.useSlide(1).validateChartLabels('SalesChart', {
636
- labels: ['High', 'Low']
186
+ // Add a badge with text and custom offsets
187
+ await ppt.addCellShape('SalesTable', 1, 2, {
188
+ type: 'badge',
189
+ text: 'Active',
190
+ fill: '#3B82F6',
191
+ x: 4,
192
+ y: 2,
193
+ width: 50,
194
+ height: 16
637
195
  });
638
- console.log(result.valid);
639
196
  ```
640
197
 
641
- #### `validateSeriesNameLabels(())`
642
- Delegates core actions to slide element sub-managers.
643
-
644
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
645
-
198
+ ### XML Folder Workflow
646
199
  ```javascript
647
- const result = await ppt.useSlide(1).validateSeriesNameLabels('SalesChart', {
648
- enabled: true,
649
- position: 'left'
650
- });
651
- console.log(result.valid);
652
- ```
653
-
654
- #### `getCharts(())`
655
- Delegates core actions to slide element sub-managers.
200
+ // Extract PPTX to a folder of XML files (version-control friendly)
201
+ await PPTXTemplater.extractPptx('template.pptx', './pptx-xml/');
656
202
 
657
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
658
-
659
- ```javascript
660
- ppt.useSlide(1).getCharts(());
203
+ // Load, modify, rebuild
204
+ const ppt = await PPTXTemplater.load('./pptx-xml/');
205
+ ppt.useAllSlides().replaceTextByTag('{{env}}', 'Production');
206
+ await PPTXTemplater.buildPptx('./pptx-xml/', 'output.pptx');
661
207
  ```
662
208
 
663
209
  ---
664
210
 
665
- ### Slides API
666
-
667
- #### `useSlide(...slideRefs)`
668
- Selects one or more slides to work on. All subsequent operations (replaceText, updateChart, etc.) apply to these slides. If not called, operations apply to ALL slides.
669
-
670
- * **Arguments**:
671
- * `slideRefs` (`...number|string`): Slide numbers (1-based), IDs, or tags.
672
- * **Returns**: `PPTXTemplater` - this (chainable)
673
-
674
- ```javascript
675
- ppt.useSlide(1).useSlide(...slideRefs);
676
- ```
677
-
678
- #### `useAllSlides()`
679
- Selects all slides.
680
-
681
- * **Returns**: `PPTXTemplater` - this (chainable)
682
-
683
- ```javascript
684
- ppt.useSlide(1).useAllSlides();
685
- ```
686
-
687
- #### `addSlide(options = {})`
688
- Adds a new slide to the presentation. Automatically generates required XML and relationship entries.
689
-
690
- * **Arguments**:
691
- * `options` (`NewSlideOptions`): Slide definition.
692
- * `[options.title]` (`string`): Slide title text.
693
- * `[options.layout]` (`string`): Layout name to use (default: 'blank').
694
- * `[options.elements]` (`SlideElement[]`): Elements to add to the slide.
695
- * **Returns**: `PPTXTemplater` - this (chainable)
696
-
697
- ```javascript
698
- ppt.useSlide(1).addSlide(options = {});
699
- ```
700
-
701
- #### `cloneSlide(sourceSlideNumber, atPosition)`
702
- Clones an existing slide and appends it to the end (or at a position).
703
-
704
- * **Arguments**:
705
- * `sourceSlideNumber` (`number`): 1-based source slide number.
706
- * `[atPosition]` (`number`): Optional position to insert (1-based). Default: append.
707
- * **Returns**: `PPTXTemplater` - this (chainable)
708
-
709
- ```javascript
710
- ppt.useSlide(1).cloneSlide(sourceSlideNumber, atPosition);
711
- ```
712
-
713
- #### `removeSlide(slideNumber)`
714
- Removes a slide from the presentation.
715
-
716
- * **Arguments**:
717
- * `slideNumber` (`number`): 1-based slide number to remove.
718
- * **Returns**: `PPTXTemplater` - this (chainable)
719
-
720
- ```javascript
721
- ppt.useSlide(1).removeSlide(slideNumber);
722
- ```
723
-
724
- #### `reorderSlides(order)`
725
- Reorders slides in the presentation.
726
-
727
- * **Arguments**:
728
- * `order` (`number[]`): Array of 1-based slide numbers in desired order.
729
- * **Returns**: `PPTXTemplater` - this (chainable)
730
-
731
- ```javascript
732
- ppt.useSlide(1).reorderSlides(order);
733
- ```
734
-
735
- #### `tagSlide(slideNumber, tag)`
736
- Tags a slide with a custom string identifier for later selection.
737
-
738
- * **Arguments**:
739
- * `slideNumber` (`number`): 1-based slide number.
740
- * `tag` (`string`): Custom tag string.
741
- * **Returns**: `PPTXTemplater` - this (chainable)
742
-
743
- ```javascript
744
- ppt.useSlide(1).tagSlide(slideNumber, tag);
745
- ```
746
-
747
- #### `exportSlides(...slideNumbers)`
748
- Exports selected slides to a new standalone PPTX engine. Useful for creating "slide decks" from a master template.
749
-
750
- * **Arguments**:
751
- * `slideNumbers` (`...number`): 1-based slide numbers to export.
752
- * **Returns**: `Promise<PPTXTemplater>` - New engine with only the selected slides.
753
-
754
- ```javascript
755
- ppt.useSlide(1).exportSlides(...slideNumbers);
756
- ```
211
+ ## API Reference
757
212
 
758
- #### `importSlideFrom(sourceEngine, slideRef)`
759
- Imports a single slide from another PPTXTemplater instance into this presentation. Preserves all slide layouts, charts, relationships, and embedded media.
213
+ Full API documentation with examples, parameter types, and advanced usage:
760
214
 
761
- * **Arguments**:
762
- * `sourceEngine` (`PPTXTemplater`): Source PPTXTemplater instance.
763
- * `slideRef` (`number|string`): Slide index (1-based), ID, or custom tag.
764
- * **Returns**: `Promise<PPTXTemplater>` - this (chainable)
765
-
766
- ```javascript
767
- const source = await PPTXTemplater.load('template2.pptx');
768
- await ppt.importSlideFrom(source, 1);
769
- ```
215
+ **👉 https://jsuyog2.github.io/node-pptx-templater/**
770
216
 
771
- #### `importSlides(slideIndices)`
772
- Imports selected slides from the current template, discarding the rest. The remaining slides are reordered to match the provided array. Preserves all layouts, themes, relationships, and embedded media.
217
+ ---
773
218
 
774
- * **Arguments**:
775
- * `slideIndices` (`number[]`): Array of 1-based slide indices to keep.
776
- * **Returns**: `PPTXTemplater` - this (chainable)
219
+ ## Output Options
777
220
 
778
221
  ```javascript
779
- ppt.useSlide(1).importSlides(slideIndices);
222
+ await ppt.saveToFile('./output.pptx'); // File on disk
223
+ const buffer = await ppt.toBuffer(); // Node.js Buffer (for HTTP responses)
224
+ const stream = await ppt.toStream(); // Readable stream (for piping)
225
+ await ppt.saveToFolder('./output-xml/'); // Raw XML folder
780
226
  ```
781
227
 
782
- #### `duplicateSlide(())`
783
- Delegates core actions to slide element sub-managers.
228
+ ---
784
229
 
785
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
230
+ ## Caching & Performance
786
231
 
787
232
  ```javascript
788
- ppt.duplicateSlide(1, 2);
789
- ```
233
+ // Preload templates for repeated generation (e.g., in a web server)
234
+ await PPTXTemplater.preload('./template.pptx');
790
235
 
791
- #### `deleteSlide(())`
792
- Delegates core actions to slide element sub-managers.
236
+ // Load from cache — zero disk I/O on subsequent calls
237
+ const ppt = await PPTXTemplater.fromCache('./template.pptx');
793
238
 
794
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
795
-
796
- ```javascript
797
- ppt.useSlide(1).deleteSlide(());
239
+ // Profile performance
240
+ ppt.enablePerformanceProfile();
241
+ // ... do work ...
242
+ console.log(ppt.getPerformanceMetrics());
798
243
  ```
799
244
 
800
- #### `moveSlide(())`
801
- Delegates core actions to slide element sub-managers.
245
+ ---
802
246
 
803
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
247
+ ## Validation & Repair
804
248
 
805
249
  ```javascript
806
- ppt.useSlide(1).moveSlide(());
807
- ```
808
-
809
- #### `insertSlide(())`
810
- Delegates core actions to slide element sub-managers.
811
-
812
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
813
-
814
- ```javascript
815
- ppt.useSlide(1).insertSlide(());
816
- ```
817
-
818
- #### `getSlides(())`
819
- Delegates core actions to slide element sub-managers.
820
-
821
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
822
-
823
- ```javascript
824
- ppt.useSlide(1).getSlides(());
825
- ```
826
-
827
- ---
828
-
829
- ### Text API
830
-
831
- #### `replaceText(replacements)`
832
- Replaces template placeholders (e.g., {{key}}) with values in the selected slides. Works inside text boxes, titles, grouped shapes, tables, and shapes.
833
-
834
- * **Arguments**:
835
- * `replacements` (`Object.<string, string>`): Map of placeholder → replacement value.
836
- * **Returns**: `PPTXTemplater` - this (chainable)
837
-
838
- ```javascript
839
- ppt.useSlide(1).replaceText(replacements);
840
- ```
841
-
842
- #### `addHyperlink(options)`
843
- Adds or replaces a hyperlink on a text run or shape.
844
-
845
- * **Arguments**:
846
- * `options` (`HyperlinkOptions`): Hyperlink configuration.
847
- * `options.text` (`string`): Text to find and make clickable.
848
- * `options.url` (`string`): Target URL.
849
- * `[options.tooltip]` (`string`): Optional tooltip.
850
- * **Returns**: `PPTXTemplater` - this (chainable)
851
-
852
- ```javascript
853
- ppt.useSlide(1).addHyperlink(options);
854
- ```
855
-
856
- #### `addSlideLink(options)`
857
- Adds an inter-slide hyperlink to a specific text element.
858
-
859
- * **Arguments**:
860
- * `options` (`Object`): Link configuration.
861
- * `options.sourceSlide` (`number`): Source slide number (1-based).
862
- * `options.targetSlide` (`number`): Destination slide number (1-based).
863
- * `options.element` (`string`): Text element to make clickable.
864
- * **Returns**: `PPTXTemplater` - this (chainable)
865
-
866
- ```javascript
867
- ppt.useSlide(1).addSlideLink(options);
868
- ```
869
-
870
- #### `addImageLink(options)`
871
- Adds an inter-slide hyperlink to an image.
872
-
873
- * **Arguments**:
874
- * `options` (`Object`):
875
- * `options.slide` (`number`): Source slide number.
876
- * `options.imageId` (`string`): Image name/id to make clickable.
877
- * `options.targetSlide` (`number`): Destination slide number.
878
- * **Returns**: `PPTXTemplater` - this
879
-
880
- ```javascript
881
- ppt.useSlide(1).addImageLink(options);
882
- ```
883
-
884
- #### `addShapeLink(options)`
885
- Adds an inter-slide hyperlink to a shape.
886
-
887
- * **Arguments**:
888
- * `options` (`Object`):
889
- * `options.slide` (`number`): Source slide number.
890
- * `options.shapeId` (`string`): Shape name/id to make clickable.
891
- * `options.targetSlide` (`number`): Destination slide number.
892
- * **Returns**: `PPTXTemplater` - this
893
-
894
- ```javascript
895
- ppt.useSlide(1).addShapeLink(options);
896
- ```
897
-
898
- #### `addTextNavigationLink(options)`
899
- Adds a special navigation link (next, previous, first, last slide) to a text element.
900
-
901
- * **Arguments**:
902
- * `options` (`Object`):
903
- * `options.slide` (`number`): Source slide number (1-based).
904
- * `options.element` (`string`): Text element to make clickable.
905
- * `options.action` (`'next'|'previous'|'first'|'last'`): Navigation action type.
906
- * **Returns**: `PPTXTemplater` - this (chainable)
907
-
908
- ```javascript
909
- ppt.useSlide(1).addTextNavigationLink(options);
910
- ```
911
-
912
- #### `addShapeNavigationLink(options)`
913
- Adds a special navigation link (next, previous, first, last slide) to a shape or image.
914
-
915
- * **Arguments**:
916
- * `options` (`Object`):
917
- * `options.slide` (`number`): Source slide number (1-based).
918
- * `options.shapeId` (`string`): Shape name/id to make clickable.
919
- * `options.action` (`'next'|'previous'|'first'|'last'`): Navigation action type.
920
- * **Returns**: `PPTXTemplater` - this (chainable)
921
-
922
- ```javascript
923
- ppt.useSlide(1).addShapeNavigationLink(options);
924
- ```
925
-
926
- #### `updateText(tag, data)`
927
- Updates shape text or list content by placeholder tag or shape name/ID. Supports bullet lists, numbered lists, nested lists, and custom styling.
928
-
929
- * **Arguments**:
930
- * `tag` (`string`): Placeholder tag (e.g. '{{name}}' or 'name') or shape name/ID.
931
- * `data` (`string|Object`): String value or list configuration object.
932
- * **Returns**: `PPTXTemplater` - this (chainable)
933
-
934
- ```javascript
935
- ppt.useSlide(1).updateText('Features', {
936
- list: ['Point A', 'Point B', 'Point C']
937
- });
938
- ```
939
-
940
- #### `getList(tag)`
941
- Retrieves list items from a shape or text box by name or placeholder tag.
942
-
943
- * **Arguments**:
944
- * `tag` (`string`): Shape name/ID or placeholder tag.
945
- * **Returns**: `Array` - Nested list structure of items.
946
-
947
- ```javascript
948
- const items = ppt.useSlide(1).getList('Features');
949
- console.log(items); // ['A', { text: 'B', children: [...] }]
950
- ```
951
-
952
- #### `validateList(data)`
953
- Validates a list structure and values.
954
-
955
- * **Arguments**:
956
- * `data` (`Object|Array`): List config object or array of items.
957
- * **Returns**: `Object` - Report containing validation result.
958
-
959
- ```javascript
960
- const result = ppt.validateList(['Valid string', 'Another item']);
961
- console.log(result.valid);
962
- ```
963
-
964
- #### `replaceTextByTag(())`
965
- Delegates core actions to slide element sub-managers.
966
-
967
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
968
-
969
- ```javascript
970
- ppt.useSlide(1).replaceTextByTag('company', 'Acme Corp');
971
- ```
972
-
973
- #### `replaceMultiple(())`
974
- Delegates core actions to slide element sub-managers.
975
-
976
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
977
-
978
- ```javascript
979
- ppt.useSlide(1).replaceMultiple(());
980
- ```
981
-
982
- #### `findText(())`
983
- Delegates core actions to slide element sub-managers.
984
-
985
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
986
-
987
- ```javascript
988
- ppt.useSlide(1).findText(());
989
- ```
990
-
991
- #### `getTextElements(())`
992
- Delegates core actions to slide element sub-managers.
993
-
994
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
995
-
996
- ```javascript
997
- ppt.useSlide(1).getTextElements(());
998
- ```
999
-
1000
- ---
1001
-
1002
- ### Images API
1003
-
1004
- #### `replaceImage(())`
1005
- Delegates core actions to slide element sub-managers.
1006
-
1007
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1008
-
1009
- ```javascript
1010
- await ppt.useSlide(1).replaceImage('logo-img', './new-logo.png');
1011
- ```
1012
-
1013
- #### `addImage(())`
1014
- Delegates core actions to slide element sub-managers.
1015
-
1016
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1017
-
1018
- ```javascript
1019
- ppt.useSlide(1).addImage(());
1020
- ```
1021
-
1022
- #### `removeImage(())`
1023
- Delegates core actions to slide element sub-managers.
1024
-
1025
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1026
-
1027
- ```javascript
1028
- ppt.useSlide(1).removeImage(());
1029
- ```
1030
-
1031
- #### `getImages(())`
1032
- Delegates core actions to slide element sub-managers.
1033
-
1034
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1035
-
1036
- ```javascript
1037
- ppt.useSlide(1).getImages(());
1038
- ```
1039
-
1040
- ---
1041
-
1042
- ### Shapes API
1043
-
1044
- #### `updateShapePosition(shapeId, options = {})`
1045
- Updates the position and/or dimensions of an existing shape on targeted slides.
1046
-
1047
- * **Arguments**:
1048
- * `shapeId` (`string`):
1049
- * `options` (`Object`):
1050
- * `[options.x]` (`number`):
1051
- * `[options.y]` (`number`):
1052
- * `[options.width]` (`number`):
1053
- * `[options.height]` (`number`):
1054
- * **Returns**: `this` - The chainable presentation engine instance.
1055
-
1056
- ```javascript
1057
- ppt.useSlide(1).updateShapePosition('TitleShape', { x: 1000000, y: 1500000 });
1058
- ```
1059
-
1060
- #### `updateTextBoxPosition(textBoxId, options = {})`
1061
- Updates the position and/or dimensions of an existing textbox on targeted slides.
1062
-
1063
- * **Arguments**:
1064
- * `textBoxId` (`string`):
1065
- * `options` (`Object`):
1066
- * `[options.x]` (`number`):
1067
- * `[options.y]` (`number`):
1068
- * `[options.width]` (`number`):
1069
- * `[options.height]` (`number`):
1070
- * **Returns**: `this` - The chainable presentation engine instance.
1071
-
1072
- ```javascript
1073
- ppt.useSlide(1).updateTextBoxPosition('TextBox 2', { x: 1000000, y: 1500000 });
1074
- ```
1075
-
1076
- #### `validateShape(options)`
1077
- Validates shape options configuration.
1078
-
1079
- * **Arguments**:
1080
- * `options` (`Object`):
1081
- * **Returns**: `string[]` - List of validation error messages.
1082
-
1083
- ```javascript
1084
- const errors = ppt.validateShape(shapeOptions);
1085
- ```
1086
-
1087
- #### `addShape(options)`
1088
- Adds a new shape dynamically to the targeted slide(s).
1089
-
1090
- * **Arguments**:
1091
- * `options` (`Object`):
1092
- * **Returns**: `this` - The chainable presentation templater instance.
1093
-
1094
- ```javascript
1095
- await ppt.useSlide(1).addShape({
1096
- type: 'rectangle',
1097
- id: 'sales-box',
1098
- x: 100,
1099
- y: 100,
1100
- width: 200,
1101
- height: 100,
1102
- fill: '#2563EB'
1103
- });
1104
- ```
1105
-
1106
- #### `updateShape(shapeId, options)`
1107
- Updates an existing shape in-place.
1108
-
1109
- * **Arguments**:
1110
- * `shapeId` (`string`):
1111
- * `options` (`Object`):
1112
- * **Returns**: `this` - The chainable presentation templater instance.
1113
-
1114
- ```javascript
1115
- await ppt.useSlide(1).updateShape('sales-box', { fill: '#10B981' });
1116
- ```
1117
-
1118
- #### `removeShape(shapeId)`
1119
- Removes a shape from the targeted slide(s).
1120
-
1121
- * **Arguments**:
1122
- * `shapeId` (`string`):
1123
- * **Returns**: `this` - The chainable presentation templater instance.
1124
-
1125
- ```javascript
1126
- await ppt.useSlide(1).removeShape('sales-box');
1127
- ```
1128
-
1129
- #### `getShape(shapeId)`
1130
- Discovers and retrieves details of an existing shape on the targeted slides.
1131
-
1132
- * **Arguments**:
1133
- * `shapeId` (`string`):
1134
- * **Returns**: `Object|null` - Shape details object, or null if not found.
1135
-
1136
- ```javascript
1137
- const shape = ppt.getShape('sales-box');
1138
- ```
1139
-
1140
- #### `updateShapeText(())`
1141
- Delegates core actions to slide element sub-managers.
1142
-
1143
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1144
-
1145
- ```javascript
1146
- ppt.useSlide(1).updateShapeText(());
1147
- ```
1148
-
1149
- #### `cloneShape(())`
1150
- Delegates core actions to slide element sub-managers.
1151
-
1152
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1153
-
1154
- ```javascript
1155
- ppt.useSlide(1).cloneShape('card-bg', 'card-bg-2');
1156
- ```
1157
-
1158
- #### `deleteShape(())`
1159
- Delegates core actions to slide element sub-managers.
1160
-
1161
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1162
-
1163
- ```javascript
1164
- ppt.useSlide(1).deleteShape(());
1165
- ```
1166
-
1167
- #### `getShapes(())`
1168
- Delegates core actions to slide element sub-managers.
1169
-
1170
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1171
-
1172
- ```javascript
1173
- ppt.useSlide(1).getShapes(());
1174
- ```
1175
-
1176
- ---
1177
-
1178
- ### Layer Stacking (Z-Order)
1179
-
1180
- #### `bringForward(optionsOrId)`
1181
- Moves slide element one layer forward.
1182
-
1183
-
1184
- ```javascript
1185
- ppt.useSlide(1).bringForward(optionsOrId);
1186
- ```
1187
-
1188
- #### `sendBackward(optionsOrId)`
1189
- Moves slide element one layer backward.
1190
-
1191
-
1192
- ```javascript
1193
- ppt.useSlide(1).sendBackward(optionsOrId);
1194
- ```
1195
-
1196
- #### `bringToFront(optionsOrId)`
1197
- Moves slide element above all other objects.
1198
-
1199
-
1200
- ```javascript
1201
- ppt.useSlide(1).bringToFront('OverlayLogo');
1202
- ```
1203
-
1204
- #### `sendToBack(optionsOrId)`
1205
- Moves slide element behind all other objects.
1206
-
1207
-
1208
- ```javascript
1209
- ppt.useSlide(1).sendToBack(optionsOrId);
1210
- ```
1211
-
1212
- #### `setZIndex(optionsOrId, zIndex)`
1213
- Moves slide element to the specific 1-based stacking position.
1214
-
1215
-
1216
- ```javascript
1217
- ppt.useSlide(1).setZIndex(optionsOrId, zIndex);
1218
- ```
1219
-
1220
- #### `moveObjectBefore(optionsOrId, targetId)`
1221
- Moves slide element directly before (below) a target element.
1222
-
1223
-
1224
- ```javascript
1225
- ppt.useSlide(1).moveObjectBefore(optionsOrId, targetId);
1226
- ```
1227
-
1228
- #### `moveObjectAfter(optionsOrId, targetId)`
1229
- Moves slide element directly after (above) a target element.
1230
-
1231
-
1232
- ```javascript
1233
- ppt.useSlide(1).moveObjectAfter(optionsOrId, targetId);
1234
- ```
1235
-
1236
- #### `reorderObjects(optionsOrOrder)`
1237
- Reorders slide objects exactly as specified in the array.
1238
-
1239
-
1240
- ```javascript
1241
- ppt.useSlide(1).reorderObjects(optionsOrOrder);
1242
- ```
1243
-
1244
- #### `getObjectOrder(slideIndex)`
1245
- Gets the ordered metadata of all objects on the slide.
1246
-
1247
-
1248
- ```javascript
1249
- ppt.useSlide(1).getObjectOrder(slideIndex);
1250
- ```
1251
-
1252
- #### `applyZOrder(slideOrConfigs, configsOption)`
1253
- Applies bulk template configurations for slide elements stacking layers.
1254
-
1255
-
1256
- ```javascript
1257
- ppt.useSlide(1).applyZOrder(slideOrConfigs, configsOption);
1258
- ```
1259
-
1260
- #### `getTopMostObject(slideIndex)`
1261
- Retrieves the info of the top-most object on the slide.
1262
-
1263
-
1264
- ```javascript
1265
- ppt.useSlide(1).getTopMostObject(slideIndex);
1266
- ```
1267
-
1268
- #### `getBottomMostObject(slideIndex)`
1269
- Retrieves the info of the bottom-most object on the slide.
1270
-
1271
-
1272
- ```javascript
1273
- ppt.useSlide(1).getBottomMostObject(slideIndex);
1274
- ```
1275
-
1276
- #### `swapObjects(slideIndexOrId1, id1OrId2, id2)`
1277
- Swaps stacking positions of two slide objects.
1278
-
1279
-
1280
- ```javascript
1281
- ppt.useSlide(1).swapObjects(slideIndexOrId1, id1OrId2, id2);
1282
- ```
1283
-
1284
- #### `sortObjects(slideIndexOrCompareFn, compareFnOption)`
1285
- Sorts stacking order using a custom comparison function.
1286
-
1287
-
1288
- ```javascript
1289
- ppt.useSlide(1).sortObjects(slideIndexOrCompareFn, compareFnOption);
1290
- ```
1291
-
1292
- #### `normalizeZOrder(slideIndex)`
1293
- Cleans up and normalizes stacking order consistency.
1294
-
1295
-
1296
- ```javascript
1297
- ppt.useSlide(1).normalizeZOrder(slideIndex);
1298
- ```
1299
-
1300
- ---
1301
-
1302
- ### Utilities & Validation
1303
-
1304
- #### `const()`
1305
- This is the primary public API. It coordinates all sub-managers (ZipManager, SlideManager, ChartManager, etc.) and exposes a fluent, chainable interface for template manipulation. OpenXML PPTX Structure: ├── [Content_Types].xml — lists all parts and their MIME types ├── _rels/.rels — root relationships (points to presentation) ├── ppt/ │ ├── presentation.xml — slide order, slide masters references │ ├── _rels/presentation.xml.rels │ ├── slides/ │ │ ├── slide1.xml — individual slide content │ │ └── _rels/slide1.xml.rels │ ├── slideLayouts/ — layout templates (title, content, etc.) │ ├── slideMasters/ — master slide designs │ ├── theme/ — color/font themes │ ├── charts/ — embedded chart XML │ └── media/ — embedded images/videos └── docProps/ ├── core.xml — author, title, etc. └── app.xml — application metadata
1306
-
1307
-
1308
- ```javascript
1309
- ppt.useSlide(1).const();
1310
- ```
1311
-
1312
- #### `class()`
1313
-
1314
-
1315
-
1316
- ```javascript
1317
- ppt.useSlide(1).class();
1318
- ```
1319
-
1320
- #### `static()`
1321
- Loads a PPTX template from a file path or buffer. @static @throws {PPTXError} If the file cannot be read or is not a valid PPTX.
1322
-
1323
- * **Arguments**:
1324
- * `source` (`string|Buffer`): Path to PPTX file or Buffer containing PPTX data.
1325
- * **Returns**: `Promise<PPTXTemplater>` - Initialized engine instance.
1326
-
1327
- ```javascript
1328
- ppt.useSlide(1).static();
1329
- ```
1330
-
1331
- #### `getInfo()`
1332
- Returns presentation metadata (title, author, slide count, etc.)
1333
-
1334
- * **Returns**: `PresentationInfo` - Metadata object.
1335
-
1336
- ```javascript
1337
- ppt.useSlide(1).getInfo();
1338
- ```
1339
-
1340
- #### `validate()`
1341
- Validates the XML structure of the current PPTX. Reports issues with relationship IDs, missing parts, etc.
1342
-
1343
- * **Returns**: `ValidationResult` - Object with `valid`, `errors`, and `warnings` arrays.
1344
-
1345
- ```javascript
1346
- ppt.useSlide(1).validate();
1347
- ```
1348
-
1349
- #### `repair()`
1350
- Repairs corrupted OpenXML structure, relationships, and content types. Removes orphan relationships, rebuilds slide references, and fixes missing entries.
1351
-
1352
- * **Returns**: `Promise<PPTXTemplater>` - this (chainable)
1353
-
1354
- ```javascript
1355
- ppt.useSlide(1).repair();
1356
- ```
1357
-
1358
- #### `debugRelationships()`
1359
- Logs all relationships across the presentation to the console for debugging.
1360
-
1361
- * **Returns**: `PPTXTemplater` - this (chainable)
1362
-
1363
- ```javascript
1364
- ppt.useSlide(1).debugRelationships();
1365
- ```
1366
-
1367
- #### `inspectSlide(slideIndex)`
1368
- Inspects a specific slide's structure and relationships.
1369
-
1370
- * **Arguments**:
1371
- * `slideIndex` (`number`): 1-based slide index.
1372
- * **Returns**: `PPTXTemplater` - this (chainable)
1373
-
1374
- ```javascript
1375
- ppt.useSlide(1).inspectSlide(slideIndex);
1376
- ```
1377
-
1378
- #### `inspectXML(xmlPath)`
1379
- Inspects and logs the raw XML of any file in the ZIP.
1380
-
1381
- * **Arguments**:
1382
- * `xmlPath` (`string`): Path inside the ZIP (e.g., 'ppt/slides/slide1.xml')
1383
- * **Returns**: `Promise<PPTXTemplater>` - this (chainable)
1384
-
1385
- ```javascript
1386
- ppt.useSlide(1).inspectXML(xmlPath);
1387
- ```
1388
-
1389
- #### `inspectChart(chartId)`
1390
- Inspects a specific chart's metadata and structure.
1391
-
1392
- * **Arguments**:
1393
- * `chartId` (`string`):
1394
-
1395
- ```javascript
1396
- ppt.useSlide(1).inspectChart(chartId);
1397
- ```
1398
-
1399
- #### `inspectChartXML(chartFileName)`
1400
- Inspects and logs the raw XML of a chart file.
1401
-
1402
- * **Arguments**:
1403
- * `chartFileName` (`string`):
1404
-
1405
- ```javascript
1406
- ppt.useSlide(1).inspectChartXML(chartFileName);
1407
- ```
1408
-
1409
- #### `debugChartRelationships()`
1410
- Logs all chart relationships.
1411
-
1412
-
1413
- ```javascript
1414
- ppt.useSlide(1).debugChartRelationships();
1415
- ```
1416
-
1417
- #### `saveToFile(filePath, options = {})`
1418
- Saves the modified PPTX to a file on disk.
1419
-
1420
- * **Arguments**:
1421
- * `filePath` (`string`): Output file path.
1422
- * `[options]` (`Object`): Save options.
1423
- * `[options.strict=false]` (`boolean`): Throw error on validation failure.
1424
- * **Returns**: `Promise<void>` -
1425
-
1426
- ```javascript
1427
- ppt.useSlide(1).saveToFile(filePath, options = {});
1428
- ```
1429
-
1430
- #### `save(filePath, options = {})`
1431
- Saves the presentation. Equivalent to saveToFile.
1432
-
1433
- * **Arguments**:
1434
- * `filePath` (`string`): Output file path.
1435
- * `[options]` (`Object`): Save options.
1436
- * **Returns**: `Promise<void>` -
1437
-
1438
- ```javascript
1439
- await ppt.save('output.pptx');
1440
- ```
1441
-
1442
- #### `saveXml(folderPath)`
1443
- Saves the modified presentation XML structures directly to a folder.
1444
-
1445
- * **Arguments**:
1446
- * `folderPath` (`string`): Target directory path.
1447
- * **Returns**: `Promise<void>` -
1448
-
1449
- ```javascript
1450
- ppt.useSlide(1).saveXml(folderPath);
1451
- ```
1452
-
1453
- #### `saveToFolder(folderPath)`
1454
- Saves the modified presentation XML structures directly to a folder.
1455
-
1456
- * **Arguments**:
1457
- * `folderPath` (`string`): Target directory path.
1458
- * **Returns**: `Promise<void>` -
1459
-
1460
- ```javascript
1461
- await ppt.saveToFolder('./output-template');
1462
- ```
1463
-
1464
- #### `toBuffer(options = {})`
1465
- Returns the PPTX content as a Node.js Buffer.
1466
-
1467
- * **Arguments**:
1468
- * `[options]` (`Object`): Save options.
1469
- * **Returns**: `Promise<Buffer>` -
1470
-
1471
- ```javascript
1472
- ppt.useSlide(1).toBuffer(options = {});
1473
- ```
1474
-
1475
- #### `toStream(options = {})`
1476
- Returns the PPTX content as a readable Node.js Stream.
1477
-
1478
- * **Arguments**:
1479
- * `[options]` (`Object`): Save options.
1480
- * **Returns**: `Promise<NodeJS.ReadableStream>` -
1481
-
1482
- ```javascript
1483
- ppt.useSlide(1).toStream(options = {});
1484
- ```
1485
-
1486
- #### `saveToStream(writableOrOptions, options = {})`
1487
- Saves the presentation to a readable stream or pipes it to a writable stream.
1488
-
1489
- * **Arguments**:
1490
- * `[writableOrOptions]` (`NodeJS.WritableStream|Object`): Writable stream to pipe to, or options object.
1491
- * `[options]` (`Object`): Save options if writable stream was passed first.
1492
- * **Returns**: `Promise<NodeJS.ReadableStream|void>` -
1493
-
1494
- ```javascript
1495
- ppt.useSlide(1).saveToStream(writableOrOptions, options = {});
1496
- ```
1497
-
1498
- #### `validatePresentationXml()`
1499
- Performs validation specifically on PowerPoint XML folder contents/relationships.
1500
-
1501
- * **Returns**: `Promise<{valid: boolean, errors: string[], warnings: string[]` -
1502
-
1503
- ```javascript
1504
- const report = await ppt.validatePresentationXml();
1505
- if (!report.valid) console.error(report.errors);
1506
- ```
1507
-
1508
- #### `slideCount()`
1509
- Returns the total number of slides in the loaded presentation. @type {number}
1510
-
1511
-
1512
- ```javascript
1513
- ppt.useSlide(1).slideCount();
1514
- ```
1515
-
1516
- #### `function()`
1517
- OpenXML relationship IDs follow the format rId1, rId2, rId3, ... They must be unique within each .rels file. These utilities generate collision-free IDs when adding new relationships. / /** Generates the next available relationship ID given an array of existing IDs. Always uses the format "rId{N}" where N is the next integer after the max.
1518
-
1519
- * **Arguments**:
1520
- * `existingIds` (`string[]`): Array of existing rId strings (e.g., ['rId1', 'rId2']).
1521
- * **Returns**: `string` - New relationship ID (e.g., 'rId3').
1522
-
1523
- ```javascript
1524
- ppt.useSlide(1).function();
1525
- ```
1526
-
1527
- #### `preload(())`
1528
- Delegates core actions to slide element sub-managers.
1529
-
1530
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1531
-
1532
- ```javascript
1533
- ppt.useSlide(1).preload(());
1534
- ```
1535
-
1536
- #### `cache(())`
1537
- Delegates core actions to slide element sub-managers.
1538
-
1539
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1540
-
1541
- ```javascript
1542
- ppt.useSlide(1).cache(());
1543
- ```
1544
-
1545
- #### `fromCache(())`
1546
- Delegates core actions to slide element sub-managers.
1547
-
1548
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1549
-
1550
- ```javascript
1551
- ppt.useSlide(1).fromCache(());
1552
- ```
1553
-
1554
- #### `clearCache(())`
1555
- Delegates core actions to slide element sub-managers.
1556
-
1557
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1558
-
1559
- ```javascript
1560
- ppt.useSlide(1).clearCache(());
1561
- ```
1562
-
1563
- #### `enablePerformanceProfile(())`
1564
- Delegates core actions to slide element sub-managers.
1565
-
1566
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1567
-
1568
- ```javascript
1569
- ppt.useSlide(1).enablePerformanceProfile(());
1570
- ```
1571
-
1572
- #### `getPerformanceMetrics(())`
1573
- Delegates core actions to slide element sub-managers.
1574
-
1575
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1576
-
1577
- ```javascript
1578
- ppt.useSlide(1).getPerformanceMetrics(());
1579
- ```
1580
-
1581
- #### `fromPresentationXml(())`
1582
- Delegates core actions to slide element sub-managers.
1583
-
1584
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1585
-
1586
- ```javascript
1587
- const ppt = await PPTXTemplate.fromPresentationXml('./template-folder');
1588
- ```
1589
-
1590
- #### `validatePresentation(())`
1591
- Delegates core actions to slide element sub-managers.
1592
-
1593
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1594
-
1595
- ```javascript
1596
- ppt.useSlide(1).validatePresentation(());
1597
- ```
1598
-
1599
- #### `validateSlide(())`
1600
- Delegates core actions to slide element sub-managers.
1601
-
1602
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1603
-
1604
- ```javascript
1605
- ppt.useSlide(1).validateSlide(());
1606
- ```
1607
-
1608
- #### `validateTable(())`
1609
- Delegates core actions to slide element sub-managers.
1610
-
1611
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1612
-
1613
- ```javascript
1614
- ppt.useSlide(1).validateTable(());
1615
- ```
1616
-
1617
- #### `validateArchive(())`
1618
- Delegates core actions to slide element sub-managers.
1619
-
1620
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1621
-
1622
- ```javascript
1623
- ppt.useSlide(1).validateArchive(());
1624
- ```
1625
-
1626
- #### `enableDebugZip(())`
1627
- Delegates core actions to slide element sub-managers.
1628
-
1629
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1630
-
1631
- ```javascript
1632
- ppt.useSlide(1).enableDebugZip(());
1633
- ```
1634
-
1635
- #### `validateRelationships(())`
1636
- Delegates core actions to slide element sub-managers.
1637
-
1638
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1639
-
1640
- ```javascript
1641
- ppt.useSlide(1).validateRelationships(());
1642
- ```
1643
-
1644
- #### `zipManager(())`
1645
- Delegates core actions to slide element sub-managers.
1646
-
1647
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1648
-
1649
- ```javascript
1650
- ppt.useSlide(1).zipManager(());
1651
- ```
1652
-
1653
- #### `xmlParser(())`
1654
- Delegates core actions to slide element sub-managers.
1655
-
1656
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1657
-
1658
- ```javascript
1659
- ppt.useSlide(1).xmlParser(());
1660
- ```
1661
-
1662
- #### `contentTypesManager(())`
1663
- Delegates core actions to slide element sub-managers.
1664
-
1665
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1666
-
1667
- ```javascript
1668
- ppt.useSlide(1).contentTypesManager(());
1669
- ```
1670
-
1671
- #### `relationshipManager(())`
1672
- Delegates core actions to slide element sub-managers.
1673
-
1674
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1675
-
1676
- ```javascript
1677
- ppt.useSlide(1).relationshipManager(());
1678
- ```
1679
-
1680
- #### `slideManager(())`
1681
- Delegates core actions to slide element sub-managers.
1682
-
1683
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1684
-
1685
- ```javascript
1686
- ppt.useSlide(1).slideManager(());
1687
- ```
1688
-
1689
- #### `chartManager(())`
1690
- Delegates core actions to slide element sub-managers.
1691
-
1692
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1693
-
1694
- ```javascript
1695
- ppt.useSlide(1).chartManager(());
1696
- ```
1697
-
1698
- #### `tableManager(())`
1699
- Delegates core actions to slide element sub-managers.
1700
-
1701
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1702
-
1703
- ```javascript
1704
- ppt.useSlide(1).tableManager(());
1705
- ```
1706
-
1707
- #### `shapeManager(())`
1708
- Delegates core actions to slide element sub-managers.
1709
-
1710
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1711
-
1712
- ```javascript
1713
- ppt.useSlide(1).shapeManager(());
1714
- ```
1715
-
1716
- #### `imageManager(())`
1717
- Delegates core actions to slide element sub-managers.
1718
-
1719
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1720
-
1721
- ```javascript
1722
- ppt.useSlide(1).imageManager(());
1723
- ```
1724
-
1725
- #### `textManager(())`
1726
- Delegates core actions to slide element sub-managers.
1727
-
1728
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1729
-
1730
- ```javascript
1731
- ppt.useSlide(1).textManager(());
1732
- ```
1733
-
1734
- #### `hyperlinkManager(())`
1735
- Delegates core actions to slide element sub-managers.
1736
-
1737
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1738
-
1739
- ```javascript
1740
- ppt.useSlide(1).hyperlinkManager(());
1741
- ```
1742
-
1743
- #### `mediaManager(())`
1744
- Delegates core actions to slide element sub-managers.
1745
-
1746
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1747
-
1748
- ```javascript
1749
- ppt.useSlide(1).mediaManager(());
1750
- ```
1751
-
1752
- #### `load(())`
1753
- Delegates core actions to slide element sub-managers.
1754
-
1755
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1756
-
1757
- ```javascript
1758
- const ppt = await PPTXTemplater.load('./my_template.pptx');
1759
- ```
1760
-
1761
- #### `create(())`
1762
- Delegates core actions to slide element sub-managers.
1763
-
1764
- * **Returns**: `PPTXTemplater` - The fluent engine instance.
1765
-
1766
- ```javascript
1767
- ppt.useSlide(1).create(());
1768
- ```
1769
-
1770
- ---
1771
-
1772
- <!-- API_REFERENCE_END -->
1773
-
1774
- ---
1775
-
1776
- ## 📊 Chart Data Labels & Value From Cells
1777
-
1778
- PPTXForge supports advanced, PowerPoint-compatible chart data labels. You can customize label data sources, templates, positioning, and visual styles.
1779
-
1780
- ### 1. Value From Cells (Excel Synchronization)
1781
- Pull dynamic data labels directly from worksheet range cells inside the backing Excel spreadsheet. This is Excel's native "Value From Cells" feature, reconstructed programmatically inside the `.pptx` XML and `.xlsx` worksheets.
1782
-
1783
- ```javascript
1784
- ppt.useSlide(1).updateDataLabels('SalesChart', {
1785
- series: 0,
1786
- labelsFromCells: 'Sheet1!D2:D5'
1787
- });
1788
- ```
1789
-
1790
- ### 2. Literal Arrays
1791
- Override data labels for specific points in a series with a static list of strings:
1792
-
1793
- ```javascript
1794
- ppt.useSlide(1).updateDataLabels('SalesChart', {
1795
- series: 0,
1796
- labels: ['Top Performance', 'Met Target', 'Action Required', 'At Risk']
1797
- });
1798
- ```
1799
-
1800
- ### 3. Dynamic Label Templates
1801
- Combine values, category names, percentages, and custom label strings to format annotations:
1802
-
1803
- ```javascript
1804
- ppt.useSlide(1).updateDataLabels('SalesChart', {
1805
- series: 0,
1806
- template: '{category}: {value} ({percentage}%)'
1807
- });
1808
- ```
1809
- * **Variables**: `{category}`, `{value}`, `{percentage}`, `{series}`, `{customLabel}`
1810
-
1811
- ### 4. Label Positions
1812
- Set label alignment to any of PowerPoint's standard values:
1813
-
1814
- ```javascript
1815
- ppt.useSlide(1).updateDataLabels('SalesChart', {
1816
- series: 0,
1817
- position: 'insideEnd' // 'center', 'insideEnd', 'insideBase', 'outsideEnd', 'bestFit', 'left', 'right', 'top', 'bottom'
1818
- });
1819
- ```
1820
-
1821
- ### 5. Custom Label Styling
1822
- Format your data labels using custom fonts, sizes, colors, and weight properties:
1823
-
1824
- ```javascript
1825
- ppt.useSlide(1).updateDataLabels('SalesChart', {
1826
- series: 0,
1827
- labels: ['High', 'Medium', 'Low'],
1828
- labelStyle: {
1829
- fontFamily: 'Century Gothic',
1830
- fontSize: 12,
1831
- color: '#0055A5',
1832
- bold: true,
1833
- italic: true,
1834
- underline: true
1835
- }
1836
- });
1837
- ```
1838
-
1839
- ### 6. Inline Custom Data Labels (`updateChart`)
1840
- You can define custom data labels inline within the standard `updateChart()` series `values` array using objects in the format `{ data: number, label: string }`. This avoids calling separate styling or update methods:
1841
-
1842
- ```javascript
1843
- ppt.useSlide(1).updateChart('RevenueChart', {
1844
- categories: ['Q1', 'Q2', 'Q3', 'Q4'],
1845
- series: [
1846
- {
1847
- name: 'Product A',
1848
- values: [
1849
- { data: 145, label: 'Q1: 145 (Low)' },
1850
- { data: 210, label: 'Q2: 210 (Med)' },
1851
- { data: 190, label: 'Q3: 190 (Med)' },
1852
- { data: 250, label: 'Q4: 250 (High)' }
1853
- ]
1854
- }
1855
- ]
1856
- });
1857
- ```
1858
-
1859
- To preserve PowerPoint integrity, the engine ensures that if one value contains a label, all values in that series must have labels, and the label properties must be string values.
1860
-
1861
- ### 7. Label Style Inheritance & Series Names Inside Bars
1862
-
1863
- #### Style Inheritance
1864
- When custom labels (inline or via `updateDataLabels`) are generated, they inherit the styling properties (font family, font size, bold, italic, color, and alignment) defined in the template's `<c:txPr>` tag for the series. This ensures your custom labels match the branding and layout design from your PowerPoint template file.
1865
-
1866
- #### Series Names Inside Bars (`showSeriesNameInBar`)
1867
- For stacked bar charts, you can show the series name inside each segment (typically centered) to make them easily readable. To enable this, set `showSeriesNameInBar: true` in the chart options (globally) or on individual series:
1868
-
1869
- ```javascript
1870
- ppt.useSlide(1).updateChart('RevenueChart', {
1871
- showSeriesNameInBar: true, // Show series name labels globally
1872
- categories: ['Q1', 'Q2', 'Q3', 'Q4'],
1873
- series: [
1874
- { name: 'Product A', values: [100, 200, 300, 400] },
1875
- { name: 'Product B', values: [150, 250, 350, 450], showSeriesNameInBar: true } // Or per-series
1876
- ]
1877
- });
1878
- ```
1879
-
1880
- #### Chart Labels Validation
1881
- You can programmatically validate that the chart labels configured in a chart conform to your template structure:
1882
-
1883
- ```javascript
1884
- const report = await ppt.useSlide(1).validateChartLabels('RevenueChart', {
1885
- labels: ['Custom label 1', 'Custom label 2'],
1886
- showSeriesNameInBar: true
1887
- });
1888
-
1889
- if (!report.valid) {
1890
- console.log('Errors:', report.errors);
1891
- console.log('Warnings:', report.warnings);
1892
- }
1893
- ```
1894
-
1895
- #### External Series Name Labels (`seriesNameLabels`)
1896
- You can position the series names outside the chart area as separate text boxes (`<p:sp>`) aligned with each corresponding bar or stack. These external labels automatically inherit the styling (font family, font size, bold, italic, color) defined in the template's series properties.
1897
-
1898
- Supported features:
1899
- * **Position**: Can be set to `'left'` or `'right'` to align text boxes to the left or right of the chart.
1900
- * **Auto-fit & Height Wrapping**: Automatically wraps text and calculates height if labels are longer than the available margin space.
1901
- * **Collision Detection**: Prevent overlapping with slide bounds by shrinking the chart, and resolve vertical overlaps of labels by shifting them apart.
1902
- * **Idempotency**: Safely clean up previous labels on multiple chart updates.
1903
-
1904
- Example usage:
1905
- ```javascript
1906
- ppt.useSlide(1).updateChart('RevenueChart', {
1907
- categories: ['Q1', 'Q2', 'Q3', 'Q4'],
1908
- series: [
1909
- { name: 'Product A', values: [100, 200, 300, 400] },
1910
- { name: 'Product B', values: [150, 250, 350, 450] }
1911
- ],
1912
- seriesNameLabels: {
1913
- enabled: true,
1914
- position: 'left', // 'left' or 'right'
1915
- autoFit: true // Automatically wrap and shrink layout if needed (default: true)
1916
- }
1917
- });
1918
- ```
1919
-
1920
- To validate your configuration and check for boundary or collision warning/errors:
1921
- ```javascript
1922
- const report = await ppt.useSlide(1).validateSeriesNameLabels('RevenueChart', {
1923
- enabled: true,
1924
- position: 'left',
1925
- autoFit: true
1926
- });
1927
-
1928
- if (!report.valid) {
1929
- console.error('Validation errors:', report.errors);
1930
- console.warn('Validation warnings:', report.warnings);
1931
- }
1932
- ```
1933
-
1934
- ---
1935
-
1936
- ## 📋 Native Lists (Bullet & Numbered Lists)
1937
-
1938
- PPTXForge supports native PowerPoint bullet lists and numbered lists across text placeholders, shapes, text boxes, table cells, and grouped shapes. When generating lists, the engine preserves run styles, custom bullet characters, indentation, and color overlays, generating valid OpenXML/DrawingML without repair alerts.
1939
-
1940
- ### 1. Basic Bullet List
1941
- Update a shape or text placeholder to be a bullet list:
1942
-
1943
- ```javascript
1944
- ppt.useSlide(1).updateText('Features', {
1945
- list: [
1946
- 'Fast PPTX generation',
1947
- 'OpenXML based',
1948
- 'Chart updates',
1949
- 'Table updates'
1950
- ]
1951
- });
1952
- ```
1953
-
1954
- ### 2. Numbered / Ordered List
1955
- Use the `ordered` flag to convert the list to a numbered format:
1956
-
1957
- ```javascript
1958
- ppt.useSlide(1).updateText('Steps', {
1959
- ordered: true,
1960
- list: [
1961
- 'Import template',
1962
- 'Update data',
1963
- 'Generate PPTX'
1964
- ]
1965
- });
1966
- ```
1967
- * Custom numbering systems can be specified with `style.numberType` (e.g., `arabicPeriod`, `alphaLcParen`, `romanUcPeriod`).
1968
-
1969
- ### 3. Nested / Multi-Level Lists
1970
- Construct hierarchy by passing objects containing a `text` string and a `children` array:
1971
-
1972
- ```javascript
1973
- ppt.useSlide(1).updateText('Requirements', {
1974
- list: [
1975
- 'Frontend',
1976
- {
1977
- text: 'Backend Development',
1978
- children: [
1979
- 'Node.js',
1980
- {
1981
- text: 'Databases',
1982
- children: ['MS SQL', 'PostgreSQL']
1983
- }
1984
- ]
1985
- }
1986
- ]
1987
- });
1988
- ```
1989
-
1990
- ### 4. Custom List Styling
1991
- Customize bullet characters, colors, sizes, and font properties:
1992
-
1993
- ```javascript
1994
- ppt.useSlide(1).updateText('KPIs', {
1995
- list: ['Revenue Up', 'Margins Normal'],
1996
- style: {
1997
- fontFamily: 'Arial',
1998
- fontSize: 18,
1999
- color: '#0055AA',
2000
- bulletColor: '#FF5500',
2001
- bulletChar: '✦',
2002
- bulletSize: 120 // Percentage relative to text (e.g. 120%)
2003
- }
2004
- });
2005
- ```
2006
-
2007
- ### 5. Table Cell Lists
2008
- You can also generate list hierarchies directly inside a cell of a DrawingML table:
2009
-
2010
- ```javascript
2011
- ppt.useSlide(3).updateTable('sales-table', [
2012
- ['Category', 'Performance details'],
2013
- ['North Region', '{{CellPlaceholder}}']
2014
- ]);
2015
-
2016
- ppt.updateText('CellPlaceholder', {
2017
- list: ['Table Bullet 1', 'Table Bullet 2']
2018
- });
2019
- ```
2020
-
2021
- ---
2022
-
2023
- ## 🔒 Advanced XML Security
2024
-
2025
- PPTXForge uses a secure XML parser that defends your application servers against common XML vulnerabilities.
2026
-
2027
- ### Built-in Protections
2028
- 1. **XML Bombs & Billion Laughs Mitigation**: Instantly rejects any XML payload containing `<!DOCTYPE>` or `<!ENTITY>` definitions, preventing parser lockups.
2029
- 2. **XXE Injection Defenses**: Completely blocks external entities referencing local files or external resources (e.g., `SYSTEM` or `PUBLIC`).
2030
- 3. **Decoupled Entity Expansion Limits**: Evaluates code points and standard characters (`&amp;`, `&lt;`, etc.) via a single-level parser, avoiding recursive parsing loops.
2031
-
2032
- ### Custom Security & Parsing API
2033
- Use the safe-parsing helpers to validate user-uploaded templates manually:
2034
- ```javascript
2035
- const { validateXml, safeParseXml } = require('node-pptx-templater');
2036
-
2037
- const schema = validateXml(userInputXml);
2038
- if (!schema.valid) {
2039
- console.error(`Invalid XML format: ${schema.error} on Line ${schema.line}`);
2040
- }
2041
- ```
2042
-
2043
- ---
2044
-
2045
- ## ⚡ Performance Benchmarks
2046
-
2047
- Below are benchmark results compiled on a standard Intel Core i7 system processing a 50-slide presentation template:
2048
-
2049
- | Operation | Average Duration |
2050
- | :--- | :--- |
2051
- | Load Template | ~110 ms |
2052
- | Scan & Replace 20 Text Tag Placeholders | ~2.5 ms |
2053
- | Validate XML Schemas & Relationship Integrity | ~14 ms |
2054
- | Table Row Cloning & Grid Merges (15 rows) | ~3 ms |
2055
- | Z-Order Re-indexing & Packaging Output | ~78 ms |
2056
-
2057
- ---
2058
-
2059
- ## ❓ FAQ & Developer Troubleshooting
2060
-
2061
- ### Q: PowerPoint displays a "Repair Presentation" alert when opening generated files.
2062
- * **Root Cause**: This is usually caused by:
2063
- 1. A missing relationship entry inside `.rels` maps (e.g., a slide refers to a chart file that isn't declared).
2064
- 2. Duplicate `<a16:rowId>` elements on a slide (caused by cloning rows raw without updating hashes).
2065
- * **Fix**: Ensure you always use the built-in `saveToFile()` or `toBuffer()` methods, which automatically execute validation checks, repair relationship indexes, and sanitize content structures.
2066
-
2067
- ### Q: A text placeholder is not replacing. How do I debug it?
2068
- * **Root Cause**: PowerPoint might have split your placeholder into multiple runs (e.g., `{{` and `placeholder}}`).
2069
- * **Fix**:
2070
- 1. Enable debug logging to locate the fragment: `PPTX_LOG_LEVEL=debug node app.js`.
2071
- 2. In PowerPoint, select the placeholder text box, cut it, and paste it back using the **"Keep Text Only"** option. This unifies the run.
2072
-
2073
- ---
2074
-
2075
- ## ⚡ Performance Optimization & Caching APIs
2076
-
2077
- The library provides first-class support for memory optimization, template caching, lazy loading, and streaming saves.
2078
-
2079
- ### 1. In-Memory Template Caching (IIS & Server Environments)
2080
- Instead of loading and parsing the PPTX ZIP structure from disk on every request, preload the template once. Subsequent templates can be instantiated from the cache in **0ms**:
2081
-
2082
- ```javascript
2083
- const { PPTXTemplater } = require('node-pptx-templater');
2084
-
2085
- // Preload templates into memory cache at server startup
2086
- await PPTXTemplater.preload('./templates/report.pptx');
2087
-
2088
- // Load from cache instantly inside request handlers
2089
- app.post('/generate-report', async (req, res) => {
2090
- const ppt = await PPTXTemplater.fromCache('./templates/report.pptx');
2091
-
2092
- ppt.useSlide(1).replaceText({ '{{title}}': req.body.title });
2093
-
2094
- const buffer = await ppt.toBuffer();
2095
- res.send(buffer);
2096
- });
2097
-
2098
- // Clear cache if templates change
2099
- PPTXTemplater.clearCache();
2100
- ```
2101
-
2102
- ### 2. Performance Profiling
2103
- Expose timing and memory metrics across the generation pipeline:
2104
-
2105
- ```javascript
2106
- const ppt = await PPTXTemplater.load('report.pptx');
2107
- ppt.enablePerformanceProfile();
2108
-
2109
- // Perform modifications...
2110
- ppt.useSlide(1).replaceText({ '{{title}}': 'Performance' });
2111
-
2112
- await ppt.toBuffer();
2113
-
2114
- // Retrieve timing statistics (in milliseconds)
2115
- const metrics = ppt.getPerformanceMetrics();
2116
- console.log(metrics);
2117
- /*
2118
- Output:
2119
- {
2120
- enabled: true,
2121
- templateLoadMs: 12.5,
2122
- parseMs: 45.2,
2123
- chartUpdateMs: 0,
2124
- imageUpdateMs: 0,
2125
- zipGenerationMs: 65.8,
2126
- totalMs: 125.4,
2127
- memoryUsedMB: 38.45
2128
- }
2129
- */
2130
- ```
2131
-
2132
- ### 3. Configurable ZIP Compression
2133
- Balance CPU execution time and file size when saving the presentation. Compression options support `'none' | 'fast' | 'balanced' | 'maximum'`:
2134
-
2135
- ```javascript
2136
- // balanced is the default (level 6 DEFLATE)
2137
- await ppt.save('output.pptx', { compression: 'balanced' });
2138
-
2139
- // maximum compression (level 9 DEFLATE) - best file size, slightly slower
2140
- await ppt.save('output.pptx', { compression: 'maximum' });
2141
-
2142
- // fast compression (level 1 DEFLATE) - fast packaging, good compression
2143
- await ppt.save('output.pptx', { compression: 'fast' });
2144
-
2145
- // none / store (0% compression) - extremely fast, skips compression entirely
2146
- const fastBuffer = await ppt.toBuffer({ compression: 'none' });
2147
- ```
2148
-
2149
- ### 4. Streaming Save & Streaming Image Input
2150
- Avoid buffering large output files in memory by saving directly to readable/writable streams. You can also pass Readable streams (like `fs.createReadStream`) directly to image APIs:
2151
-
2152
- ```javascript
2153
- const fs = require('fs');
2154
-
2155
- const ppt = await PPTXTemplater.load('report.pptx');
2156
-
2157
- // Stream image from file path without loading into memory buffer
2158
- const imageStream = fs.createReadStream('large-image.png');
2159
- await ppt.useSlide(1).replaceImage('placeholder-img', imageStream);
250
+ const result = await ppt.validatePresentation();
251
+ // → { valid: true, errors: [], warnings: [] }
2160
252
 
2161
- // Stream final PPTX directly to file disk or HTTP response
2162
- const writeStream = fs.createWriteStream('output.pptx');
2163
- await ppt.saveToStream(writeStream);
253
+ await ppt.repair(); // Auto-fix orphan relationships
254
+ await ppt.repairCharts(); // Fix broken chart workbook references
2164
255
  ```
2165
256
 
2166
257
  ---
2167
258
 
2168
- ## 🌐 IIS & Windows Server Deployment Guide
259
+ ## Migration from v1.0.x
2169
260
 
2170
- When deploying the library on **Windows Server** with **IIS** using `httpPlatformHandler` or `iisnode`, follow these production-ready recommendations:
261
+ No breaking changes. All existing APIs continue to work unchanged.
2171
262
 
2172
- 1. **Preload Large Templates**: Always call `await PPTXTemplater.preload(templatePath)` during application startup. This avoids high IIS request queue concurrency from competing for file handles or causing disk bottlenecks.
2173
- 2. **Use Streaming Saves**: For concurrent routes serving large PPTX outputs, use `saveToStream()` to stream data straight into the HTTP response stream rather than buffering the output as large Node buffers.
2174
- 3. **Optimize Compression**: If CPU cycles are a bottleneck on the IIS worker process, set `{ compression: 'fast' }` or `{ compression: 'none' }` on your save options.
2175
- 4. **Increase httpPlatformHandler Request Limits**: Ensure the `requestTimeout` and `maxConnections` settings in your IIS `web.config` are set appropriately to allow long-running file streaming tasks.
263
+ **New in v1.1.0:**
264
+ - `PPTXTemplater.load(path, { logLevel })` configure logging at load time
265
+ - `PPTXTemplater.setLogLevel(level)` global log level control
266
+ - `ppt.enableDebug()` instance debug shortcut
267
+ - `ppt.getTableRows(tableId, options)` — table data extraction
268
+ - `ppt.addTableRow(tableId, data, { mergeStrategy })` — nested row support
269
+ - `setGlobalLogLevel` / `resetLogLevel` now exported from public API
270
+ - 8 new example files
271
+ - `npm run docs:validate` — documentation validation
272
+ - `npm run docs:inventory` — feature inventory generation
2176
273
 
2177
274
  ---
2178
275
 
2179
- ## 🤝 Contributing
276
+ ## Contributing
2180
277
 
2181
- We welcome contributions from the community. Please read our [CONTRIBUTING.md](./CONTRIBUTING.md) to set up the development environment, format code, and submit Pull Requests.
278
+ Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) and open a pull request.
2182
279
 
2183
280
  ```bash
2184
281
  git clone https://github.com/jsuyog2/node-pptx-templater.git
@@ -2189,6 +286,6 @@ npm test
2189
286
 
2190
287
  ---
2191
288
 
2192
- ## 📄 License
289
+ ## License
2193
290
 
2194
- Licensed under the MIT License. © node-pptx-templater contributors.
291
+ [MIT](./LICENSE) © node-pptx-templater contributors