node-pptx-templater 1.0.3 → 1.0.5

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,24 +1,38 @@
1
1
  # node-pptx-templater
2
2
 
3
- > A low-level PowerPoint OpenXML templating engine for Node.js that generates and edits PPTX files directly through XML manipulation without relying on PowerPoint generation libraries.
3
+ > High-performance, low-level PowerPoint (PPTX) OpenXML template engine for Node.js. Dynamically replace text, insert images, update charts (with Excel workbook data caching), and merge table cells without PowerPoint corruption or Repair Mode prompts.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/node-pptx-templater.svg)](https://www.npmjs.com/package/node-pptx-templater)
6
- [![CI](https://github.com/jsuyog2/node-pptx-templater/actions/workflows/ci.yml/badge.svg)](https://github.com/jsuyog2/node-pptx-templater/actions/workflows/ci.yml)
7
- [![Coverage](https://img.shields.io/codecov/c/github/jsuyog2/node-pptx-templater)](https://codecov.io/gh/jsuyog2/node-pptx-templater)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)
9
- [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org)
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)
11
+
12
+ ---
13
+
14
+ ## ⚡ Why node-pptx-templater?
15
+
16
+ Traditional PowerPoint generation libraries require building slides from scratch in code, which is verbose, hard to maintain, and strips away the power of visual design tools.
17
+
18
+ `node-pptx-templater` takes a different approach: **Design visually in PowerPoint, populate dynamically in Node.js.**
19
+
20
+ You create slide decks using PowerPoint, Google Slides, or Keynote, set your formatting, themes, animations, and layouts, and place placeholders like `{{company}}` or `{{revenue-chart}}`. `node-pptx-templater` parses the template and updates text, injects images, replaces chart values (updating both Excel workbook data caches and XML shapes), and merges tables dynamically while keeping the presentation 100% compliant with standard OpenXML guidelines.
10
21
 
11
22
  ---
12
23
 
13
24
  ## ✨ Features
14
25
 
15
- - 🏗️ **Zero PPTX Library Dependencies**: Operates entirely via low-level XML/ZIP manipulation.
16
- - 🔁 **Advanced Text Replacement**: Handles fragmented runs seamlessly (`{{placeholders}}`).
17
- - 📊 **Comprehensive Chart Support**: Bar, Column, Line, Pie, Doughnut, Area, Scatter—preserves original themes.
18
- - 📋 **Safe Table Updates**: Fully manages row insertion, deletion, merging, resizing, and cell formatting while maintaining PowerPoint integrity (prevents repair warnings by generating unique row IDs).
19
- - 🎨 **Shape & Image Manipulation**: Update text, clone, position, replace, or delete slide components.
20
- - 🎯 **Slide Operations**: Duplicate, reorder, insert, or delete slide parts on the fly.
21
- - 🔍 **Deep Integrity Validation**: Live validation of relationships, XML schema consistency, table columns, and duplicates prior to saving.
26
+ - 🏗️ **Zero Native Office/Java Dependencies**: Runs on pure Javascript/Node.js, making it ideal for high-throughput cloud environments, Lambda, or serverless runtimes.
27
+ - 🔁 **Fragmented Placeholder Resolution**: PowerPoint often splits text runs like `{{company}}` into `<a:r>` nodes. Our engine merges and resolves fragmented tags automatically.
28
+ - 📊 **Full Chart Engine Integration**: Supports Bar, Column, Line, Pie, Doughnut, Area, Scatter, and Bubble charts. Automatically synchronizes chart XML properties and coordinates with the embedded Excel sheets (`ppt/embeddings/`).
29
+ - 📋 **Flexible Table Merging & Templating**:
30
+ - Horizontal column merge (`gridSpan` & `hMerge`), vertical row merge (`rowSpan` & `vMerge`), and rectangular block merges.
31
+ - Formats cells dynamically with inline options (`align`, `fontSize`, `fill`).
32
+ - Automatically handles slide table duplicates by generating unique `<a16:rowId>` 32-bit hashes to **prevent PowerPoint Repair Mode** screens.
33
+ - 🎨 **Shape & Image Manipulation**: Find shapes, clone layout blocks with offsets, replace image sources while keeping exact positions, or delete elements.
34
+ - 🎯 **Slide Management Operations**: Duplicate, reorder, delete, and import slides from external templates with automatic media asset deduplication.
35
+ - 🔍 **Deep Packaging Integrity Validation**: Real-time checking of relationships, XML schemas, table column numbers, and override duplicates.
22
36
 
23
37
  ---
24
38
 
@@ -32,58 +46,92 @@ npm install node-pptx-templater
32
46
 
33
47
  ## 🚀 Quick Start
34
48
 
49
+ Get up and running in under 60 seconds with this simple template rendering example:
50
+
35
51
  ```js
36
52
  const { PPTXTemplater } = require('node-pptx-templater');
37
53
 
38
- async function run() {
39
- // Load presentation template
40
- const ppt = await PPTXTemplater.load('template.pptx');
54
+ async function main() {
55
+ // 1. Load your PowerPoint presentation template
56
+ const ppt = await PPTXTemplater.load('monthly_report_template.pptx');
41
57
 
42
- // Use Slide 1 and replace text placeholders
58
+ // 2. Select slide 1 and execute operations
43
59
  ppt.useSlide(1)
44
- .replaceTextByTag('title', 'Annual Sales Report')
45
- .replaceMultiple({ company: 'Google DeepMind', year: '2026' });
60
+ .replaceTextByTag('title', 'Quarterly Earnings Report')
61
+ .replaceMultiple({
62
+ company: 'Acme Corporation',
63
+ year: '2026'
64
+ });
46
65
 
47
- // Update chart data on Slide 2
66
+ // 3. Update chart series data on Slide 2
48
67
  ppt.useSlide(2)
49
68
  .updateChartData('sales-chart', {
50
69
  categories: ['Q1', 'Q2', 'Q3', 'Q4'],
51
- series: [{ name: 'Revenue', values: [100, 150, 180, 220] }]
70
+ series: [
71
+ { name: 'Target', values: [100, 120, 140, 160] },
72
+ { name: 'Revenue', values: [105, 118, 145, 172] }
73
+ ]
52
74
  });
53
75
 
54
- // Save the result (validates structural integrity automatically)
55
- await ppt.saveToFile('output.pptx');
76
+ // 4. Update table with cell merging and formatting on Slide 3
77
+ ppt.useSlide(3)
78
+ .updateTable('sales-table', [
79
+ ['Region', 'Q1 Actual', 'Q2 Actual', 'Status'],
80
+ ['North', '120k', '140k', { value: 'On Track', align: 'ctr', fill: '10b981' }],
81
+ ['South', '95k', '110k', { value: 'Review', align: 'ctr', fill: 'f59e0b' }]
82
+ ]);
83
+
84
+ // 5. Save the non-corrupted PPTX back to disk
85
+ await ppt.saveToFile('./output/annual_earnings.pptx');
56
86
  }
57
- run();
87
+
88
+ main().catch(err => console.error(err));
58
89
  ```
59
90
 
60
91
  ---
61
92
 
62
- ## 🏗️ Architecture & OpenXML Internals
93
+ ## 🏗️ OpenXML Architecture & Internals
63
94
 
64
- A PPTX file is an OPC (Open Packaging Convention) ZIP containing XML parts:
95
+ A `.pptx` file is an OPC (Open Packaging Convention) ZIP archive containing structured XML documents and asset folders:
65
96
 
66
- - `/ppt/presentation.xml` – The slides inventory (`sldIdLst`) and masters.
67
- - `/ppt/slides/slideN.xml` – Slide elements (shapes, images, tables).
68
- - `/ppt/slides/_rels/slideN.xml.rels` – Relationship links (`rId`).
69
- - `[Content_Types].xml` – Declares MIME types for slide parts, charts, and media.
97
+ - `[Content_Types].xml` – Global manifest declaring content MIME types for every file part in the ZIP.
98
+ - `_rels/.rels`Root-level package relationship index.
99
+ - `ppt/presentation.xml` – Root presentation settings and slide inventory (`sldIdLst`).
100
+ - `ppt/slides/slideN.xml` – Main slide canvas storing shapes, lines, tables, text runs, and layout components.
101
+ - `ppt/slides/_rels/slideN.xml.rels` – Relationship indexes mapping slide XML components to charts, layouts, and image assets.
70
102
 
71
- ### The rowId Table Corruption Bug
72
- Microsoft PowerPoint assigns an `<a16:rowId>` identifier to each row to facilitate collaborative editing. Duplicate row IDs trigger the PowerPoint **Repair Mode**, causing slide elements and styles to break.
73
- `node-pptx-templater` generates unique 32-bit unsigned integers for `<a16:rowId>` whenever a table row is cloned, appended, or inserted, ensuring output documents open flawlessly in MS PowerPoint, Google Slides, and LibreOffice.
103
+ ### Preventing PowerPoint Table Repair Errors
104
+ PowerPoint slide tables utilize unique 32-bit identifiers inside `<a16:rowId>` nodes for collaborative edits. Duplicating rows using naive array copy operations results in overlapping IDs, triggering Microsoft PowerPoint's **"PowerPoint found a problem with content"** repair screen on open.
105
+ `node-pptx-templater` intercepts all table operations (adding, cloning, inserting, or merging rows) and dynamically injects newly generated unique `rowId` hashes, ensuring a seamless, warning-free loading experience in:
106
+ - Microsoft PowerPoint (Desktop, Mac, Online)
107
+ - Google Slides
108
+ - LibreOffice Impress
74
109
 
75
110
  ---
76
111
 
77
- ## 📚 API Reference
112
+ ## 📊 Feature Comparison Matrix
113
+
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 |
124
+
125
+ ---
78
126
 
79
- Here is the complete reference of all public APIs.
127
+ ## 📚 API Reference
80
128
 
81
- ### Slide Features
129
+ ### Slide Operations
82
130
 
83
131
  #### `duplicateSlide(slideIndex, atPosition)`
84
132
  Duplicates a slide.
85
133
  ```js
86
- ppt.duplicateSlide(1, 2); // Duplicate Slide 1 and insert it as Slide 2
134
+ ppt.duplicateSlide(1, 2); // Duplicate Slide 1 and insert it at position 2
87
135
  ```
88
136
 
89
137
  #### `deleteSlide(slideIndex)`
@@ -98,363 +146,215 @@ Moves a slide to a new position.
98
146
  ppt.moveSlide(1, 3); // Move Slide 1 to position 3
99
147
  ```
100
148
 
101
- #### `insertSlide(slideIndex, options)`
102
- Inserts a new blank slide at a specific position.
149
+ #### `importSlideFrom(sourcePresentation, sourceSlideIndex)`
150
+ Deep-copies a slide from another loaded presentation, automatically remapping layouts, shapes, charts, and deduplicating media assets.
103
151
  ```js
104
- ppt.insertSlide(2, { title: 'New Layout Slide' });
105
- ```
106
-
107
- #### `getSlides()`
108
- Returns metadata about all slides in the deck.
109
- ```js
110
- const slides = ppt.getSlides();
111
- console.log(slides);
152
+ const source = await PPTXTemplater.load('marketing_slides.pptx');
153
+ await ppt.importSlideFrom(source, 2); // Import Slide 2 of marketing deck
112
154
  ```
113
155
 
114
156
  ---
115
157
 
116
- ### Table Features
158
+ ### Table Manipulation
117
159
 
118
- #### `addTableRow(tableId, rowData)`
119
- Appends a new row to the table.
160
+ #### `updateTable(tableId, data)`
161
+ Updates a table with rows data, merge rules, and cell styles.
120
162
  ```js
121
- ppt.addTableRow('sales-table', ['Q4', '150', '210', '190', '250']);
163
+ ppt.updateTable('revenue-table', [
164
+ ['Year', 'Revenue', 'Profit'],
165
+ ['2025', '120k', '40k'],
166
+ ['2026', '150k', { value: '60k', fill: '10b981', align: 'ctr' }]
167
+ ]);
122
168
  ```
123
169
 
124
- #### `removeTableRow(tableId, rowIndex)`
125
- Removes a table row.
170
+ #### `mergeCells(options)`
171
+ Merges a rectangular block of cells. Supports horizontal, vertical, and block merging, concatenating all text from the merged region into the top-left cell.
126
172
  ```js
127
- ppt.removeTableRow('sales-table', 1); // Delete 2nd row (0-based)
128
- ```
129
-
130
- #### `insertTableRow(tableId, rowIndex, rowData)`
131
- Inserts a row at a specific index.
132
- ```js
133
- ppt.insertTableRow('sales-table', 2, ['Q3', '100', '120', '140', '160']);
134
- ```
135
-
136
- #### `cloneTableRow(tableId, sourceRowIndex, targetRowIndex)`
137
- Clones a row style/data and inserts it.
138
- ```js
139
- ppt.cloneTableRow('sales-table', 1, 3);
140
- ```
141
-
142
- #### `updateCell(tableId, rowIndex, colIndex, value, options)`
143
- Updates a specific cell value and styling.
144
- ```js
145
- ppt.updateCell('sales-table', 1, 0, 'New Product Name', {
146
- fill: 'FF0000', // HEX background color
147
- align: 'ctr', // Text alignment: 'l', 'ctr', 'r', 'just'
148
- fontSize: 14 // Font size in points
149
- });
150
- ```
151
-
152
- #### `mergeCells(options)` or `mergeCells(tableId, startRow, startCol, endRow, endCol)`
153
- Merges a rectangular range of cells. Supports horizontal, vertical, and block merges, moving cell texts to the origin cell (concatenated with newlines) and setting correct OpenXML `gridSpan` and `rowSpan` attributes.
154
- ```js
155
- // Config object signature (Recommended)
156
173
  ppt.mergeCells({
157
- slide: 3,
158
174
  tableId: 'sales-table',
159
175
  startRow: 1,
160
176
  startCol: 1,
161
- endRow: 3,
162
- endCol: 3
177
+ endRow: 2,
178
+ endCol: 2
163
179
  });
164
-
165
- // Legacy positional signature
166
- ppt.mergeCells('sales-table', 1, 1, 3, 3);
167
180
  ```
168
181
 
169
- #### `unmergeCells(options)` or `unmergeCells(tableId, startRow, startCol, endRow, endCol)`
170
- Unmerges cells, restoring original XML cell structures.
182
+ #### `unmergeCells(options)`
183
+ Splits a merged region back to its original individual cells, removing `gridSpan`, `rowSpan`, `hMerge`, and `vMerge` attributes.
171
184
  ```js
172
- // Cell-coordinate coordinate signature (Recommended)
173
185
  ppt.unmergeCells({
174
- slide: 3,
175
186
  tableId: 'sales-table',
176
- row: 2,
177
- col: 2
178
- });
179
-
180
- // Legacy positional signature
181
- ppt.unmergeCells('sales-table', 1, 1, 3, 3);
182
- ```
183
-
184
- #### `getMergedCells(tableId)`
185
- Scans the slide table and returns all active merged regions.
186
- ```js
187
- const merges = ppt.getMergedCells('sales-table');
188
- // Output: [ { startRow: 1, startCol: 1, endRow: 3, endCol: 3 } ]
189
- ```
190
-
191
- #### `validateMergeRegion(tableId, startRow, startCol, endRow, endCol)`
192
- Checks bounds and overlaps, returning detailed validation errors.
193
- ```js
194
- const report = ppt.validateMergeRegion('sales-table', 1, 1, 3, 3);
195
- console.log(report.valid); // true or false
196
- console.log(report.errors); // Array of error strings
197
- ```
198
-
199
- #### `isMergedCell(tableId, row, col)`
200
- Returns `true` if the cell at `(row, col)` is part of any merged region.
201
- ```js
202
- const merged = ppt.isMergedCell('sales-table', 2, 2);
203
- ```
204
-
205
- #### `getMergeParent(tableId, row, col)`
206
- Returns the coordinates `{ row, col }` of the top-left origin cell of the merge region containing `(row, col)`.
207
- ```js
208
- const parent = ppt.getMergeParent('sales-table', 2, 2); // { row: 1, col: 1 }
209
- ```
210
-
211
- #### `getMergeRegion(tableId, row, col)`
212
- Returns the merged region object `{ startRow, startCol, endRow, endCol }` containing `(row, col)`.
213
- ```js
214
- const region = ppt.getMergeRegion('sales-table', 2, 2);
215
- ```
216
-
217
- #### `splitMergedRegion(tableId, row, col)`
218
- Splits the merged region containing cell `(row, col)`.
219
- ```js
220
- ppt.splitMergedRegion('sales-table', 2, 2);
221
- ```
222
-
223
- #### `cloneMergedRegion(tableId, row, col, targetRow, targetCol)`
224
- Clones a merged region to another starting position, preserving cell text and formatting.
225
- ```js
226
- ppt.cloneMergedRegion('sales-table', 1, 1, 4, 1);
227
- ```
228
-
229
- #### Template-driven cell merges
230
- Support cell merging inside template updates dynamically via `colSpan`/`rowSpan` or `merge` arrays:
231
- ```js
232
- ppt.updateTable('sales-table', {
233
- rows: [
234
- ['Header 1', 'Header 2', 'Header 3'],
235
- ['Row 1 Col 1', { value: 'Spanned Cell', colSpan: 2 }],
236
- ['Row 2 Col 1', 'Row 2 Col 2', { value: 'Spanned V', rowSpan: 2 }]
237
- ],
238
- merge: [
239
- { startRow: 0, startCol: 0, endRow: 0, endCol: 2 }
240
- ]
187
+ row: 1,
188
+ col: 1
241
189
  });
242
190
  ```
243
191
 
244
- #### `autoFitTable(tableId)`
245
- Resizes columns to fit text width.
246
- ```js
247
- ppt.autoFitTable('sales-table');
248
- ```
249
-
250
- #### `resizeTable(tableId, width, height)`
251
- Resizes the bounding frame of the table. Width/height can be in EMUs or inches.
252
- ```js
253
- ppt.resizeTable('sales-table', 8.5, 4.2); // Dimensions in inches
254
- ```
255
-
256
- #### `getTables()`
257
- Lists tables in the current slide.
258
- ```js
259
- const tables = ppt.getTables();
260
- ```
261
-
262
192
  ---
263
193
 
264
- ### Chart Features
194
+ ### Chart Integration
265
195
 
266
196
  #### `updateChartData(chartId, data)`
267
- Updates a chart's categories, series, values, and embedded Excel workbook.
197
+ Overwrites chart categories and series values. Updates the embedded Excel spreadsheet to ensure the chart matches perfectly on refresh.
268
198
  ```js
269
199
  ppt.updateChartData('sales-chart', {
270
- categories: ['Q1', 'Q2', 'Q3'],
271
- series: [{ name: 'Sales', values: [100, 120, 150] }]
272
- });
273
- ```
274
-
275
- #### `replaceChartSeries(chartId, seriesIndex, newSeriesData)`
276
- Replaces values and name of a single series.
277
- ```js
278
- ppt.replaceChartSeries('sales-chart', 0, {
279
- name: 'Updated Series Name',
280
- values: [80, 95, 110]
200
+ categories: ['Q1', 'Q2', 'Q3', 'Q4'],
201
+ series: [
202
+ { name: 'Revenue', values: [100, 150, 180, 220] }
203
+ ]
281
204
  });
282
205
  ```
283
206
 
284
207
  #### `updateChartTitle(chartId, title)`
285
- Updates the chart's title.
286
- ```js
287
- ppt.updateChartTitle('sales-chart', 'Quarterly Revenue Performance');
288
- ```
289
-
290
- #### `updateChartCategories(chartId, categories)`
291
- Updates the chart categories.
292
208
  ```js
293
- ppt.updateChartCategories('sales-chart', ['Jan', 'Feb', 'Mar']);
294
- ```
295
-
296
- #### `getCharts()`
297
- Returns chart metadata from the slide.
298
- ```js
299
- const charts = ppt.getCharts();
209
+ ppt.updateChartTitle('sales-chart', 'Revenue Growth (2026)');
300
210
  ```
301
211
 
302
212
  ---
303
213
 
304
- ### Text Features
214
+ ### Z-Order (Layer Management)
305
215
 
306
- #### `replaceTextByTag(tag, value, options)`
307
- Replaces placeholders with custom values.
308
- ```js
309
- ppt.replaceTextByTag('username', 'Alice Cooper');
310
- ```
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.
311
217
 
312
- #### `replaceMultiple(replacements, options)`
313
- Performs multiple text tag replacements.
314
- ```js
315
- ppt.replaceMultiple({
316
- 'date': '2026-06-02',
317
- 'location': 'New York'
318
- });
319
- ```
218
+ All operations accept either an **options object** with a `slide` key or can be chained after `useSlide()`:
320
219
 
321
- #### `findText(text)`
322
- Searches for text in slide shape runs.
323
220
  ```js
324
- const matches = ppt.findText('DeepMind');
325
- ```
221
+ // Option A — explicit slide number
222
+ ppt.bringForward({ slide: 2, objectId: 'logo' });
326
223
 
327
- #### `getTextElements()`
328
- Gets all raw text segments in the selected slide.
329
- ```js
330
- const elements = ppt.getTextElements();
224
+ // Option B — fluent chain
225
+ ppt.useSlide(2).bringForward('logo');
331
226
  ```
332
227
 
333
- ---
334
-
335
- ### Shape Features
336
-
337
- #### `updateShapeText(shapeId, text)`
338
- Updates text inside a shapes run.
228
+ #### `getObjectOrder(slideIndex)`
229
+ Returns a sorted array describing every drawing element on the slide, bottom-to-top.
339
230
  ```js
340
- ppt.updateShapeText('HeaderShape', 'Updated Slide Header');
231
+ const layers = ppt.getObjectOrder(1);
232
+ // → [{ id: 'Background', type: 'shape', zIndex: 1 }, ...]
341
233
  ```
342
234
 
343
- #### `cloneShape(shapeId, newShapeId, options)`
344
- Clones a shape and places it with offsets.
235
+ #### `bringForward(options)` / `sendBackward(options)`
236
+ Move an object one layer up or down.
345
237
  ```js
346
- ppt.cloneShape('HeaderShape', 'HeaderShapeCopy', {
347
- offsetX: 1.0, // Offset X in inches
348
- offsetY: 0.5, // Offset Y in inches
349
- width: 5.0, // Resize width
350
- height: 2.0 // Resize height
351
- });
238
+ ppt.bringForward({ slide: 1, objectId: 'logo' });
239
+ ppt.sendBackward({ slide: 1, objectId: 'logo' });
352
240
  ```
353
241
 
354
- #### `deleteShape(shapeId)`
355
- Deletes a shape.
242
+ #### `bringToFront(options)` / `sendToBack(options)`
243
+ Move an object to the very top or very bottom of the stack.
356
244
  ```js
357
- ppt.deleteShape('HeaderShapeCopy');
245
+ ppt.bringToFront({ slide: 1, objectId: 'logo' });
246
+ ppt.sendToBack({ slide: 1, objectId: 'background' });
358
247
  ```
359
248
 
360
- #### `getShapes()`
361
- Lists shapes on the slide.
249
+ #### `setZIndex(options)`
250
+ Place an object at an exact 1-based stacking position.
362
251
  ```js
363
- const shapes = ppt.getShapes();
252
+ ppt.setZIndex({ slide: 1, objectId: 'logo', zIndex: 3 });
364
253
  ```
365
254
 
366
- ---
367
-
368
- ### Image Features
369
-
370
- #### `replaceImage(imageIdOrName, sourcePathOrBuffer)`
371
- Replaces an image file binary, keeping the layout.
255
+ #### `moveObjectBefore(options)` / `moveObjectAfter(options)`
256
+ Position an object immediately below or above a specific target.
372
257
  ```js
373
- await ppt.replaceImage('LogoImage', 'path/to/new-logo.png');
258
+ ppt.moveObjectBefore({ slide: 1, objectId: 'overlay', targetId: 'chart' });
259
+ ppt.moveObjectAfter({ slide: 1, objectId: 'label', targetId: 'chart' });
374
260
  ```
375
261
 
376
- #### `addImage(sourcePathOrBuffer, options)`
377
- Embeds a new image with layout options.
262
+ #### `reorderObjects(options)`
263
+ Bulk-reorder the entire slide stack by specifying all object names in desired bottom-to-top order.
378
264
  ```js
379
- await ppt.addImage('path/to/badge.png', {
380
- x: 2.0, // Position X (inches)
381
- y: 1.5, // Position Y (inches)
382
- width: 3.0, // Width (inches)
383
- height: 3.0 // Height (inches)
265
+ ppt.reorderObjects({
266
+ slide: 1,
267
+ order: ['background', 'chart', 'logo', 'title']
384
268
  });
385
269
  ```
386
270
 
387
- #### `removeImage(imageIdOrName)`
388
- Deletes an image.
271
+ #### `applyZOrder(slideIndex, configs)`
272
+ Apply multiple stacking rules in a single call. Operations are executed sequentially.
389
273
  ```js
390
- ppt.removeImage('Picture 1002');
274
+ ppt.applyZOrder(1, [
275
+ { id: 'background', sendToBack: true },
276
+ { id: 'overlay', zIndex: 2 },
277
+ { id: 'logo', bringToFront: true },
278
+ ]);
391
279
  ```
392
280
 
393
- #### `getImages()`
394
- Lists images inside the slide.
281
+ #### `swapObjects(slideIndex, objectId1, objectId2)`
282
+ Exchange the stacking positions of two objects.
395
283
  ```js
396
- const images = ppt.getImages();
284
+ ppt.swapObjects(1, 'logo', 'chart');
397
285
  ```
398
286
 
399
- ---
400
-
401
- ### Validation System
402
-
403
- #### `validatePresentation()`
404
- Audits the complete presentation.
287
+ #### `sortObjects(slideIndex, compareFn)`
288
+ Sort the layer stack using a custom comparator (receives `{ id, type, zIndex }` objects).
405
289
  ```js
406
- const report = await ppt.validatePresentation();
407
- console.log('Errors:', report.errors);
408
- console.log('Warnings:', report.warnings);
290
+ // Alphabetical ascending by name
291
+ ppt.sortObjects(1, (a, b) => a.id.localeCompare(b.id));
409
292
  ```
410
293
 
411
- #### `validateSlide(slideIndex)`
412
- Validates slide XML structure.
294
+ #### `getTopMostObject(slideIndex)` / `getBottomMostObject(slideIndex)`
295
+ Retrieve metadata for the topmost or bottommost element.
413
296
  ```js
414
- const report = await ppt.validateSlide(1);
297
+ const top = ppt.getTopMostObject(1); // { id: 'logo', type: 'image', zIndex: 5 }
298
+ const bottom = ppt.getBottomMostObject(1); // { id: 'background', type: 'shape', zIndex: 1 }
415
299
  ```
416
300
 
417
- #### `validateTable(tableId)`
418
- Audits columns and duplicate rowIds in a table.
301
+ #### `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.
419
303
  ```js
420
- const report = await ppt.validateTable('sales-table');
304
+ ppt.normalizeZOrder(1);
421
305
  ```
422
306
 
423
- #### `validateRelationships(partPath)`
424
- Audits `.rels` relationship references.
425
- ```js
426
- const report = ppt.validateRelationships('ppt/slides/slide1.xml');
427
- ```
307
+ **Supported element types:**
308
+
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` |
428
318
 
429
319
  ---
430
320
 
431
321
  ## ⚡ Performance Benchmarks
432
322
 
433
- Tested on a 50-slide presentation template:
323
+ Tested on a standard 50-slide enterprise presentation template:
434
324
 
435
- | Operation | Average Duration |
436
- |---|---|
437
- | Load presentation | ~120ms |
438
- | Replace 20 text tags | ~2ms |
439
- | Audit presentation structure | ~15ms |
440
- | Update table rows (10 rows) | ~3ms |
441
- | Rebuild ZIP and Save | ~80ms |
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 |
442
332
 
443
333
  ---
444
334
 
445
- ## ❓ FAQ
335
+ ## ❓ FAQ & Troubleshooting
446
336
 
447
- #### Why use this over libraries like pptxgenjs or officegen?
448
- Those libraries generate presentations from scratch in code. `node-pptx-templater` is a **templating** engine. You create a beautiful template inside MS PowerPoint (applying themes, layout grids, animations, or styling), and then use this library to dynamically populate slides, update tables, and refresh chart values while preserving the design.
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.
449
342
 
450
- #### How is chart styling preserved?
451
- We update only the `<c:cat>` (categories), `<c:val>` (values) XML nodes, and the underlying data sheet inside the embedded Excel workbook. PowerPoint reads these updated values and uses the template's pre-configured colors, font layouts, labels, and axes.
343
+ *Fix*: Ensure you always use the public `saveToFile()` or `toBuffer()` helper functions, which automatically execute structural verification passes and update relationship chains.
344
+
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
350
+ ```
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.
452
352
 
453
353
  ---
454
354
 
455
355
  ## 🤝 Contributing
456
356
 
457
- See [CONTRIBUTING.md](./CONTRIBUTING.md).
357
+ We welcome contributions! Please check out [CONTRIBUTING.md](./CONTRIBUTING.md) to get started.
458
358
 
459
359
  ```bash
460
360
  git clone https://github.com/jsuyog2/node-pptx-templater.git
@@ -467,4 +367,4 @@ npm test
467
367
 
468
368
  ## 📄 License
469
369
 
470
- MIT © [node-pptx-templater contributors](./LICENSE)
370
+ Licensed under the MIT License. © [node-pptx-templater contributors](./LICENSE)