node-pptx-templater 1.0.5 → 1.0.7

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,38 +1,45 @@
1
- # node-pptx-templater
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%">
3
+ </p>
2
4
 
3
- > High-performance, low-level PowerPoint (PPTX) OpenXML template engine for Node.js. Dynamically replace text, insert images, update charts (with Excel workbook data caching), and merge table cells without PowerPoint corruption or Repair Mode prompts.
5
+ <h1 align="center">node-pptx-templater (PPTXForge Engine)</h1>
4
6
 
5
- [![npm version](https://img.shields.io/npm/v/node-pptx-templater.svg?style=flat-square&color=blue)](https://www.npmjs.com/package/node-pptx-templater)
6
- [![CI Build Status](https://img.shields.io/github/actions/workflow/status/jsuyog2/node-pptx-templater/ci.yml?branch=main&style=flat-square)](https://github.com/jsuyog2/node-pptx-templater/actions/workflows/ci.yml)
7
- [![Bundle Size](https://img.shields.io/bundlephobia/min/node-pptx-templater?style=flat-square&color=brightgreen)](https://bundlephobia.com/package/node-pptx-templater)
8
- [![Downloads](https://img.shields.io/npm/dm/node-pptx-templater.svg?style=flat-square&color=orange)](https://www.npmjs.com/package/node-pptx-templater)
9
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg?style=flat-square)](./LICENSE)
10
- [![Node.js Version](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen?style=flat-square)](https://nodejs.org)
7
+ <p align="center">
8
+ <strong>The High-Performance, Secure OpenXML PowerPoint Template Engine for Node.js</strong>
9
+ </p>
10
+
11
+ <p align="center">
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>
15
+ <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>
18
+ </p>
11
19
 
12
20
  ---
13
21
 
14
- ## ⚡ Why node-pptx-templater?
22
+ ## ⚡ Why PPTXForge (`node-pptx-templater`)?
15
23
 
16
- Traditional PowerPoint generation libraries require building slides from scratch in code, which is verbose, hard to maintain, and strips away the power of visual design tools.
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.
17
25
 
18
- `node-pptx-templater` takes a different approach: **Design visually in PowerPoint, populate dynamically in Node.js.**
26
+ **PPTXForge takes a visual-first approach: Design visually in PowerPoint, populate dynamically in Node.js.**
19
27
 
20
- You create slide decks using PowerPoint, Google Slides, or Keynote, set your formatting, themes, animations, and layouts, and place placeholders like `{{company}}` or `{{revenue-chart}}`. `node-pptx-templater` parses the template and updates text, injects images, replaces chart values (updating both Excel workbook data caches and XML shapes), and merges tables dynamically while keeping the presentation 100% compliant with standard OpenXML guidelines.
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.
21
29
 
22
30
  ---
23
31
 
24
- ## ✨ Features
32
+ ## ✨ Enterprise-Grade Core Features
25
33
 
26
- - 🏗️ **Zero Native Office/Java Dependencies**: Runs on pure Javascript/Node.js, making it ideal for high-throughput cloud environments, Lambda, or serverless runtimes.
27
- - 🔁 **Fragmented Placeholder Resolution**: PowerPoint often splits text runs like `{{company}}` into `<a:r>` nodes. Our engine merges and resolves fragmented tags automatically.
28
- - 📊 **Full Chart Engine Integration**: Supports Bar, Column, Line, Pie, Doughnut, Area, Scatter, and Bubble charts. Automatically synchronizes chart XML properties and coordinates with the embedded Excel sheets (`ppt/embeddings/`).
29
- - 📋 **Flexible Table Merging & Templating**:
30
- - Horizontal column merge (`gridSpan` & `hMerge`), vertical row merge (`rowSpan` & `vMerge`), and rectangular block merges.
31
- - Formats cells dynamically with inline options (`align`, `fontSize`, `fill`).
32
- - Automatically handles slide table duplicates by generating unique `<a16:rowId>` 32-bit hashes to **prevent PowerPoint Repair Mode** screens.
33
- - 🎨 **Shape & Image Manipulation**: Find shapes, clone layout blocks with offsets, replace image sources while keeping exact positions, or delete elements.
34
- - 🎯 **Slide Management Operations**: Duplicate, reorder, delete, and import slides from external templates with automatic media asset deduplication.
35
- - 🔍 **Deep Packaging Integrity Validation**: Real-time checking of relationships, XML schemas, table column numbers, and override duplicates.
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.
36
43
 
37
44
  ---
38
45
 
@@ -44,317 +51,1390 @@ npm install node-pptx-templater
44
51
 
45
52
  ---
46
53
 
47
- ## 🚀 Quick Start
54
+ ## 🚀 Quick Start Guide
48
55
 
49
- Get up and running in under 60 seconds with this simple template rendering example:
56
+ Generate a formatted presentation report from a visually designed template in under 60 seconds:
50
57
 
51
- ```js
58
+ ```javascript
52
59
  const { PPTXTemplater } = require('node-pptx-templater');
53
60
 
54
- async function main() {
55
- // 1. Load your PowerPoint presentation template
61
+ async function generateReport() {
62
+ // 1. Load the presentation template
56
63
  const ppt = await PPTXTemplater.load('monthly_report_template.pptx');
57
64
 
58
- // 2. Select slide 1 and execute operations
65
+ // 2. Select slide 1 and execute standard text replacements
59
66
  ppt.useSlide(1)
60
- .replaceTextByTag('title', 'Quarterly Earnings Report')
67
+ .replaceTextByTag('title', 'Q2 Business Overview')
61
68
  .replaceMultiple({
62
- company: 'Acme Corporation',
63
- year: '2026'
69
+ client: 'Acme Corporation',
70
+ analyst: 'Sarah Jenkins',
71
+ date: 'June 2026'
64
72
  });
65
73
 
66
- // 3. Update chart series data on Slide 2
74
+ // 3. Update Excel-backed chart data on Slide 2
67
75
  ppt.useSlide(2)
68
- .updateChartData('sales-chart', {
69
- categories: ['Q1', 'Q2', 'Q3', 'Q4'],
76
+ .updateChartData('revenue-chart', {
77
+ categories: ['April', 'May', 'June'],
70
78
  series: [
71
- { name: 'Target', values: [100, 120, 140, 160] },
72
- { name: 'Revenue', values: [105, 118, 145, 172] }
79
+ { name: 'Target', values: [120000, 150000, 180000] },
80
+ { name: 'Actual', values: [125000, 162000, 195000] }
73
81
  ]
74
82
  });
75
83
 
76
- // 4. Update table with cell merging and formatting on Slide 3
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
77
94
  ppt.useSlide(3)
78
- .updateTable('sales-table', [
79
- ['Region', 'Q1 Actual', 'Q2 Actual', 'Status'],
80
- ['North', '120k', '140k', { value: 'On Track', align: 'ctr', fill: '10b981' }],
81
- ['South', '95k', '110k', { value: 'Review', align: 'ctr', fill: 'f59e0b' }]
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' }, '']
82
99
  ]);
83
100
 
84
- // 5. Save the non-corrupted PPTX back to disk
85
- await ppt.saveToFile('./output/annual_earnings.pptx');
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!');
86
110
  }
87
111
 
88
- main().catch(err => console.error(err));
112
+ generateReport().catch(console.error);
89
113
  ```
90
114
 
91
115
  ---
92
116
 
93
- ## 🏗️ OpenXML Architecture & Internals
94
-
95
- A `.pptx` file is an OPC (Open Packaging Convention) ZIP archive containing structured XML documents and asset folders:
117
+ ## 📋 OpenXML Presentation Architecture
118
+
119
+ A `.pptx` file is an Open Packaging Convention (OPC) ZIP package containing structured XML schemas:
120
+
121
+ ```text
122
+ PPTX Archive Structure:
123
+ ├── [Content_Types].xml # MIME type registry for all zip contents
124
+ ├── _rels/
125
+ │ └── .rels # Package-level relationship mappings
126
+ └── ppt/
127
+ ├── presentation.xml # Global presentation slide order and layouts
128
+ ├── slides/
129
+ │ ├── slide1.xml # DrawingML elements, text runs, and shapes
130
+ │ └── _rels/
131
+ │ └── slide1.xml.rels # Resource mappings (images, charts, tables)
132
+ ├── media/ # Image and SVG database
133
+ └── embeddings/ # Embedded Excel books backing charts
134
+ ```
96
135
 
97
- - `[Content_Types].xml` Global manifest declaring content MIME types for every file part in the ZIP.
98
- - `_rels/.rels` Root-level package relationship index.
99
- - `ppt/presentation.xml` – Root presentation settings and slide inventory (`sldIdLst`).
100
- - `ppt/slides/slideN.xml` Main slide canvas storing shapes, lines, tables, text runs, and layout components.
101
- - `ppt/slides/_rels/slideN.xml.rels` – Relationship indexes mapping slide XML components to charts, layouts, and image assets.
136
+ ### Unifying Text Run Segmentations
137
+ 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:
138
+ ```xml
139
+ <!-- Split layout generated by PowerPoint -->
140
+ <a:r><a:t>{{n</a:t></a:r>
141
+ <a:r><a:t>ame}}</a:t></a:r>
142
+ ```
143
+ 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.
102
144
 
103
- ### Preventing PowerPoint Table Repair Errors
104
- PowerPoint slide tables utilize unique 32-bit identifiers inside `<a16:rowId>` nodes for collaborative edits. Duplicating rows using naive array copy operations results in overlapping IDs, triggering Microsoft PowerPoint's **"PowerPoint found a problem with content"** repair screen on open.
105
- `node-pptx-templater` intercepts all table operations (adding, cloning, inserting, or merging rows) and dynamically injects newly generated unique `rowId` hashes, ensuring a seamless, warning-free loading experience in:
106
- - Microsoft PowerPoint (Desktop, Mac, Online)
107
- - Google Slides
108
- - LibreOffice Impress
145
+ ### Preventing PPTX "Repair Presentation" Warnings
146
+ 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.
109
147
 
110
148
  ---
111
149
 
112
150
  ## 📊 Feature Comparison Matrix
113
151
 
114
- | Feature / Library | `node-pptx-templater` | `pptxgenjs` | `pptx-template` | `pptx-automizer` | `officegen` |
115
- |:---|:---:|:---:|:---:|:---:|:---:|
116
- | **Approach** | **Template-based** | Code-based | Template-based | Template-based | Code-based |
117
- | **No PPTX Corruption / Repair Warnings** | **Yes** (Automatic Metadata Sync) | Yes | No (Fragile row duplication) | Yes | Yes (Limited layouts) |
118
- | **Text Run Fragmentation Resolution** | **Yes** (Dynamic merging) | N/A | No (Placeholder breaks) | Yes | N/A |
119
- | **Chart Data Workbook Sync** | **Yes** (Direct excel caching) | Yes | No (Only raw XML text) | Yes | Yes |
120
- | **Horizontal & Vertical Cell Merge** | **Yes** (gridSpan, rowSpan, hMerge, vMerge) | Yes | No | No | No |
121
- | **Slide Duplication & Reordering** | **Yes** | No | No | Yes | No |
122
- | **External Slide Imports** | **Yes** (With asset deduplication) | No | No | Yes | No |
123
- | **Dependencies** | **Zero Native Dependencies** | Zero | Zero | Zero | Node-zip, xmlbuilder |
152
+ Compare PPTXForge with other popular PowerPoint automation libraries:
124
153
 
125
- ---
154
+ | Feature / Metric | **node-pptx-templater** (PPTXForge) | **pptxgenjs** | **pptx-template** | **pptx-automizer** | **officegen** |
155
+ | :--- | :---: | :---: | :---: | :---: | :---: |
156
+ | **Automation Flow** | **Template-based** | Code-based | Template-based | Template-based | Code-based |
157
+ | **No Office/Java Dependency** | **Yes** (Pure JS) | Yes | Yes | Yes | Yes |
158
+ | **Bypass Corruption/Repair Alerts** | **Yes** (RowId sync) | Yes | No (Row duplicate breaks) | Yes | Yes |
159
+ | **Heal Text Run Fragmentation** | **Yes** | N/A | No (Tags break) | Yes | N/A |
160
+ | **Synchronized Excel Chart Updates** | **Yes** (Direct sync) | Yes | No (Text updates only) | Yes | Yes |
161
+ | **Horizontal & Vertical Cell Merge** | **Yes** (gridSpan/rowSpan) | Yes | No | No | No |
162
+ | **Z-Order Layer Reordering** | **Yes** (Front/Back) | No | No | Yes | No |
163
+ | **External Slide Imports** | **Yes** (Deduplicated) | No | No | Yes | No |
164
+
165
+ <!-- API_REFERENCE_START -->
126
166
 
127
- ## 📚 API Reference
167
+ ### Tables API
128
168
 
129
- ### Slide Operations
169
+ #### `updateTable(tableId, rows)`
170
+ Replaces table rows with new data in the selected slide(s). Preserves borders, merged cells, fonts, colors, and alignment from the template.
130
171
 
131
- #### `duplicateSlide(slideIndex, atPosition)`
132
- Duplicates a slide.
133
- ```js
134
- ppt.duplicateSlide(1, 2); // Duplicate Slide 1 and insert it at position 2
172
+ * **Arguments**:
173
+ * `tableId` (`string`): Table name or shape ID.
174
+ * `rows` (`string[][]`): 2D array of cell values (row × col).
175
+ * **Returns**: `PPTXTemplater` - this (chainable)
176
+
177
+ ```javascript
178
+ ppt.useSlide(1).updateTable('summary-table', [
179
+ ['Item', 'Value'],
180
+ ['Widgets', '1,200'],
181
+ ['Gadgets', '850']
182
+ ]);
135
183
  ```
136
184
 
137
- #### `deleteSlide(slideIndex)`
138
- Deletes a slide from the deck.
139
- ```js
140
- ppt.deleteSlide(3); // Delete Slide 3
185
+ #### `addTableRow(())`
186
+ Delegates core actions to slide element sub-managers.
187
+
188
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
189
+
190
+ ```javascript
191
+ ppt.useSlide(1).addTableRow('data-table', ['John Doe', 'Sales Manager', '$120k']);
141
192
  ```
142
193
 
143
- #### `moveSlide(fromIndex, toIndex)`
144
- Moves a slide to a new position.
145
- ```js
146
- ppt.moveSlide(1, 3); // Move Slide 1 to position 3
194
+ #### `removeTableRow(())`
195
+ Delegates core actions to slide element sub-managers.
196
+
197
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
198
+
199
+ ```javascript
200
+ ppt.useSlide(1).removeTableRow('data-table', 2);
147
201
  ```
148
202
 
149
- #### `importSlideFrom(sourcePresentation, sourceSlideIndex)`
150
- Deep-copies a slide from another loaded presentation, automatically remapping layouts, shapes, charts, and deduplicating media assets.
151
- ```js
152
- const source = await PPTXTemplater.load('marketing_slides.pptx');
153
- await ppt.importSlideFrom(source, 2); // Import Slide 2 of marketing deck
203
+ #### `insertTableRow(())`
204
+ Delegates core actions to slide element sub-managers.
205
+
206
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
207
+
208
+ ```javascript
209
+ ppt.useSlide(1).insertTableRow(());
210
+ ```
211
+
212
+ #### `cloneTableRow(())`
213
+ Delegates core actions to slide element sub-managers.
214
+
215
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
216
+
217
+ ```javascript
218
+ ppt.useSlide(1).cloneTableRow(());
219
+ ```
220
+
221
+ #### `updateCell(())`
222
+ Delegates core actions to slide element sub-managers.
223
+
224
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
225
+
226
+ ```javascript
227
+ ppt.useSlide(1).updateCell(());
228
+ ```
229
+
230
+ #### `mergeCells(())`
231
+ Delegates core actions to slide element sub-managers.
232
+
233
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
234
+
235
+ ```javascript
236
+ ppt.useSlide(1).mergeCells('metrics-table', 1, 1, 2, 2);
237
+ ```
238
+
239
+ #### `unmergeCells(())`
240
+ Delegates core actions to slide element sub-managers.
241
+
242
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
243
+
244
+ ```javascript
245
+ ppt.useSlide(1).unmergeCells('metrics-table', 1, 1, 2, 2);
246
+ ```
247
+
248
+ #### `getMergedCells(())`
249
+ Delegates core actions to slide element sub-managers.
250
+
251
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
252
+
253
+ ```javascript
254
+ ppt.useSlide(1).getMergedCells(());
255
+ ```
256
+
257
+ #### `validateMergeRegion(())`
258
+ Delegates core actions to slide element sub-managers.
259
+
260
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
261
+
262
+ ```javascript
263
+ ppt.useSlide(1).validateMergeRegion(());
264
+ ```
265
+
266
+ #### `isMergedCell(())`
267
+ Delegates core actions to slide element sub-managers.
268
+
269
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
270
+
271
+ ```javascript
272
+ ppt.useSlide(1).isMergedCell(());
273
+ ```
274
+
275
+ #### `getMergeParent(())`
276
+ Delegates core actions to slide element sub-managers.
277
+
278
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
279
+
280
+ ```javascript
281
+ ppt.useSlide(1).getMergeParent(());
282
+ ```
283
+
284
+ #### `getMergeRegion(())`
285
+ Delegates core actions to slide element sub-managers.
286
+
287
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
288
+
289
+ ```javascript
290
+ ppt.useSlide(1).getMergeRegion(());
291
+ ```
292
+
293
+ #### `splitMergedRegion(())`
294
+ Delegates core actions to slide element sub-managers.
295
+
296
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
297
+
298
+ ```javascript
299
+ ppt.useSlide(1).splitMergedRegion(());
300
+ ```
301
+
302
+ #### `cloneMergedRegion(())`
303
+ Delegates core actions to slide element sub-managers.
304
+
305
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
306
+
307
+ ```javascript
308
+ ppt.useSlide(1).cloneMergedRegion(());
309
+ ```
310
+
311
+ #### `autoFitTable(())`
312
+ Delegates core actions to slide element sub-managers.
313
+
314
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
315
+
316
+ ```javascript
317
+ ppt.useSlide(1).autoFitTable(());
318
+ ```
319
+
320
+ #### `resizeTable(())`
321
+ Delegates core actions to slide element sub-managers.
322
+
323
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
324
+
325
+ ```javascript
326
+ ppt.useSlide(1).resizeTable(());
327
+ ```
328
+
329
+ #### `getTables(())`
330
+ Delegates core actions to slide element sub-managers.
331
+
332
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
333
+
334
+ ```javascript
335
+ ppt.useSlide(1).getTables(());
154
336
  ```
155
337
 
156
338
  ---
157
339
 
158
- ### Table Manipulation
340
+ ### Charts API
159
341
 
160
- #### `updateTable(tableId, data)`
161
- Updates a table with rows data, merge rules, and cell styles.
162
- ```js
163
- ppt.updateTable('revenue-table', [
164
- ['Year', 'Revenue', 'Profit'],
165
- ['2025', '120k', '40k'],
166
- ['2026', '150k', { value: '60k', fill: '10b981', align: 'ctr' }]
167
- ]);
342
+ #### `updateChart(chartId, data)`
343
+ 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.
344
+
345
+ * **Arguments**:
346
+ * `chartId` (`string`): Chart name or relationship ID.
347
+ * `data` (`ChartData`): New chart data.
348
+ * `data.categories` (`string[]`): Category labels (X-axis).
349
+ * `data.series` (`SeriesData[]`): Data series array.
350
+ * `data.series[].name` (`string`): Series name.
351
+ * `data.series[].values` (`number[]|object[]`): Data values (numbers or label objects).
352
+ * **Returns**: `PPTXTemplater` - this (chainable)
353
+
354
+ ```javascript
355
+ ppt.useSlide(1).updateChart(chartId, data);
356
+ ```
357
+
358
+ #### `validateCharts()`
359
+ Validates all charts in the presentation to ensure they are not corrupted. Checks XML, caches, and embedded workbook references.
360
+
361
+ * **Returns**: `Promise<Object>` - Validation results for charts.
362
+
363
+ ```javascript
364
+ ppt.useSlide(1).validateCharts();
168
365
  ```
169
366
 
170
- #### `mergeCells(options)`
171
- Merges a rectangular block of cells. Supports horizontal, vertical, and block merging, concatenating all text from the merged region into the top-left cell.
172
- ```js
173
- ppt.mergeCells({
174
- tableId: 'sales-table',
175
- startRow: 1,
176
- startCol: 1,
177
- endRow: 2,
178
- endCol: 2
367
+ #### `repairCharts()`
368
+ Repairs common chart corruption issues such as broken caches, missing embedded workbooks, or orphan nodes.
369
+
370
+ * **Returns**: `Promise<PPTXTemplater>` - this
371
+
372
+ ```javascript
373
+ ppt.useSlide(1).repairCharts();
374
+ ```
375
+
376
+ #### `updateChartData(())`
377
+ Delegates core actions to slide element sub-managers.
378
+
379
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
380
+
381
+ ```javascript
382
+ ppt.useSlide(1).updateChartData('sales-chart', {
383
+ categories: ['Q1', 'Q2'],
384
+ series: [{ name: 'Actual', values: [100, 150] }]
179
385
  });
180
386
  ```
181
387
 
182
- #### `unmergeCells(options)`
183
- Splits a merged region back to its original individual cells, removing `gridSpan`, `rowSpan`, `hMerge`, and `vMerge` attributes.
184
- ```js
185
- ppt.unmergeCells({
186
- tableId: 'sales-table',
187
- row: 1,
188
- col: 1
388
+ #### `replaceChartSeries(())`
389
+ Delegates core actions to slide element sub-managers.
390
+
391
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
392
+
393
+ ```javascript
394
+ ppt.useSlide(1).replaceChartSeries(());
395
+ ```
396
+
397
+ #### `updateChartTitle(())`
398
+ Delegates core actions to slide element sub-managers.
399
+
400
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
401
+
402
+ ```javascript
403
+ ppt.useSlide(1).updateChartTitle('sales-chart', 'Quarterly Metrics Overview');
404
+ ```
405
+
406
+ #### `updateChartCategories(())`
407
+ Delegates core actions to slide element sub-managers.
408
+
409
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
410
+
411
+ ```javascript
412
+ ppt.useSlide(1).updateChartCategories(());
413
+ ```
414
+
415
+ #### `updateDataLabels(())`
416
+ Delegates core actions to slide element sub-managers.
417
+
418
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
419
+
420
+ ```javascript
421
+ ppt.useSlide(1).updateDataLabels('SalesChart', {
422
+ series: 0,
423
+ labels: ['Excellent', 'Good', 'Poor']
189
424
  });
190
425
  ```
191
426
 
192
- ---
427
+ #### `getDataLabels(())`
428
+ Delegates core actions to slide element sub-managers.
193
429
 
194
- ### Chart Integration
430
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
195
431
 
196
- #### `updateChartData(chartId, data)`
197
- Overwrites chart categories and series values. Updates the embedded Excel spreadsheet to ensure the chart matches perfectly on refresh.
198
- ```js
199
- ppt.updateChartData('sales-chart', {
200
- categories: ['Q1', 'Q2', 'Q3', 'Q4'],
201
- series: [
202
- { name: 'Revenue', values: [100, 150, 180, 220] }
203
- ]
432
+ ```javascript
433
+ const labels = await ppt.useSlide(1).getDataLabels('SalesChart', { series: 0 });
434
+ console.log(labels); // [{ point: 0, value: 'Excellent' }, ...]
435
+ ```
436
+
437
+ #### `validateDataLabels(())`
438
+ Delegates core actions to slide element sub-managers.
439
+
440
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
441
+
442
+ ```javascript
443
+ const result = await ppt.useSlide(1).validateDataLabels('SalesChart', {
444
+ labels: ['High', 'Low']
204
445
  });
446
+ console.log(result.valid);
205
447
  ```
206
448
 
207
- #### `updateChartTitle(chartId, title)`
208
- ```js
209
- ppt.updateChartTitle('sales-chart', 'Revenue Growth (2026)');
449
+ #### `getCharts(())`
450
+ Delegates core actions to slide element sub-managers.
451
+
452
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
453
+
454
+ ```javascript
455
+ ppt.useSlide(1).getCharts(());
210
456
  ```
211
457
 
212
458
  ---
213
459
 
214
- ### Z-Order (Layer Management)
460
+ ### Slides API
461
+
462
+ #### `useSlide(...slideRefs)`
463
+ 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.
215
464
 
216
- Control the stacking order of shapes, images, charts, tables, groups, and connectors on any slide — just like PowerPoint's **Bring Forward / Send Backward** panel. The Z-order directly maps to the XML element order inside the slide's `<p:spTree>`, which is what PowerPoint reads when rendering.
465
+ * **Arguments**:
466
+ * `slideRefs` (`...number|string`): Slide numbers (1-based), IDs, or tags.
467
+ * **Returns**: `PPTXTemplater` - this (chainable)
468
+
469
+ ```javascript
470
+ ppt.useSlide(1).useSlide(...slideRefs);
471
+ ```
217
472
 
218
- All operations accept either an **options object** with a `slide` key or can be chained after `useSlide()`:
473
+ #### `useAllSlides()`
474
+ Selects all slides.
219
475
 
220
- ```js
221
- // Option A — explicit slide number
222
- ppt.bringForward({ slide: 2, objectId: 'logo' });
476
+ * **Returns**: `PPTXTemplater` - this (chainable)
223
477
 
224
- // Option B — fluent chain
225
- ppt.useSlide(2).bringForward('logo');
478
+ ```javascript
479
+ ppt.useSlide(1).useAllSlides();
226
480
  ```
227
481
 
228
- #### `getObjectOrder(slideIndex)`
229
- Returns a sorted array describing every drawing element on the slide, bottom-to-top.
230
- ```js
231
- const layers = ppt.getObjectOrder(1);
232
- // [{ id: 'Background', type: 'shape', zIndex: 1 }, ...]
482
+ #### `addSlide(options = {})`
483
+ Adds a new slide to the presentation. Automatically generates required XML and relationship entries.
484
+
485
+ * **Arguments**:
486
+ * `options` (`NewSlideOptions`): Slide definition.
487
+ * `[options.title]` (`string`): Slide title text.
488
+ * `[options.layout]` (`string`): Layout name to use (default: 'blank').
489
+ * `[options.elements]` (`SlideElement[]`): Elements to add to the slide.
490
+ * **Returns**: `PPTXTemplater` - this (chainable)
491
+
492
+ ```javascript
493
+ ppt.useSlide(1).addSlide(options = {});
233
494
  ```
234
495
 
235
- #### `bringForward(options)` / `sendBackward(options)`
236
- Move an object one layer up or down.
237
- ```js
238
- ppt.bringForward({ slide: 1, objectId: 'logo' });
239
- ppt.sendBackward({ slide: 1, objectId: 'logo' });
496
+ #### `cloneSlide(sourceSlideNumber, atPosition)`
497
+ Clones an existing slide and appends it to the end (or at a position).
498
+
499
+ * **Arguments**:
500
+ * `sourceSlideNumber` (`number`): 1-based source slide number.
501
+ * `[atPosition]` (`number`): Optional position to insert (1-based). Default: append.
502
+ * **Returns**: `PPTXTemplater` - this (chainable)
503
+
504
+ ```javascript
505
+ ppt.useSlide(1).cloneSlide(sourceSlideNumber, atPosition);
240
506
  ```
241
507
 
242
- #### `bringToFront(options)` / `sendToBack(options)`
243
- Move an object to the very top or very bottom of the stack.
244
- ```js
245
- ppt.bringToFront({ slide: 1, objectId: 'logo' });
246
- ppt.sendToBack({ slide: 1, objectId: 'background' });
508
+ #### `removeSlide(slideNumber)`
509
+ Removes a slide from the presentation.
510
+
511
+ * **Arguments**:
512
+ * `slideNumber` (`number`): 1-based slide number to remove.
513
+ * **Returns**: `PPTXTemplater` - this (chainable)
514
+
515
+ ```javascript
516
+ ppt.useSlide(1).removeSlide(slideNumber);
247
517
  ```
248
518
 
249
- #### `setZIndex(options)`
250
- Place an object at an exact 1-based stacking position.
251
- ```js
252
- ppt.setZIndex({ slide: 1, objectId: 'logo', zIndex: 3 });
519
+ #### `reorderSlides(order)`
520
+ Reorders slides in the presentation.
521
+
522
+ * **Arguments**:
523
+ * `order` (`number[]`): Array of 1-based slide numbers in desired order.
524
+ * **Returns**: `PPTXTemplater` - this (chainable)
525
+
526
+ ```javascript
527
+ ppt.useSlide(1).reorderSlides(order);
253
528
  ```
254
529
 
255
- #### `moveObjectBefore(options)` / `moveObjectAfter(options)`
256
- Position an object immediately below or above a specific target.
257
- ```js
258
- ppt.moveObjectBefore({ slide: 1, objectId: 'overlay', targetId: 'chart' });
259
- ppt.moveObjectAfter({ slide: 1, objectId: 'label', targetId: 'chart' });
530
+ #### `tagSlide(slideNumber, tag)`
531
+ Tags a slide with a custom string identifier for later selection.
532
+
533
+ * **Arguments**:
534
+ * `slideNumber` (`number`): 1-based slide number.
535
+ * `tag` (`string`): Custom tag string.
536
+ * **Returns**: `PPTXTemplater` - this (chainable)
537
+
538
+ ```javascript
539
+ ppt.useSlide(1).tagSlide(slideNumber, tag);
260
540
  ```
261
541
 
262
- #### `reorderObjects(options)`
263
- Bulk-reorder the entire slide stack by specifying all object names in desired bottom-to-top order.
264
- ```js
265
- ppt.reorderObjects({
266
- slide: 1,
267
- order: ['background', 'chart', 'logo', 'title']
268
- });
542
+ #### `exportSlides(...slideNumbers)`
543
+ Exports selected slides to a new standalone PPTX engine. Useful for creating "slide decks" from a master template.
544
+
545
+ * **Arguments**:
546
+ * `slideNumbers` (`...number`): 1-based slide numbers to export.
547
+ * **Returns**: `Promise<PPTXTemplater>` - New engine with only the selected slides.
548
+
549
+ ```javascript
550
+ ppt.useSlide(1).exportSlides(...slideNumbers);
269
551
  ```
270
552
 
271
- #### `applyZOrder(slideIndex, configs)`
272
- Apply multiple stacking rules in a single call. Operations are executed sequentially.
273
- ```js
274
- ppt.applyZOrder(1, [
275
- { id: 'background', sendToBack: true },
276
- { id: 'overlay', zIndex: 2 },
277
- { id: 'logo', bringToFront: true },
278
- ]);
553
+ #### `importSlideFrom(sourceEngine, slideRef)`
554
+ Imports a single slide from another PPTXTemplater instance into this presentation. Preserves all slide layouts, charts, relationships, and embedded media.
555
+
556
+ * **Arguments**:
557
+ * `sourceEngine` (`PPTXTemplater`): Source PPTXTemplater instance.
558
+ * `slideRef` (`number|string`): Slide index (1-based), ID, or custom tag.
559
+ * **Returns**: `Promise<PPTXTemplater>` - this (chainable)
560
+
561
+ ```javascript
562
+ const source = await PPTXTemplater.load('template2.pptx');
563
+ await ppt.importSlideFrom(source, 1);
564
+ ```
565
+
566
+ #### `importSlides(slideIndices)`
567
+ 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.
568
+
569
+ * **Arguments**:
570
+ * `slideIndices` (`number[]`): Array of 1-based slide indices to keep.
571
+ * **Returns**: `PPTXTemplater` - this (chainable)
572
+
573
+ ```javascript
574
+ ppt.useSlide(1).importSlides(slideIndices);
575
+ ```
576
+
577
+ #### `duplicateSlide(())`
578
+ Delegates core actions to slide element sub-managers.
579
+
580
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
581
+
582
+ ```javascript
583
+ ppt.duplicateSlide(1, 2);
584
+ ```
585
+
586
+ #### `deleteSlide(())`
587
+ Delegates core actions to slide element sub-managers.
588
+
589
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
590
+
591
+ ```javascript
592
+ ppt.useSlide(1).deleteSlide(());
593
+ ```
594
+
595
+ #### `moveSlide(())`
596
+ Delegates core actions to slide element sub-managers.
597
+
598
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
599
+
600
+ ```javascript
601
+ ppt.useSlide(1).moveSlide(());
602
+ ```
603
+
604
+ #### `insertSlide(())`
605
+ Delegates core actions to slide element sub-managers.
606
+
607
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
608
+
609
+ ```javascript
610
+ ppt.useSlide(1).insertSlide(());
611
+ ```
612
+
613
+ #### `getSlides(())`
614
+ Delegates core actions to slide element sub-managers.
615
+
616
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
617
+
618
+ ```javascript
619
+ ppt.useSlide(1).getSlides(());
620
+ ```
621
+
622
+ ---
623
+
624
+ ### Text API
625
+
626
+ #### `replaceText(replacements)`
627
+ Replaces template placeholders (e.g., {{key}}) with values in the selected slides. Works inside text boxes, titles, grouped shapes, tables, and shapes.
628
+
629
+ * **Arguments**:
630
+ * `replacements` (`Object.<string, string>`): Map of placeholder → replacement value.
631
+ * **Returns**: `PPTXTemplater` - this (chainable)
632
+
633
+ ```javascript
634
+ ppt.useSlide(1).replaceText(replacements);
635
+ ```
636
+
637
+ #### `addHyperlink(options)`
638
+ Adds or replaces a hyperlink on a text run or shape.
639
+
640
+ * **Arguments**:
641
+ * `options` (`HyperlinkOptions`): Hyperlink configuration.
642
+ * `options.text` (`string`): Text to find and make clickable.
643
+ * `options.url` (`string`): Target URL.
644
+ * `[options.tooltip]` (`string`): Optional tooltip.
645
+ * **Returns**: `PPTXTemplater` - this (chainable)
646
+
647
+ ```javascript
648
+ ppt.useSlide(1).addHyperlink(options);
649
+ ```
650
+
651
+ #### `addSlideLink(options)`
652
+ Adds an inter-slide hyperlink to a specific text element.
653
+
654
+ * **Arguments**:
655
+ * `options` (`Object`): Link configuration.
656
+ * `options.sourceSlide` (`number`): Source slide number (1-based).
657
+ * `options.targetSlide` (`number`): Destination slide number (1-based).
658
+ * `options.element` (`string`): Text element to make clickable.
659
+ * **Returns**: `PPTXTemplater` - this (chainable)
660
+
661
+ ```javascript
662
+ ppt.useSlide(1).addSlideLink(options);
663
+ ```
664
+
665
+ #### `addImageLink(options)`
666
+ Adds an inter-slide hyperlink to an image.
667
+
668
+ * **Arguments**:
669
+ * `options` (`Object`):
670
+ * `options.slide` (`number`): Source slide number.
671
+ * `options.imageId` (`string`): Image name/id to make clickable.
672
+ * `options.targetSlide` (`number`): Destination slide number.
673
+ * **Returns**: `PPTXTemplater` - this
674
+
675
+ ```javascript
676
+ ppt.useSlide(1).addImageLink(options);
677
+ ```
678
+
679
+ #### `addShapeLink(options)`
680
+ Adds an inter-slide hyperlink to a shape.
681
+
682
+ * **Arguments**:
683
+ * `options` (`Object`):
684
+ * `options.slide` (`number`): Source slide number.
685
+ * `options.shapeId` (`string`): Shape name/id to make clickable.
686
+ * `options.targetSlide` (`number`): Destination slide number.
687
+ * **Returns**: `PPTXTemplater` - this
688
+
689
+ ```javascript
690
+ ppt.useSlide(1).addShapeLink(options);
691
+ ```
692
+
693
+ #### `addTextNavigationLink(options)`
694
+ Adds a special navigation link (next, previous, first, last slide) to a text element.
695
+
696
+ * **Arguments**:
697
+ * `options` (`Object`):
698
+ * `options.slide` (`number`): Source slide number (1-based).
699
+ * `options.element` (`string`): Text element to make clickable.
700
+ * `options.action` (`'next'|'previous'|'first'|'last'`): Navigation action type.
701
+ * **Returns**: `PPTXTemplater` - this (chainable)
702
+
703
+ ```javascript
704
+ ppt.useSlide(1).addTextNavigationLink(options);
705
+ ```
706
+
707
+ #### `addShapeNavigationLink(options)`
708
+ Adds a special navigation link (next, previous, first, last slide) to a shape or image.
709
+
710
+ * **Arguments**:
711
+ * `options` (`Object`):
712
+ * `options.slide` (`number`): Source slide number (1-based).
713
+ * `options.shapeId` (`string`): Shape name/id to make clickable.
714
+ * `options.action` (`'next'|'previous'|'first'|'last'`): Navigation action type.
715
+ * **Returns**: `PPTXTemplater` - this (chainable)
716
+
717
+ ```javascript
718
+ ppt.useSlide(1).addShapeNavigationLink(options);
719
+ ```
720
+
721
+ #### `replaceTextByTag(())`
722
+ Delegates core actions to slide element sub-managers.
723
+
724
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
725
+
726
+ ```javascript
727
+ ppt.useSlide(1).replaceTextByTag('company', 'Acme Corp');
728
+ ```
729
+
730
+ #### `replaceMultiple(())`
731
+ Delegates core actions to slide element sub-managers.
732
+
733
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
734
+
735
+ ```javascript
736
+ ppt.useSlide(1).replaceMultiple(());
737
+ ```
738
+
739
+ #### `findText(())`
740
+ Delegates core actions to slide element sub-managers.
741
+
742
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
743
+
744
+ ```javascript
745
+ ppt.useSlide(1).findText(());
746
+ ```
747
+
748
+ #### `getTextElements(())`
749
+ Delegates core actions to slide element sub-managers.
750
+
751
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
752
+
753
+ ```javascript
754
+ ppt.useSlide(1).getTextElements(());
755
+ ```
756
+
757
+ ---
758
+
759
+ ### Images API
760
+
761
+ #### `replaceImage(())`
762
+ Delegates core actions to slide element sub-managers.
763
+
764
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
765
+
766
+ ```javascript
767
+ await ppt.useSlide(1).replaceImage('logo-img', './new-logo.png');
768
+ ```
769
+
770
+ #### `addImage(())`
771
+ Delegates core actions to slide element sub-managers.
772
+
773
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
774
+
775
+ ```javascript
776
+ ppt.useSlide(1).addImage(());
777
+ ```
778
+
779
+ #### `removeImage(())`
780
+ Delegates core actions to slide element sub-managers.
781
+
782
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
783
+
784
+ ```javascript
785
+ ppt.useSlide(1).removeImage(());
786
+ ```
787
+
788
+ #### `getImages(())`
789
+ Delegates core actions to slide element sub-managers.
790
+
791
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
792
+
793
+ ```javascript
794
+ ppt.useSlide(1).getImages(());
795
+ ```
796
+
797
+ ---
798
+
799
+ ### Shapes API
800
+
801
+ #### `updateShapeText(())`
802
+ Delegates core actions to slide element sub-managers.
803
+
804
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
805
+
806
+ ```javascript
807
+ ppt.useSlide(1).updateShapeText(());
808
+ ```
809
+
810
+ #### `cloneShape(())`
811
+ Delegates core actions to slide element sub-managers.
812
+
813
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
814
+
815
+ ```javascript
816
+ ppt.useSlide(1).cloneShape('card-bg', 'card-bg-2');
817
+ ```
818
+
819
+ #### `deleteShape(())`
820
+ Delegates core actions to slide element sub-managers.
821
+
822
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
823
+
824
+ ```javascript
825
+ ppt.useSlide(1).deleteShape(());
826
+ ```
827
+
828
+ #### `getShapes(())`
829
+ Delegates core actions to slide element sub-managers.
830
+
831
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
832
+
833
+ ```javascript
834
+ ppt.useSlide(1).getShapes(());
835
+ ```
836
+
837
+ ---
838
+
839
+ ### Layer Stacking (Z-Order)
840
+
841
+ #### `bringForward(optionsOrId)`
842
+ Moves slide element one layer forward.
843
+
844
+
845
+ ```javascript
846
+ ppt.useSlide(1).bringForward(optionsOrId);
847
+ ```
848
+
849
+ #### `sendBackward(optionsOrId)`
850
+ Moves slide element one layer backward.
851
+
852
+
853
+ ```javascript
854
+ ppt.useSlide(1).sendBackward(optionsOrId);
279
855
  ```
280
856
 
281
- #### `swapObjects(slideIndex, objectId1, objectId2)`
282
- Exchange the stacking positions of two objects.
283
- ```js
284
- ppt.swapObjects(1, 'logo', 'chart');
857
+ #### `bringToFront(optionsOrId)`
858
+ Moves slide element above all other objects.
859
+
860
+
861
+ ```javascript
862
+ ppt.useSlide(1).bringToFront('OverlayLogo');
285
863
  ```
286
864
 
287
- #### `sortObjects(slideIndex, compareFn)`
288
- Sort the layer stack using a custom comparator (receives `{ id, type, zIndex }` objects).
289
- ```js
290
- // Alphabetical ascending by name
291
- ppt.sortObjects(1, (a, b) => a.id.localeCompare(b.id));
865
+ #### `sendToBack(optionsOrId)`
866
+ Moves slide element behind all other objects.
867
+
868
+
869
+ ```javascript
870
+ ppt.useSlide(1).sendToBack(optionsOrId);
292
871
  ```
293
872
 
294
- #### `getTopMostObject(slideIndex)` / `getBottomMostObject(slideIndex)`
295
- Retrieve metadata for the topmost or bottommost element.
296
- ```js
297
- const top = ppt.getTopMostObject(1); // { id: 'logo', type: 'image', zIndex: 5 }
298
- const bottom = ppt.getBottomMostObject(1); // { id: 'background', type: 'shape', zIndex: 1 }
873
+ #### `setZIndex(optionsOrId, zIndex)`
874
+ Moves slide element to the specific 1-based stacking position.
875
+
876
+
877
+ ```javascript
878
+ ppt.useSlide(1).setZIndex(optionsOrId, zIndex);
879
+ ```
880
+
881
+ #### `moveObjectBefore(optionsOrId, targetId)`
882
+ Moves slide element directly before (below) a target element.
883
+
884
+
885
+ ```javascript
886
+ ppt.useSlide(1).moveObjectBefore(optionsOrId, targetId);
887
+ ```
888
+
889
+ #### `moveObjectAfter(optionsOrId, targetId)`
890
+ Moves slide element directly after (above) a target element.
891
+
892
+
893
+ ```javascript
894
+ ppt.useSlide(1).moveObjectAfter(optionsOrId, targetId);
895
+ ```
896
+
897
+ #### `reorderObjects(optionsOrOrder)`
898
+ Reorders slide objects exactly as specified in the array.
899
+
900
+
901
+ ```javascript
902
+ ppt.useSlide(1).reorderObjects(optionsOrOrder);
903
+ ```
904
+
905
+ #### `getObjectOrder(slideIndex)`
906
+ Gets the ordered metadata of all objects on the slide.
907
+
908
+
909
+ ```javascript
910
+ ppt.useSlide(1).getObjectOrder(slideIndex);
911
+ ```
912
+
913
+ #### `applyZOrder(slideOrConfigs, configsOption)`
914
+ Applies bulk template configurations for slide elements stacking layers.
915
+
916
+
917
+ ```javascript
918
+ ppt.useSlide(1).applyZOrder(slideOrConfigs, configsOption);
919
+ ```
920
+
921
+ #### `getTopMostObject(slideIndex)`
922
+ Retrieves the info of the top-most object on the slide.
923
+
924
+
925
+ ```javascript
926
+ ppt.useSlide(1).getTopMostObject(slideIndex);
927
+ ```
928
+
929
+ #### `getBottomMostObject(slideIndex)`
930
+ Retrieves the info of the bottom-most object on the slide.
931
+
932
+
933
+ ```javascript
934
+ ppt.useSlide(1).getBottomMostObject(slideIndex);
935
+ ```
936
+
937
+ #### `swapObjects(slideIndexOrId1, id1OrId2, id2)`
938
+ Swaps stacking positions of two slide objects.
939
+
940
+
941
+ ```javascript
942
+ ppt.useSlide(1).swapObjects(slideIndexOrId1, id1OrId2, id2);
943
+ ```
944
+
945
+ #### `sortObjects(slideIndexOrCompareFn, compareFnOption)`
946
+ Sorts stacking order using a custom comparison function.
947
+
948
+
949
+ ```javascript
950
+ ppt.useSlide(1).sortObjects(slideIndexOrCompareFn, compareFnOption);
299
951
  ```
300
952
 
301
953
  #### `normalizeZOrder(slideIndex)`
302
- Re-derives the Z-order directly from the current XML element order. Useful after manual XML edits or imports to reset the internal ordering state.
303
- ```js
304
- ppt.normalizeZOrder(1);
954
+ Cleans up and normalizes stacking order consistency.
955
+
956
+
957
+ ```javascript
958
+ ppt.useSlide(1).normalizeZOrder(slideIndex);
305
959
  ```
306
960
 
307
- **Supported element types:**
961
+ ---
962
+
963
+ ### Utilities & Validation
964
+
965
+ #### `const()`
966
+ 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
967
+
968
+
969
+ ```javascript
970
+ ppt.useSlide(1).const();
971
+ ```
308
972
 
309
- | PowerPoint Type | XML Tag | `type` Value |
310
- |:---|:---|:---|
311
- | Shape / Text Box | `p:sp` | `shape` / `text` |
312
- | Image | `p:pic` | `image` |
313
- | Chart | `p:graphicFrame` + chart URI | `chart` |
314
- | Table | `p:graphicFrame` + table URI | `table` |
315
- | SmartArt | `p:graphicFrame` + diagram URI | `smartart` |
316
- | Group | `p:grpSp` | `group` |
317
- | Connector | `p:cxnSp` | `connector` |
973
+ #### `class()`
974
+
975
+
976
+
977
+ ```javascript
978
+ ppt.useSlide(1).class();
979
+ ```
980
+
981
+ #### `static()`
982
+ 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.
983
+
984
+ * **Arguments**:
985
+ * `source` (`string|Buffer`): Path to PPTX file or Buffer containing PPTX data.
986
+ * **Returns**: `Promise<PPTXTemplater>` - Initialized engine instance.
987
+
988
+ ```javascript
989
+ ppt.useSlide(1).static();
990
+ ```
991
+
992
+ #### `getInfo()`
993
+ Returns presentation metadata (title, author, slide count, etc.)
994
+
995
+ * **Returns**: `PresentationInfo` - Metadata object.
996
+
997
+ ```javascript
998
+ ppt.useSlide(1).getInfo();
999
+ ```
1000
+
1001
+ #### `validate()`
1002
+ Validates the XML structure of the current PPTX. Reports issues with relationship IDs, missing parts, etc.
1003
+
1004
+ * **Returns**: `ValidationResult` - Object with `valid`, `errors`, and `warnings` arrays.
1005
+
1006
+ ```javascript
1007
+ ppt.useSlide(1).validate();
1008
+ ```
1009
+
1010
+ #### `repair()`
1011
+ Repairs corrupted OpenXML structure, relationships, and content types. Removes orphan relationships, rebuilds slide references, and fixes missing entries.
1012
+
1013
+ * **Returns**: `Promise<PPTXTemplater>` - this (chainable)
1014
+
1015
+ ```javascript
1016
+ ppt.useSlide(1).repair();
1017
+ ```
1018
+
1019
+ #### `debugRelationships()`
1020
+ Logs all relationships across the presentation to the console for debugging.
1021
+
1022
+ * **Returns**: `PPTXTemplater` - this (chainable)
1023
+
1024
+ ```javascript
1025
+ ppt.useSlide(1).debugRelationships();
1026
+ ```
1027
+
1028
+ #### `inspectSlide(slideIndex)`
1029
+ Inspects a specific slide's structure and relationships.
1030
+
1031
+ * **Arguments**:
1032
+ * `slideIndex` (`number`): 1-based slide index.
1033
+ * **Returns**: `PPTXTemplater` - this (chainable)
1034
+
1035
+ ```javascript
1036
+ ppt.useSlide(1).inspectSlide(slideIndex);
1037
+ ```
1038
+
1039
+ #### `inspectXML(xmlPath)`
1040
+ Inspects and logs the raw XML of any file in the ZIP.
1041
+
1042
+ * **Arguments**:
1043
+ * `xmlPath` (`string`): Path inside the ZIP (e.g., 'ppt/slides/slide1.xml')
1044
+ * **Returns**: `Promise<PPTXTemplater>` - this (chainable)
1045
+
1046
+ ```javascript
1047
+ ppt.useSlide(1).inspectXML(xmlPath);
1048
+ ```
1049
+
1050
+ #### `inspectChart(chartId)`
1051
+ Inspects a specific chart's metadata and structure.
1052
+
1053
+ * **Arguments**:
1054
+ * `chartId` (`string`):
1055
+
1056
+ ```javascript
1057
+ ppt.useSlide(1).inspectChart(chartId);
1058
+ ```
1059
+
1060
+ #### `inspectChartXML(chartFileName)`
1061
+ Inspects and logs the raw XML of a chart file.
1062
+
1063
+ * **Arguments**:
1064
+ * `chartFileName` (`string`):
1065
+
1066
+ ```javascript
1067
+ ppt.useSlide(1).inspectChartXML(chartFileName);
1068
+ ```
1069
+
1070
+ #### `debugChartRelationships()`
1071
+ Logs all chart relationships.
1072
+
1073
+
1074
+ ```javascript
1075
+ ppt.useSlide(1).debugChartRelationships();
1076
+ ```
1077
+
1078
+ #### `saveToFile(filePath, options = {})`
1079
+ Saves the modified PPTX to a file on disk.
1080
+
1081
+ * **Arguments**:
1082
+ * `filePath` (`string`): Output file path.
1083
+ * `[options]` (`Object`): Save options.
1084
+ * `[options.strict=false]` (`boolean`): Throw error on validation failure.
1085
+ * **Returns**: `Promise<void>` -
1086
+
1087
+ ```javascript
1088
+ ppt.useSlide(1).saveToFile(filePath, options = {});
1089
+ ```
1090
+
1091
+ #### `toBuffer()`
1092
+ Returns the PPTX content as a Node.js Buffer.
1093
+
1094
+ * **Returns**: `Promise<Buffer>` -
1095
+
1096
+ ```javascript
1097
+ ppt.useSlide(1).toBuffer();
1098
+ ```
1099
+
1100
+ #### `toStream()`
1101
+ Returns the PPTX content as a readable Node.js Stream.
1102
+
1103
+ * **Returns**: `Promise<NodeJS.ReadableStream>` -
1104
+
1105
+ ```javascript
1106
+ ppt.useSlide(1).toStream();
1107
+ ```
1108
+
1109
+ #### `slideCount()`
1110
+ Returns the total number of slides in the loaded presentation. @type {number}
1111
+
1112
+
1113
+ ```javascript
1114
+ ppt.useSlide(1).slideCount();
1115
+ ```
1116
+
1117
+ #### `function()`
1118
+ 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.
1119
+
1120
+ * **Arguments**:
1121
+ * `existingIds` (`string[]`): Array of existing rId strings (e.g., ['rId1', 'rId2']).
1122
+ * **Returns**: `string` - New relationship ID (e.g., 'rId3').
1123
+
1124
+ ```javascript
1125
+ ppt.useSlide(1).function();
1126
+ ```
1127
+
1128
+ #### `validatePresentation(())`
1129
+ Delegates core actions to slide element sub-managers.
1130
+
1131
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1132
+
1133
+ ```javascript
1134
+ ppt.useSlide(1).validatePresentation(());
1135
+ ```
1136
+
1137
+ #### `validateSlide(())`
1138
+ Delegates core actions to slide element sub-managers.
1139
+
1140
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1141
+
1142
+ ```javascript
1143
+ ppt.useSlide(1).validateSlide(());
1144
+ ```
1145
+
1146
+ #### `validateTable(())`
1147
+ Delegates core actions to slide element sub-managers.
1148
+
1149
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1150
+
1151
+ ```javascript
1152
+ ppt.useSlide(1).validateTable(());
1153
+ ```
1154
+
1155
+ #### `validateRelationships(())`
1156
+ Delegates core actions to slide element sub-managers.
1157
+
1158
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1159
+
1160
+ ```javascript
1161
+ ppt.useSlide(1).validateRelationships(());
1162
+ ```
1163
+
1164
+ #### `zipManager(())`
1165
+ Delegates core actions to slide element sub-managers.
1166
+
1167
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1168
+
1169
+ ```javascript
1170
+ ppt.useSlide(1).zipManager(());
1171
+ ```
1172
+
1173
+ #### `xmlParser(())`
1174
+ Delegates core actions to slide element sub-managers.
1175
+
1176
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1177
+
1178
+ ```javascript
1179
+ ppt.useSlide(1).xmlParser(());
1180
+ ```
1181
+
1182
+ #### `contentTypesManager(())`
1183
+ Delegates core actions to slide element sub-managers.
1184
+
1185
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1186
+
1187
+ ```javascript
1188
+ ppt.useSlide(1).contentTypesManager(());
1189
+ ```
1190
+
1191
+ #### `relationshipManager(())`
1192
+ Delegates core actions to slide element sub-managers.
1193
+
1194
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1195
+
1196
+ ```javascript
1197
+ ppt.useSlide(1).relationshipManager(());
1198
+ ```
1199
+
1200
+ #### `slideManager(())`
1201
+ Delegates core actions to slide element sub-managers.
1202
+
1203
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1204
+
1205
+ ```javascript
1206
+ ppt.useSlide(1).slideManager(());
1207
+ ```
1208
+
1209
+ #### `chartManager(())`
1210
+ Delegates core actions to slide element sub-managers.
1211
+
1212
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1213
+
1214
+ ```javascript
1215
+ ppt.useSlide(1).chartManager(());
1216
+ ```
1217
+
1218
+ #### `tableManager(())`
1219
+ Delegates core actions to slide element sub-managers.
1220
+
1221
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1222
+
1223
+ ```javascript
1224
+ ppt.useSlide(1).tableManager(());
1225
+ ```
1226
+
1227
+ #### `shapeManager(())`
1228
+ Delegates core actions to slide element sub-managers.
1229
+
1230
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1231
+
1232
+ ```javascript
1233
+ ppt.useSlide(1).shapeManager(());
1234
+ ```
1235
+
1236
+ #### `imageManager(())`
1237
+ Delegates core actions to slide element sub-managers.
1238
+
1239
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1240
+
1241
+ ```javascript
1242
+ ppt.useSlide(1).imageManager(());
1243
+ ```
1244
+
1245
+ #### `textManager(())`
1246
+ Delegates core actions to slide element sub-managers.
1247
+
1248
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1249
+
1250
+ ```javascript
1251
+ ppt.useSlide(1).textManager(());
1252
+ ```
1253
+
1254
+ #### `hyperlinkManager(())`
1255
+ Delegates core actions to slide element sub-managers.
1256
+
1257
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1258
+
1259
+ ```javascript
1260
+ ppt.useSlide(1).hyperlinkManager(());
1261
+ ```
1262
+
1263
+ #### `mediaManager(())`
1264
+ Delegates core actions to slide element sub-managers.
1265
+
1266
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1267
+
1268
+ ```javascript
1269
+ ppt.useSlide(1).mediaManager(());
1270
+ ```
1271
+
1272
+ #### `load(())`
1273
+ Delegates core actions to slide element sub-managers.
1274
+
1275
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1276
+
1277
+ ```javascript
1278
+ const ppt = await PPTXTemplater.load('./my_template.pptx');
1279
+ ```
1280
+
1281
+ #### `create(())`
1282
+ Delegates core actions to slide element sub-managers.
1283
+
1284
+ * **Returns**: `PPTXTemplater` - The fluent engine instance.
1285
+
1286
+ ```javascript
1287
+ ppt.useSlide(1).create(());
1288
+ ```
318
1289
 
319
1290
  ---
320
1291
 
321
- ## Performance Benchmarks
1292
+ <!-- API_REFERENCE_END -->
1293
+
1294
+ ---
1295
+
1296
+ ## 📊 Chart Data Labels & Value From Cells
1297
+
1298
+ PPTXForge supports advanced, PowerPoint-compatible chart data labels. You can customize label data sources, templates, positioning, and visual styles.
1299
+
1300
+ ### 1. Value From Cells (Excel Synchronization)
1301
+ 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.
1302
+
1303
+ ```javascript
1304
+ ppt.useSlide(1).updateDataLabels('SalesChart', {
1305
+ series: 0,
1306
+ labelsFromCells: 'Sheet1!D2:D5'
1307
+ });
1308
+ ```
1309
+
1310
+ ### 2. Literal Arrays
1311
+ Override data labels for specific points in a series with a static list of strings:
1312
+
1313
+ ```javascript
1314
+ ppt.useSlide(1).updateDataLabels('SalesChart', {
1315
+ series: 0,
1316
+ labels: ['Top Performance', 'Met Target', 'Action Required', 'At Risk']
1317
+ });
1318
+ ```
1319
+
1320
+ ### 3. Dynamic Label Templates
1321
+ Combine values, category names, percentages, and custom label strings to format annotations:
322
1322
 
323
- Tested on a standard 50-slide enterprise presentation template:
1323
+ ```javascript
1324
+ ppt.useSlide(1).updateDataLabels('SalesChart', {
1325
+ series: 0,
1326
+ template: '{category}: {value} ({percentage}%)'
1327
+ });
1328
+ ```
1329
+ * **Variables**: `{category}`, `{value}`, `{percentage}`, `{series}`, `{customLabel}`
1330
+
1331
+ ### 4. Label Positions
1332
+ Set label alignment to any of PowerPoint's standard values:
1333
+
1334
+ ```javascript
1335
+ ppt.useSlide(1).updateDataLabels('SalesChart', {
1336
+ series: 0,
1337
+ position: 'insideEnd' // 'center', 'insideEnd', 'insideBase', 'outsideEnd', 'bestFit', 'left', 'right', 'top', 'bottom'
1338
+ });
1339
+ ```
1340
+
1341
+ ### 5. Custom Label Styling
1342
+ Format your data labels using custom fonts, sizes, colors, and weight properties:
1343
+
1344
+ ```javascript
1345
+ ppt.useSlide(1).updateDataLabels('SalesChart', {
1346
+ series: 0,
1347
+ labels: ['High', 'Medium', 'Low'],
1348
+ labelStyle: {
1349
+ fontFamily: 'Century Gothic',
1350
+ fontSize: 12,
1351
+ color: '#0055A5',
1352
+ bold: true,
1353
+ italic: true,
1354
+ underline: true
1355
+ }
1356
+ });
1357
+ ```
1358
+
1359
+ ### 6. Inline Custom Data Labels (`updateChart`)
1360
+ 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:
1361
+
1362
+ ```javascript
1363
+ ppt.useSlide(1).updateChart('RevenueChart', {
1364
+ categories: ['Q1', 'Q2', 'Q3', 'Q4'],
1365
+ series: [
1366
+ {
1367
+ name: 'Product A',
1368
+ values: [
1369
+ { data: 145, label: 'Q1: 145 (Low)' },
1370
+ { data: 210, label: 'Q2: 210 (Med)' },
1371
+ { data: 190, label: 'Q3: 190 (Med)' },
1372
+ { data: 250, label: 'Q4: 250 (High)' }
1373
+ ]
1374
+ }
1375
+ ]
1376
+ });
1377
+ ```
324
1378
 
325
- | Operation | Execution Duration |
326
- |:---|:---|
327
- | Load PPTX Template | ~110ms |
328
- | Find & Replace 20 Text Placeholders | ~2.5ms |
329
- | XML Schema & Integrity Validation Check | ~14ms |
330
- | Dynamic Row Insertion & Merging (15 rows) | ~3ms |
331
- | Save and Re-package to PPTX ZIP | ~78ms |
1379
+ 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.
332
1380
 
333
1381
  ---
334
1382
 
335
- ## FAQ & Troubleshooting
1383
+ ## 🔒 Advanced XML Security
336
1384
 
337
- ### PowerPoint displays a "Repair" prompt when opening my generated file
338
- This is commonly caused by:
339
- 1. **Missing overridden content type**: A new slide or chart XML was added but not registered in `[Content_Types].xml`.
340
- 2. **Duplicate row identifiers**: If table rows are duplicated without generating a new unique `rowId` under `<a16:rowId>`.
341
- 3. **Invalid relationship mapping**: An asset (like an image or worksheet) is referenced in slide XML but is missing from the slide's `.rels` file.
1385
+ PPTXForge uses a secure XML parser that defends your application servers against common XML vulnerabilities.
342
1386
 
343
- *Fix*: Ensure you always use the public `saveToFile()` or `toBuffer()` helper functions, which automatically execute structural verification passes and update relationship chains.
1387
+ ### Built-in Protections
1388
+ 1. **XML Bombs & Billion Laughs Mitigation**: Instantly rejects any XML payload containing `<!DOCTYPE>` or `<!ENTITY>` definitions, preventing parser lockups.
1389
+ 2. **XXE Injection Defenses**: Completely blocks external entities referencing local files or external resources (e.g., `SYSTEM` or `PUBLIC`).
1390
+ 3. **Decoupled Entity Expansion Limits**: Evaluates code points and standard characters (`&amp;`, `&lt;`, etc.) via a single-level parser, avoiding recursive parsing loops.
344
1391
 
345
- ### My text placeholders are not replacing
346
- PowerPoint text editors segment formatting runs into separate XML elements. The text `{{title}}` may look normal in PowerPoint, but in XML it could be split into `<a:t>{{ti</a:t><a:t>tle}}</a:t>`.
347
- *Fix*: You can enable logger output to see split tags:
348
- ```bash
349
- PPTX_LOG_LEVEL=debug node app.js
1392
+ ### Custom Security & Parsing API
1393
+ Use the safe-parsing helpers to validate user-uploaded templates manually:
1394
+ ```javascript
1395
+ const { validateXml, safeParseXml } = require('node-pptx-templater');
1396
+
1397
+ const schema = validateXml(userInputXml);
1398
+ if (!schema.valid) {
1399
+ console.error(`Invalid XML format: ${schema.error} on Line ${schema.line}`);
1400
+ }
350
1401
  ```
351
- To fix this in PowerPoint, highlight the entire placeholder block, cut it, and paste it back as "Keep Text Only" to unify the XML text runs.
1402
+
1403
+ ---
1404
+
1405
+ ## ⚡ Performance Benchmarks
1406
+
1407
+ Below are benchmark results compiled on a standard Intel Core i7 system processing a 50-slide presentation template:
1408
+
1409
+ | Operation | Average Duration |
1410
+ | :--- | :--- |
1411
+ | Load Template | ~110 ms |
1412
+ | Scan & Replace 20 Text Tag Placeholders | ~2.5 ms |
1413
+ | Validate XML Schemas & Relationship Integrity | ~14 ms |
1414
+ | Table Row Cloning & Grid Merges (15 rows) | ~3 ms |
1415
+ | Z-Order Re-indexing & Packaging Output | ~78 ms |
1416
+
1417
+ ---
1418
+
1419
+ ## ❓ FAQ & Developer Troubleshooting
1420
+
1421
+ ### Q: PowerPoint displays a "Repair Presentation" alert when opening generated files.
1422
+ * **Root Cause**: This is usually caused by:
1423
+ 1. A missing relationship entry inside `.rels` maps (e.g., a slide refers to a chart file that isn't declared).
1424
+ 2. Duplicate `<a16:rowId>` elements on a slide (caused by cloning rows raw without updating hashes).
1425
+ * **Fix**: Ensure you always use the built-in `saveToFile()` or `toBuffer()` methods, which automatically execute validation checks, repair relationship indexes, and sanitize content structures.
1426
+
1427
+ ### Q: A text placeholder is not replacing. How do I debug it?
1428
+ * **Root Cause**: PowerPoint might have split your placeholder into multiple runs (e.g., `{{` and `placeholder}}`).
1429
+ * **Fix**:
1430
+ 1. Enable debug logging to locate the fragment: `PPTX_LOG_LEVEL=debug node app.js`.
1431
+ 2. In PowerPoint, select the placeholder text box, cut it, and paste it back using the **"Keep Text Only"** option. This unifies the run.
352
1432
 
353
1433
  ---
354
1434
 
355
1435
  ## 🤝 Contributing
356
1436
 
357
- We welcome contributions! Please check out [CONTRIBUTING.md](./CONTRIBUTING.md) to get started.
1437
+ 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.
358
1438
 
359
1439
  ```bash
360
1440
  git clone https://github.com/jsuyog2/node-pptx-templater.git
@@ -367,4 +1447,4 @@ npm test
367
1447
 
368
1448
  ## 📄 License
369
1449
 
370
- Licensed under the MIT License. © [node-pptx-templater contributors](./LICENSE)
1450
+ Licensed under the MIT License. © node-pptx-templater contributors.