node-pptx-templater 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +336 -281
- package/package.json +1 -1
- package/src/cli/commands/build.js +30 -31
- package/src/cli/commands/debug.js +23 -23
- package/src/cli/commands/extract.js +21 -21
- package/src/cli/commands/inspect.js +23 -23
- package/src/cli/commands/validate.js +17 -17
- package/src/cli/index.js +39 -36
- package/src/core/OutputWriter.js +79 -78
- package/src/core/PPTXTemplater.js +856 -273
- package/src/core/TemplateEngine.js +67 -71
- package/src/core/ValidationEngine.js +246 -0
- package/src/index.js +30 -17
- package/src/managers/ChartManager.js +195 -70
- package/src/managers/ContentTypesManager.js +49 -45
- package/src/managers/HyperlinkManager.js +146 -142
- package/src/managers/ImageManager.js +336 -0
- package/src/managers/MediaManager.js +62 -81
- package/src/managers/RelationshipManager.js +99 -95
- package/src/managers/ShapeManager.js +340 -0
- package/src/managers/SlideManager.js +408 -311
- package/src/managers/TableManager.js +979 -262
- package/src/managers/TextManager.js +197 -0
- package/src/managers/ZipManager.js +69 -69
- package/src/managers/charts/ChartCacheGenerator.js +75 -58
- package/src/managers/charts/ChartParser.js +9 -13
- package/src/managers/charts/ChartRelationshipManager.js +12 -10
- package/src/managers/charts/ChartWorkbookUpdater.js +59 -56
- package/src/parsers/XMLParser.js +47 -50
- package/src/templates/blankPptx.js +3 -2
- package/src/templates/slideTemplate.js +28 -34
- package/src/utils/contentTypesHelper.js +40 -54
- package/src/utils/errors.js +18 -18
- package/src/utils/idUtils.js +16 -14
- package/src/utils/logger.js +18 -16
- package/src/utils/relationshipUtils.js +19 -20
- package/src/utils/xmlUtils.js +26 -26
|
@@ -24,21 +24,25 @@
|
|
|
24
24
|
* └── app.xml — application metadata
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
const { ZipManager } = require('../managers/ZipManager.js')
|
|
28
|
-
const { XMLParser } = require('../parsers/XMLParser.js')
|
|
29
|
-
const { ContentTypesManager } = require('../managers/ContentTypesManager.js')
|
|
30
|
-
const { SlideManager } = require('../managers/SlideManager.js')
|
|
31
|
-
const { ChartManager } = require('../managers/ChartManager.js')
|
|
32
|
-
const { TableManager } = require('../managers/TableManager.js')
|
|
33
|
-
const { HyperlinkManager } = require('../managers/HyperlinkManager.js')
|
|
34
|
-
const { MediaManager } = require('../managers/MediaManager.js')
|
|
35
|
-
const { RelationshipManager } = require('../managers/RelationshipManager.js')
|
|
36
|
-
const {
|
|
37
|
-
const {
|
|
38
|
-
const {
|
|
39
|
-
const {
|
|
40
|
-
|
|
41
|
-
const
|
|
27
|
+
const { ZipManager } = require('../managers/ZipManager.js')
|
|
28
|
+
const { XMLParser } = require('../parsers/XMLParser.js')
|
|
29
|
+
const { ContentTypesManager } = require('../managers/ContentTypesManager.js')
|
|
30
|
+
const { SlideManager } = require('../managers/SlideManager.js')
|
|
31
|
+
const { ChartManager } = require('../managers/ChartManager.js')
|
|
32
|
+
const { TableManager } = require('../managers/TableManager.js')
|
|
33
|
+
const { HyperlinkManager } = require('../managers/HyperlinkManager.js')
|
|
34
|
+
const { MediaManager } = require('../managers/MediaManager.js')
|
|
35
|
+
const { RelationshipManager } = require('../managers/RelationshipManager.js')
|
|
36
|
+
const { ShapeManager } = require('../managers/ShapeManager.js')
|
|
37
|
+
const { ImageManager } = require('../managers/ImageManager.js')
|
|
38
|
+
const { TextManager } = require('../managers/TextManager.js')
|
|
39
|
+
const { ValidationEngine } = require('./ValidationEngine.js')
|
|
40
|
+
const { OutputWriter } = require('./OutputWriter.js')
|
|
41
|
+
const { TemplateEngine } = require('./TemplateEngine.js')
|
|
42
|
+
const { createLogger } = require('../utils/logger.js')
|
|
43
|
+
const { PPTXError } = require('../utils/errors.js')
|
|
44
|
+
|
|
45
|
+
const logger = createLogger('PPTXTemplater')
|
|
42
46
|
|
|
43
47
|
/**
|
|
44
48
|
* @class PPTXTemplater
|
|
@@ -55,92 +59,117 @@ class PPTXTemplater {
|
|
|
55
59
|
* @private
|
|
56
60
|
* @type {ZipManager}
|
|
57
61
|
*/
|
|
58
|
-
#zipManager
|
|
62
|
+
#zipManager
|
|
59
63
|
|
|
60
64
|
/**
|
|
61
65
|
* @private
|
|
62
66
|
* @type {XMLParser}
|
|
63
67
|
*/
|
|
64
|
-
#xmlParser
|
|
68
|
+
#xmlParser
|
|
65
69
|
|
|
66
70
|
/**
|
|
67
71
|
* @private
|
|
68
72
|
* @type {ContentTypesManager}
|
|
69
73
|
*/
|
|
70
|
-
#contentTypesManager
|
|
74
|
+
#contentTypesManager
|
|
71
75
|
|
|
72
76
|
/**
|
|
73
77
|
* @private
|
|
74
78
|
* @type {SlideManager}
|
|
75
79
|
*/
|
|
76
|
-
#slideManager
|
|
80
|
+
#slideManager
|
|
77
81
|
|
|
78
82
|
/**
|
|
79
83
|
* @private
|
|
80
84
|
* @type {ChartManager}
|
|
81
85
|
*/
|
|
82
|
-
#chartManager
|
|
86
|
+
#chartManager
|
|
83
87
|
|
|
84
88
|
/**
|
|
85
89
|
* @private
|
|
86
90
|
* @type {TableManager}
|
|
87
91
|
*/
|
|
88
|
-
#tableManager
|
|
92
|
+
#tableManager
|
|
89
93
|
|
|
90
94
|
/**
|
|
91
95
|
* @private
|
|
92
96
|
* @type {HyperlinkManager}
|
|
93
97
|
*/
|
|
94
|
-
#hyperlinkManager
|
|
98
|
+
#hyperlinkManager
|
|
95
99
|
|
|
96
100
|
/**
|
|
97
101
|
* @private
|
|
98
102
|
* @type {MediaManager}
|
|
99
103
|
*/
|
|
100
|
-
#mediaManager
|
|
104
|
+
#mediaManager
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @private
|
|
108
|
+
* @type {ShapeManager}
|
|
109
|
+
*/
|
|
110
|
+
#shapeManager
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @private
|
|
114
|
+
* @type {ImageManager}
|
|
115
|
+
*/
|
|
116
|
+
#imageManager
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @private
|
|
120
|
+
* @type {TextManager}
|
|
121
|
+
*/
|
|
122
|
+
#textManager
|
|
101
123
|
|
|
102
124
|
/**
|
|
103
125
|
* @private
|
|
104
126
|
* @type {RelationshipManager}
|
|
105
127
|
*/
|
|
106
|
-
#relationshipManager
|
|
128
|
+
#relationshipManager
|
|
107
129
|
|
|
108
130
|
/**
|
|
109
131
|
* @private
|
|
110
132
|
* @type {OutputWriter}
|
|
111
133
|
*/
|
|
112
|
-
#outputWriter
|
|
134
|
+
#outputWriter
|
|
113
135
|
|
|
114
136
|
/**
|
|
115
137
|
* @private
|
|
116
138
|
* @type {TemplateEngine}
|
|
117
139
|
*/
|
|
118
|
-
#templateEngine
|
|
140
|
+
#templateEngine
|
|
119
141
|
|
|
120
142
|
/**
|
|
121
143
|
* @private
|
|
122
144
|
* @type {number[]} - Currently selected slide indices (1-based)
|
|
123
145
|
*/
|
|
124
|
-
#selectedSlides = []
|
|
146
|
+
#selectedSlides = []
|
|
125
147
|
|
|
126
148
|
/**
|
|
127
149
|
* @private
|
|
128
150
|
* @type {boolean}
|
|
129
151
|
*/
|
|
130
|
-
#loaded = false
|
|
152
|
+
#loaded = false
|
|
131
153
|
|
|
132
154
|
constructor() {
|
|
133
|
-
this.#xmlParser = new XMLParser()
|
|
134
|
-
this.#zipManager = new ZipManager()
|
|
135
|
-
this.#contentTypesManager = new ContentTypesManager(this.#xmlParser)
|
|
136
|
-
this.#relationshipManager = new RelationshipManager(this.#xmlParser)
|
|
137
|
-
this.#slideManager = new SlideManager(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
this.#
|
|
143
|
-
this.#
|
|
155
|
+
this.#xmlParser = new XMLParser()
|
|
156
|
+
this.#zipManager = new ZipManager()
|
|
157
|
+
this.#contentTypesManager = new ContentTypesManager(this.#xmlParser)
|
|
158
|
+
this.#relationshipManager = new RelationshipManager(this.#xmlParser)
|
|
159
|
+
this.#slideManager = new SlideManager(
|
|
160
|
+
this.#xmlParser,
|
|
161
|
+
this.#relationshipManager,
|
|
162
|
+
this.#contentTypesManager
|
|
163
|
+
)
|
|
164
|
+
this.#chartManager = new ChartManager(this.#xmlParser, this.#contentTypesManager)
|
|
165
|
+
this.#tableManager = new TableManager(this.#xmlParser)
|
|
166
|
+
this.#hyperlinkManager = new HyperlinkManager(this.#xmlParser, this.#relationshipManager)
|
|
167
|
+
this.#mediaManager = new MediaManager(this.#contentTypesManager)
|
|
168
|
+
this.#shapeManager = new ShapeManager(this.#xmlParser)
|
|
169
|
+
this.#imageManager = new ImageManager(this.#xmlParser)
|
|
170
|
+
this.#textManager = new TextManager(this.#xmlParser)
|
|
171
|
+
this.#templateEngine = new TemplateEngine(this.#xmlParser)
|
|
172
|
+
this.#outputWriter = new OutputWriter(this.#zipManager, this.#contentTypesManager)
|
|
144
173
|
}
|
|
145
174
|
|
|
146
175
|
/**
|
|
@@ -160,9 +189,9 @@ class PPTXTemplater {
|
|
|
160
189
|
* const ppt = await PPTXTemplater.load(buffer);
|
|
161
190
|
*/
|
|
162
191
|
static async load(source) {
|
|
163
|
-
const engine = new PPTXTemplater()
|
|
164
|
-
await engine.#initialize(source)
|
|
165
|
-
return engine
|
|
192
|
+
const engine = new PPTXTemplater()
|
|
193
|
+
await engine.#initialize(source)
|
|
194
|
+
return engine
|
|
166
195
|
}
|
|
167
196
|
|
|
168
197
|
/**
|
|
@@ -177,9 +206,9 @@ class PPTXTemplater {
|
|
|
177
206
|
* await ppt.saveToFile('./new.pptx');
|
|
178
207
|
*/
|
|
179
208
|
static async create() {
|
|
180
|
-
const engine = new PPTXTemplater()
|
|
181
|
-
await engine.#initializeBlank()
|
|
182
|
-
return engine
|
|
209
|
+
const engine = new PPTXTemplater()
|
|
210
|
+
await engine.#initializeBlank()
|
|
211
|
+
return engine
|
|
183
212
|
}
|
|
184
213
|
|
|
185
214
|
/**
|
|
@@ -188,31 +217,31 @@ class PPTXTemplater {
|
|
|
188
217
|
* @param {string|Buffer} source
|
|
189
218
|
*/
|
|
190
219
|
async #initialize(source) {
|
|
191
|
-
logger.debug(`Loading PPTX from ${typeof source === 'string' ? source : 'buffer'}`)
|
|
220
|
+
logger.debug(`Loading PPTX from ${typeof source === 'string' ? source : 'buffer'}`)
|
|
192
221
|
|
|
193
222
|
// Load and extract the ZIP archive (PPTX is just a ZIP)
|
|
194
|
-
await this.#zipManager.load(source)
|
|
223
|
+
await this.#zipManager.load(source)
|
|
195
224
|
|
|
196
225
|
// Initialize content types manager first!
|
|
197
|
-
await this.#contentTypesManager.initialize(this.#zipManager)
|
|
226
|
+
await this.#contentTypesManager.initialize(this.#zipManager)
|
|
198
227
|
|
|
199
228
|
// Parse the core presentation relationships and structure
|
|
200
|
-
await this.#relationshipManager.initialize(this.#zipManager)
|
|
229
|
+
await this.#relationshipManager.initialize(this.#zipManager)
|
|
201
230
|
|
|
202
231
|
// Load all slide references from presentation.xml
|
|
203
|
-
await this.#slideManager.initialize(this.#zipManager)
|
|
232
|
+
await this.#slideManager.initialize(this.#zipManager)
|
|
204
233
|
|
|
205
234
|
// Pre-load all slide XML into cache to allow synchronous operations like replaceText()
|
|
206
|
-
await this.#slideManager.preloadAll()
|
|
235
|
+
await this.#slideManager.preloadAll()
|
|
207
236
|
|
|
208
237
|
// Initialize chart manager with zip context
|
|
209
|
-
await this.#chartManager.initialize(this.#zipManager)
|
|
238
|
+
await this.#chartManager.initialize(this.#zipManager)
|
|
210
239
|
|
|
211
240
|
// Deduplicate and index media files
|
|
212
|
-
await this.#mediaManager.initialize(this.#zipManager)
|
|
241
|
+
await this.#mediaManager.initialize(this.#zipManager)
|
|
213
242
|
|
|
214
|
-
this.#loaded = true
|
|
215
|
-
logger.debug(`Loaded ${this.#slideManager.slideCount} slides successfully`)
|
|
243
|
+
this.#loaded = true
|
|
244
|
+
logger.debug(`Loaded ${this.#slideManager.slideCount} slides successfully`)
|
|
216
245
|
}
|
|
217
246
|
|
|
218
247
|
/**
|
|
@@ -220,13 +249,13 @@ class PPTXTemplater {
|
|
|
220
249
|
* @private
|
|
221
250
|
*/
|
|
222
251
|
async #initializeBlank() {
|
|
223
|
-
await this.#zipManager.createBlank()
|
|
224
|
-
await this.#contentTypesManager.initialize(this.#zipManager)
|
|
225
|
-
await this.#relationshipManager.initialize(this.#zipManager)
|
|
226
|
-
await this.#slideManager.initialize(this.#zipManager)
|
|
227
|
-
await this.#chartManager.initialize(this.#zipManager)
|
|
228
|
-
await this.#mediaManager.initialize(this.#zipManager)
|
|
229
|
-
this.#loaded = true
|
|
252
|
+
await this.#zipManager.createBlank()
|
|
253
|
+
await this.#contentTypesManager.initialize(this.#zipManager)
|
|
254
|
+
await this.#relationshipManager.initialize(this.#zipManager)
|
|
255
|
+
await this.#slideManager.initialize(this.#zipManager)
|
|
256
|
+
await this.#chartManager.initialize(this.#zipManager)
|
|
257
|
+
await this.#mediaManager.initialize(this.#zipManager)
|
|
258
|
+
this.#loaded = true
|
|
230
259
|
}
|
|
231
260
|
|
|
232
261
|
/**
|
|
@@ -235,7 +264,7 @@ class PPTXTemplater {
|
|
|
235
264
|
*/
|
|
236
265
|
#assertLoaded() {
|
|
237
266
|
if (!this.#loaded) {
|
|
238
|
-
throw new PPTXError('Engine not initialized. Call PPTXTemplater.load() first.')
|
|
267
|
+
throw new PPTXError('Engine not initialized. Call PPTXTemplater.load() first.')
|
|
239
268
|
}
|
|
240
269
|
}
|
|
241
270
|
|
|
@@ -253,10 +282,10 @@ class PPTXTemplater {
|
|
|
253
282
|
* ppt.useSlide('intro'); // Select by custom tag
|
|
254
283
|
*/
|
|
255
284
|
useSlide(...slideRefs) {
|
|
256
|
-
this.#assertLoaded()
|
|
257
|
-
this.#selectedSlides = slideRefs
|
|
258
|
-
logger.debug(`Selected slides: ${slideRefs.join(', ')}`)
|
|
259
|
-
return this
|
|
285
|
+
this.#assertLoaded()
|
|
286
|
+
this.#selectedSlides = slideRefs
|
|
287
|
+
logger.debug(`Selected slides: ${slideRefs.join(', ')}`)
|
|
288
|
+
return this
|
|
260
289
|
}
|
|
261
290
|
|
|
262
291
|
/**
|
|
@@ -264,9 +293,9 @@ class PPTXTemplater {
|
|
|
264
293
|
* @returns {PPTXTemplater} this (chainable)
|
|
265
294
|
*/
|
|
266
295
|
useAllSlides() {
|
|
267
|
-
this.#assertLoaded()
|
|
268
|
-
this.#selectedSlides = []
|
|
269
|
-
return this
|
|
296
|
+
this.#assertLoaded()
|
|
297
|
+
this.#selectedSlides = []
|
|
298
|
+
return this
|
|
270
299
|
}
|
|
271
300
|
|
|
272
301
|
/**
|
|
@@ -277,13 +306,13 @@ class PPTXTemplater {
|
|
|
277
306
|
*/
|
|
278
307
|
#getTargetSlideIndices() {
|
|
279
308
|
if (this.#selectedSlides.length === 0) {
|
|
280
|
-
return this.#slideManager.getAllSlideIndices()
|
|
309
|
+
return this.#slideManager.getAllSlideIndices()
|
|
281
310
|
}
|
|
282
311
|
return this.#selectedSlides.flatMap(ref => {
|
|
283
|
-
if (typeof ref === 'number') return [ref]
|
|
312
|
+
if (typeof ref === 'number') return [ref]
|
|
284
313
|
// Resolve by tag or ID
|
|
285
|
-
return this.#slideManager.resolveSlideRef(ref)
|
|
286
|
-
})
|
|
314
|
+
return this.#slideManager.resolveSlideRef(ref)
|
|
315
|
+
})
|
|
287
316
|
}
|
|
288
317
|
|
|
289
318
|
/**
|
|
@@ -301,17 +330,19 @@ class PPTXTemplater {
|
|
|
301
330
|
* });
|
|
302
331
|
*/
|
|
303
332
|
replaceText(replacements) {
|
|
304
|
-
this.#assertLoaded()
|
|
305
|
-
const targetIndices = this.#getTargetSlideIndices()
|
|
333
|
+
this.#assertLoaded()
|
|
334
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
306
335
|
|
|
307
336
|
for (const slideIndex of targetIndices) {
|
|
308
|
-
const slideXml = this.#slideManager.getSlideXml(slideIndex)
|
|
309
|
-
const updated = this.#templateEngine.replaceTextInXml(slideXml, replacements)
|
|
310
|
-
this.#slideManager.setSlideXml(slideIndex, updated)
|
|
337
|
+
const slideXml = this.#slideManager.getSlideXml(slideIndex)
|
|
338
|
+
const updated = this.#templateEngine.replaceTextInXml(slideXml, replacements)
|
|
339
|
+
this.#slideManager.setSlideXml(slideIndex, updated)
|
|
311
340
|
}
|
|
312
341
|
|
|
313
|
-
logger.debug(
|
|
314
|
-
|
|
342
|
+
logger.debug(
|
|
343
|
+
`Replaced ${Object.keys(replacements).length} placeholder(s) in ${targetIndices.length} slide(s)`
|
|
344
|
+
)
|
|
345
|
+
return this
|
|
315
346
|
}
|
|
316
347
|
|
|
317
348
|
/**
|
|
@@ -334,8 +365,8 @@ class PPTXTemplater {
|
|
|
334
365
|
* });
|
|
335
366
|
*/
|
|
336
367
|
updateChart(chartId, data) {
|
|
337
|
-
this.#assertLoaded()
|
|
338
|
-
const targetIndices = this.#getTargetSlideIndices()
|
|
368
|
+
this.#assertLoaded()
|
|
369
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
339
370
|
|
|
340
371
|
for (const slideIndex of targetIndices) {
|
|
341
372
|
this.#chartManager.updateChart(
|
|
@@ -344,11 +375,11 @@ class PPTXTemplater {
|
|
|
344
375
|
data,
|
|
345
376
|
this.#slideManager,
|
|
346
377
|
this.#relationshipManager
|
|
347
|
-
)
|
|
378
|
+
)
|
|
348
379
|
}
|
|
349
380
|
|
|
350
|
-
logger.debug(`Updated chart "${chartId}" in ${targetIndices.length} slide(s)`)
|
|
351
|
-
return this
|
|
381
|
+
logger.debug(`Updated chart "${chartId}" in ${targetIndices.length} slide(s)`)
|
|
382
|
+
return this
|
|
352
383
|
}
|
|
353
384
|
|
|
354
385
|
/**
|
|
@@ -367,15 +398,15 @@ class PPTXTemplater {
|
|
|
367
398
|
* ]);
|
|
368
399
|
*/
|
|
369
400
|
updateTable(tableId, rows) {
|
|
370
|
-
this.#assertLoaded()
|
|
371
|
-
const targetIndices = this.#getTargetSlideIndices()
|
|
401
|
+
this.#assertLoaded()
|
|
402
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
372
403
|
|
|
373
404
|
for (const slideIndex of targetIndices) {
|
|
374
|
-
this.#tableManager.updateTable(slideIndex, tableId, rows, this.#slideManager)
|
|
405
|
+
this.#tableManager.updateTable(slideIndex, tableId, rows, this.#slideManager)
|
|
375
406
|
}
|
|
376
407
|
|
|
377
|
-
logger.debug(`Updated table "${tableId}" in ${targetIndices.length} slide(s)`)
|
|
378
|
-
return this
|
|
408
|
+
logger.debug(`Updated table "${tableId}" in ${targetIndices.length} slide(s)`)
|
|
409
|
+
return this
|
|
379
410
|
}
|
|
380
411
|
|
|
381
412
|
/**
|
|
@@ -391,8 +422,8 @@ class PPTXTemplater {
|
|
|
391
422
|
* ppt.addHyperlink({ text: 'Open Website', url: 'https://example.com' });
|
|
392
423
|
*/
|
|
393
424
|
addHyperlink(options) {
|
|
394
|
-
this.#assertLoaded()
|
|
395
|
-
const targetIndices = this.#getTargetSlideIndices()
|
|
425
|
+
this.#assertLoaded()
|
|
426
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
396
427
|
|
|
397
428
|
for (const slideIndex of targetIndices) {
|
|
398
429
|
this.#hyperlinkManager.addExternalHyperlink(
|
|
@@ -400,10 +431,10 @@ class PPTXTemplater {
|
|
|
400
431
|
options,
|
|
401
432
|
this.#slideManager,
|
|
402
433
|
this.#relationshipManager
|
|
403
|
-
)
|
|
434
|
+
)
|
|
404
435
|
}
|
|
405
436
|
|
|
406
|
-
return this
|
|
437
|
+
return this
|
|
407
438
|
}
|
|
408
439
|
|
|
409
440
|
/**
|
|
@@ -416,8 +447,8 @@ class PPTXTemplater {
|
|
|
416
447
|
* @returns {PPTXTemplater} this (chainable)
|
|
417
448
|
*/
|
|
418
449
|
addSlideLink(options) {
|
|
419
|
-
this.#assertLoaded()
|
|
420
|
-
const { sourceSlide, targetSlide, element } = options
|
|
450
|
+
this.#assertLoaded()
|
|
451
|
+
const { sourceSlide, targetSlide, element } = options
|
|
421
452
|
|
|
422
453
|
// Fallback: If no element text is provided, link the slide number (legacy behavior)
|
|
423
454
|
if (!element) {
|
|
@@ -426,7 +457,7 @@ class PPTXTemplater {
|
|
|
426
457
|
targetSlide,
|
|
427
458
|
this.#slideManager,
|
|
428
459
|
this.#relationshipManager
|
|
429
|
-
)
|
|
460
|
+
)
|
|
430
461
|
} else {
|
|
431
462
|
// Add a slide hyperlink on specific text
|
|
432
463
|
this.#hyperlinkManager.addTextSlideLink(
|
|
@@ -435,9 +466,9 @@ class PPTXTemplater {
|
|
|
435
466
|
targetSlide,
|
|
436
467
|
this.#slideManager,
|
|
437
468
|
this.#relationshipManager
|
|
438
|
-
)
|
|
469
|
+
)
|
|
439
470
|
}
|
|
440
|
-
return this
|
|
471
|
+
return this
|
|
441
472
|
}
|
|
442
473
|
|
|
443
474
|
/**
|
|
@@ -450,15 +481,15 @@ class PPTXTemplater {
|
|
|
450
481
|
* @returns {PPTXTemplater} this
|
|
451
482
|
*/
|
|
452
483
|
addImageLink(options) {
|
|
453
|
-
this.#assertLoaded()
|
|
484
|
+
this.#assertLoaded()
|
|
454
485
|
this.#hyperlinkManager.addShapeSlideLink(
|
|
455
486
|
options.slide,
|
|
456
487
|
options.imageId,
|
|
457
488
|
options.targetSlide,
|
|
458
489
|
this.#slideManager,
|
|
459
490
|
this.#relationshipManager
|
|
460
|
-
)
|
|
461
|
-
return this
|
|
491
|
+
)
|
|
492
|
+
return this
|
|
462
493
|
}
|
|
463
494
|
|
|
464
495
|
/**
|
|
@@ -471,15 +502,15 @@ class PPTXTemplater {
|
|
|
471
502
|
* @returns {PPTXTemplater} this
|
|
472
503
|
*/
|
|
473
504
|
addShapeLink(options) {
|
|
474
|
-
this.#assertLoaded()
|
|
505
|
+
this.#assertLoaded()
|
|
475
506
|
this.#hyperlinkManager.addShapeSlideLink(
|
|
476
507
|
options.slide,
|
|
477
508
|
options.shapeId,
|
|
478
509
|
options.targetSlide,
|
|
479
510
|
this.#slideManager,
|
|
480
511
|
this.#relationshipManager
|
|
481
|
-
)
|
|
482
|
-
return this
|
|
512
|
+
)
|
|
513
|
+
return this
|
|
483
514
|
}
|
|
484
515
|
|
|
485
516
|
/**
|
|
@@ -492,10 +523,10 @@ class PPTXTemplater {
|
|
|
492
523
|
* @returns {PPTXTemplater} this (chainable)
|
|
493
524
|
*/
|
|
494
525
|
addTextNavigationLink(options) {
|
|
495
|
-
this.#assertLoaded()
|
|
496
|
-
const { slide, element, action } = options
|
|
497
|
-
this.#hyperlinkManager.addTextNavigationLink(slide, element, action, this.#slideManager)
|
|
498
|
-
return this
|
|
526
|
+
this.#assertLoaded()
|
|
527
|
+
const { slide, element, action } = options
|
|
528
|
+
this.#hyperlinkManager.addTextNavigationLink(slide, element, action, this.#slideManager)
|
|
529
|
+
return this
|
|
499
530
|
}
|
|
500
531
|
|
|
501
532
|
/**
|
|
@@ -508,10 +539,10 @@ class PPTXTemplater {
|
|
|
508
539
|
* @returns {PPTXTemplater} this (chainable)
|
|
509
540
|
*/
|
|
510
541
|
addShapeNavigationLink(options) {
|
|
511
|
-
this.#assertLoaded()
|
|
512
|
-
const { slide, shapeId, action } = options
|
|
513
|
-
this.#hyperlinkManager.addShapeNavigationLink(slide, shapeId, action, this.#slideManager)
|
|
514
|
-
return this
|
|
542
|
+
this.#assertLoaded()
|
|
543
|
+
const { slide, shapeId, action } = options
|
|
544
|
+
this.#hyperlinkManager.addShapeNavigationLink(slide, shapeId, action, this.#slideManager)
|
|
545
|
+
return this
|
|
515
546
|
}
|
|
516
547
|
|
|
517
548
|
/**
|
|
@@ -534,10 +565,10 @@ class PPTXTemplater {
|
|
|
534
565
|
* });
|
|
535
566
|
*/
|
|
536
567
|
addSlide(options = {}) {
|
|
537
|
-
this.#assertLoaded()
|
|
538
|
-
this.#slideManager.addNewSlide(options, this.#relationshipManager, this.#mediaManager)
|
|
539
|
-
logger.debug(`Added new slide: "${options.title || 'Untitled'}"`)
|
|
540
|
-
return this
|
|
568
|
+
this.#assertLoaded()
|
|
569
|
+
this.#slideManager.addNewSlide(options, this.#relationshipManager, this.#mediaManager)
|
|
570
|
+
logger.debug(`Added new slide: "${options.title || 'Untitled'}"`)
|
|
571
|
+
return this
|
|
541
572
|
}
|
|
542
573
|
|
|
543
574
|
/**
|
|
@@ -548,9 +579,9 @@ class PPTXTemplater {
|
|
|
548
579
|
* @returns {PPTXTemplater} this (chainable)
|
|
549
580
|
*/
|
|
550
581
|
cloneSlide(sourceSlideNumber, atPosition) {
|
|
551
|
-
this.#assertLoaded()
|
|
552
|
-
this.#slideManager.cloneSlide(sourceSlideNumber, atPosition, this.#relationshipManager)
|
|
553
|
-
return this
|
|
582
|
+
this.#assertLoaded()
|
|
583
|
+
this.#slideManager.cloneSlide(sourceSlideNumber, atPosition, this.#relationshipManager)
|
|
584
|
+
return this
|
|
554
585
|
}
|
|
555
586
|
|
|
556
587
|
/**
|
|
@@ -560,9 +591,9 @@ class PPTXTemplater {
|
|
|
560
591
|
* @returns {PPTXTemplater} this (chainable)
|
|
561
592
|
*/
|
|
562
593
|
removeSlide(slideNumber) {
|
|
563
|
-
this.#assertLoaded()
|
|
564
|
-
this.#slideManager.removeSlide(slideNumber)
|
|
565
|
-
return this
|
|
594
|
+
this.#assertLoaded()
|
|
595
|
+
this.#slideManager.removeSlide(slideNumber)
|
|
596
|
+
return this
|
|
566
597
|
}
|
|
567
598
|
|
|
568
599
|
/**
|
|
@@ -575,9 +606,9 @@ class PPTXTemplater {
|
|
|
575
606
|
* ppt.reorderSlides([3, 1, 2]); // Move slide 3 to position 1
|
|
576
607
|
*/
|
|
577
608
|
reorderSlides(order) {
|
|
578
|
-
this.#assertLoaded()
|
|
579
|
-
this.#slideManager.reorderSlides(order)
|
|
580
|
-
return this
|
|
609
|
+
this.#assertLoaded()
|
|
610
|
+
this.#slideManager.reorderSlides(order)
|
|
611
|
+
return this
|
|
581
612
|
}
|
|
582
613
|
|
|
583
614
|
/**
|
|
@@ -592,9 +623,9 @@ class PPTXTemplater {
|
|
|
592
623
|
* ppt.useSlide('intro').replaceText({ '{{title}}': 'Hello' });
|
|
593
624
|
*/
|
|
594
625
|
tagSlide(slideNumber, tag) {
|
|
595
|
-
this.#assertLoaded()
|
|
596
|
-
this.#slideManager.tagSlide(slideNumber, tag)
|
|
597
|
-
return this
|
|
626
|
+
this.#assertLoaded()
|
|
627
|
+
this.#slideManager.tagSlide(slideNumber, tag)
|
|
628
|
+
return this
|
|
598
629
|
}
|
|
599
630
|
|
|
600
631
|
/**
|
|
@@ -609,8 +640,8 @@ class PPTXTemplater {
|
|
|
609
640
|
* await subset.saveToFile('./subset.pptx');
|
|
610
641
|
*/
|
|
611
642
|
async exportSlides(...slideNumbers) {
|
|
612
|
-
this.#assertLoaded()
|
|
613
|
-
return this.#slideManager.exportSlides(slideNumbers, this)
|
|
643
|
+
this.#assertLoaded()
|
|
644
|
+
return this.#slideManager.exportSlides(slideNumbers, this)
|
|
614
645
|
}
|
|
615
646
|
|
|
616
647
|
/**
|
|
@@ -622,9 +653,9 @@ class PPTXTemplater {
|
|
|
622
653
|
* @returns {Promise<PPTXTemplater>} this (chainable)
|
|
623
654
|
*/
|
|
624
655
|
async importSlideFrom(sourceEngine, slideRef) {
|
|
625
|
-
this.#assertLoaded()
|
|
626
|
-
await this.#slideManager.importSlide(sourceEngine, slideRef, this.#mediaManager)
|
|
627
|
-
return this
|
|
656
|
+
this.#assertLoaded()
|
|
657
|
+
await this.#slideManager.importSlide(sourceEngine, slideRef, this.#mediaManager)
|
|
658
|
+
return this
|
|
628
659
|
}
|
|
629
660
|
|
|
630
661
|
/**
|
|
@@ -639,32 +670,34 @@ class PPTXTemplater {
|
|
|
639
670
|
* ppt.importSlides([1, 3, 5]);
|
|
640
671
|
*/
|
|
641
672
|
importSlides(slideIndices) {
|
|
642
|
-
this.#assertLoaded()
|
|
643
|
-
const slidesToKeep = slideIndices.map(i => this.#slideManager.getSlideInfo(i).slideId)
|
|
673
|
+
this.#assertLoaded()
|
|
674
|
+
const slidesToKeep = slideIndices.map(i => this.#slideManager.getSlideInfo(i).slideId)
|
|
644
675
|
|
|
645
676
|
// Remove unneeded slides from highest to lowest index to avoid shifting issues
|
|
646
|
-
const allIndices = this.#slideManager.getAllSlideIndices()
|
|
677
|
+
const allIndices = this.#slideManager.getAllSlideIndices()
|
|
647
678
|
for (let i = allIndices.length; i >= 1; i--) {
|
|
648
|
-
const info = this.#slideManager.getSlideInfo(i)
|
|
679
|
+
const info = this.#slideManager.getSlideInfo(i)
|
|
649
680
|
if (!slidesToKeep.includes(info.slideId)) {
|
|
650
|
-
this.#slideManager.removeSlide(i)
|
|
681
|
+
this.#slideManager.removeSlide(i)
|
|
651
682
|
}
|
|
652
683
|
}
|
|
653
684
|
|
|
654
685
|
// Calculate new target order based on the requested slideIndices
|
|
655
|
-
const currentOrder = this.#slideManager
|
|
686
|
+
const currentOrder = this.#slideManager
|
|
687
|
+
.getAllSlideIndices()
|
|
688
|
+
.map(i => this.#slideManager.getSlideInfo(i).slideId)
|
|
656
689
|
|
|
657
690
|
const newOrder = slidesToKeep.map(id => {
|
|
658
|
-
return currentOrder.indexOf(id) + 1
|
|
659
|
-
})
|
|
691
|
+
return currentOrder.indexOf(id) + 1
|
|
692
|
+
})
|
|
660
693
|
|
|
661
694
|
// Only reorder if needed
|
|
662
695
|
if (newOrder.join(',') !== currentOrder.map((_, i) => i + 1).join(',')) {
|
|
663
|
-
this.#slideManager.reorderSlides(newOrder)
|
|
696
|
+
this.#slideManager.reorderSlides(newOrder)
|
|
664
697
|
}
|
|
665
698
|
|
|
666
|
-
logger.debug(`Imported ${slideIndices.length} slide(s).`)
|
|
667
|
-
return this
|
|
699
|
+
logger.debug(`Imported ${slideIndices.length} slide(s).`)
|
|
700
|
+
return this
|
|
668
701
|
}
|
|
669
702
|
|
|
670
703
|
/**
|
|
@@ -673,7 +706,7 @@ class PPTXTemplater {
|
|
|
673
706
|
* @returns {PresentationInfo} Metadata object.
|
|
674
707
|
*/
|
|
675
708
|
getInfo() {
|
|
676
|
-
this.#assertLoaded()
|
|
709
|
+
this.#assertLoaded()
|
|
677
710
|
return {
|
|
678
711
|
slideCount: this.#slideManager.slideCount,
|
|
679
712
|
title: this.#zipManager.getCoreProperty('dc:title') || '',
|
|
@@ -682,7 +715,7 @@ class PPTXTemplater {
|
|
|
682
715
|
modified: this.#zipManager.getCoreProperty('dcterms:modified') || '',
|
|
683
716
|
slides: this.#slideManager.getAllSlideInfo(),
|
|
684
717
|
mediaCount: this.#mediaManager.mediaCount,
|
|
685
|
-
}
|
|
718
|
+
}
|
|
686
719
|
}
|
|
687
720
|
|
|
688
721
|
/**
|
|
@@ -692,8 +725,8 @@ class PPTXTemplater {
|
|
|
692
725
|
* @returns {ValidationResult} Object with `valid`, `errors`, and `warnings` arrays.
|
|
693
726
|
*/
|
|
694
727
|
validate() {
|
|
695
|
-
this.#assertLoaded()
|
|
696
|
-
return this.#slideManager.validateStructure(this.#relationshipManager, this.#zipManager)
|
|
728
|
+
this.#assertLoaded()
|
|
729
|
+
return this.#slideManager.validateStructure(this.#relationshipManager, this.#zipManager)
|
|
697
730
|
}
|
|
698
731
|
|
|
699
732
|
/**
|
|
@@ -703,16 +736,16 @@ class PPTXTemplater {
|
|
|
703
736
|
* @returns {Promise<PPTXTemplater>} this (chainable)
|
|
704
737
|
*/
|
|
705
738
|
async repair() {
|
|
706
|
-
this.#assertLoaded()
|
|
739
|
+
this.#assertLoaded()
|
|
707
740
|
|
|
708
741
|
// 1. Rebuild presentation.xml slide mappings
|
|
709
|
-
this.#slideManager.rebuildPresentationSlideOrder()
|
|
742
|
+
this.#slideManager.rebuildPresentationSlideOrder()
|
|
710
743
|
|
|
711
744
|
// 2. Remove orphan relationships
|
|
712
|
-
this.#relationshipManager.removeOrphanRelationships(this.#zipManager)
|
|
745
|
+
this.#relationshipManager.removeOrphanRelationships(this.#zipManager)
|
|
713
746
|
|
|
714
|
-
logger.info('PPTX repair complete.')
|
|
715
|
-
return this
|
|
747
|
+
logger.info('PPTX repair complete.')
|
|
748
|
+
return this
|
|
716
749
|
}
|
|
717
750
|
|
|
718
751
|
/**
|
|
@@ -720,15 +753,17 @@ class PPTXTemplater {
|
|
|
720
753
|
* @returns {PPTXTemplater} this (chainable)
|
|
721
754
|
*/
|
|
722
755
|
debugRelationships() {
|
|
723
|
-
this.#assertLoaded()
|
|
724
|
-
const files = this.#zipManager.listFiles('').filter(f => f.endsWith('.rels'))
|
|
725
|
-
console.log('=== Relationship Graph ===')
|
|
756
|
+
this.#assertLoaded()
|
|
757
|
+
const files = this.#zipManager.listFiles('').filter(f => f.endsWith('.rels'))
|
|
758
|
+
console.log('=== Relationship Graph ===')
|
|
726
759
|
for (const file of files) {
|
|
727
|
-
console.log(`\n${file}:`)
|
|
728
|
-
const rels = this.#relationshipManager.getRelationships(
|
|
729
|
-
|
|
760
|
+
console.log(`\n${file}:`)
|
|
761
|
+
const rels = this.#relationshipManager.getRelationships(
|
|
762
|
+
file.replace('_rels/', '').replace('.rels', '')
|
|
763
|
+
)
|
|
764
|
+
rels.forEach(r => console.log(` - ${r.id} [${r.type.split('/').pop()}] -> ${r.target}`))
|
|
730
765
|
}
|
|
731
|
-
return this
|
|
766
|
+
return this
|
|
732
767
|
}
|
|
733
768
|
|
|
734
769
|
/**
|
|
@@ -737,21 +772,21 @@ class PPTXTemplater {
|
|
|
737
772
|
* @returns {PPTXTemplater} this (chainable)
|
|
738
773
|
*/
|
|
739
774
|
inspectSlide(slideIndex) {
|
|
740
|
-
this.#assertLoaded()
|
|
741
|
-
const info = this.#slideManager.getSlideInfo(slideIndex)
|
|
742
|
-
const xml = this.#slideManager.getSlideXml(slideIndex)
|
|
743
|
-
const rels = this.#relationshipManager.getRelationships(info.zipPath)
|
|
744
|
-
|
|
745
|
-
console.log(`=== Slide ${slideIndex} Inspection ===`)
|
|
746
|
-
console.log(`Path: ${info.zipPath}`)
|
|
747
|
-
console.log(`ID: ${info.slideId}`)
|
|
748
|
-
console.log(`rId: ${info.relationshipId}`)
|
|
749
|
-
console.log(`Title: ${info.title}`)
|
|
750
|
-
console.log(`XML Size: ${xml.length} characters`)
|
|
751
|
-
console.log(`Relationships (${rels.length}):`)
|
|
752
|
-
rels.forEach(r => console.log(` - ${r.id} [${r.type.split('/').pop()}] -> ${r.target}`))
|
|
753
|
-
|
|
754
|
-
return this
|
|
775
|
+
this.#assertLoaded()
|
|
776
|
+
const info = this.#slideManager.getSlideInfo(slideIndex)
|
|
777
|
+
const xml = this.#slideManager.getSlideXml(slideIndex)
|
|
778
|
+
const rels = this.#relationshipManager.getRelationships(info.zipPath)
|
|
779
|
+
|
|
780
|
+
console.log(`=== Slide ${slideIndex} Inspection ===`)
|
|
781
|
+
console.log(`Path: ${info.zipPath}`)
|
|
782
|
+
console.log(`ID: ${info.slideId}`)
|
|
783
|
+
console.log(`rId: ${info.relationshipId}`)
|
|
784
|
+
console.log(`Title: ${info.title}`)
|
|
785
|
+
console.log(`XML Size: ${xml.length} characters`)
|
|
786
|
+
console.log(`Relationships (${rels.length}):`)
|
|
787
|
+
rels.forEach(r => console.log(` - ${r.id} [${r.type.split('/').pop()}] -> ${r.target}`))
|
|
788
|
+
|
|
789
|
+
return this
|
|
755
790
|
}
|
|
756
791
|
|
|
757
792
|
/**
|
|
@@ -760,15 +795,15 @@ class PPTXTemplater {
|
|
|
760
795
|
* @returns {Promise<PPTXTemplater>} this (chainable)
|
|
761
796
|
*/
|
|
762
797
|
async inspectXML(xmlPath) {
|
|
763
|
-
this.#assertLoaded()
|
|
764
|
-
const xml = await this.#zipManager.readFile(xmlPath)
|
|
765
|
-
console.log(`=== XML Inspection: ${xmlPath} ===`)
|
|
798
|
+
this.#assertLoaded()
|
|
799
|
+
const xml = await this.#zipManager.readFile(xmlPath)
|
|
800
|
+
console.log(`=== XML Inspection: ${xmlPath} ===`)
|
|
766
801
|
if (!xml) {
|
|
767
|
-
console.log('(File not found or empty)')
|
|
802
|
+
console.log('(File not found or empty)')
|
|
768
803
|
} else {
|
|
769
|
-
console.log(xml.substring(0, 1500) + (xml.length > 1500 ? '...\n[Truncated]' : ''))
|
|
804
|
+
console.log(xml.substring(0, 1500) + (xml.length > 1500 ? '...\n[Truncated]' : ''))
|
|
770
805
|
}
|
|
771
|
-
return this
|
|
806
|
+
return this
|
|
772
807
|
}
|
|
773
808
|
|
|
774
809
|
/**
|
|
@@ -778,26 +813,29 @@ class PPTXTemplater {
|
|
|
778
813
|
* @returns {Promise<Object>} Validation results for charts.
|
|
779
814
|
*/
|
|
780
815
|
async validateCharts() {
|
|
781
|
-
this.#assertLoaded()
|
|
782
|
-
const issues = { valid: true, errors: [], warnings: [] }
|
|
816
|
+
this.#assertLoaded()
|
|
817
|
+
const issues = { valid: true, errors: [], warnings: [] }
|
|
783
818
|
|
|
784
819
|
// We lazy require ChartRelationshipManager so we don't circularly depend if not needed
|
|
785
|
-
const { ChartRelationshipManager } = require('../managers/charts/ChartRelationshipManager.js')
|
|
820
|
+
const { ChartRelationshipManager } = require('../managers/charts/ChartRelationshipManager.js')
|
|
786
821
|
|
|
787
|
-
const chartFiles = this.#zipManager.listFiles('ppt/charts/')
|
|
788
|
-
.
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
});
|
|
822
|
+
const chartFiles = this.#zipManager.listFiles('ppt/charts/').filter(f => {
|
|
823
|
+
const name = f.split('/').pop()
|
|
824
|
+
return name.startsWith('chart') && name.endsWith('.xml') && !f.includes('_rels')
|
|
825
|
+
})
|
|
792
826
|
|
|
793
827
|
for (const chartPath of chartFiles) {
|
|
794
|
-
const relIssues = ChartRelationshipManager.validateChartRelationships(
|
|
795
|
-
|
|
796
|
-
|
|
828
|
+
const relIssues = ChartRelationshipManager.validateChartRelationships(
|
|
829
|
+
this.#relationshipManager,
|
|
830
|
+
this.#zipManager,
|
|
831
|
+
chartPath
|
|
832
|
+
)
|
|
833
|
+
issues.errors.push(...relIssues.errors)
|
|
834
|
+
issues.warnings.push(...relIssues.warnings)
|
|
797
835
|
}
|
|
798
836
|
|
|
799
|
-
if (issues.errors.length > 0) issues.valid = false
|
|
800
|
-
return issues
|
|
837
|
+
if (issues.errors.length > 0) issues.valid = false
|
|
838
|
+
return issues
|
|
801
839
|
}
|
|
802
840
|
|
|
803
841
|
/**
|
|
@@ -807,34 +845,40 @@ class PPTXTemplater {
|
|
|
807
845
|
* @returns {Promise<PPTXTemplater>} this
|
|
808
846
|
*/
|
|
809
847
|
async repairCharts() {
|
|
810
|
-
this.#assertLoaded()
|
|
811
|
-
logger.info('Repairing charts...')
|
|
848
|
+
this.#assertLoaded()
|
|
849
|
+
logger.info('Repairing charts...')
|
|
812
850
|
|
|
813
851
|
// Check all charts for missing embedded workbooks
|
|
814
|
-
const chartFiles = this.#zipManager.listFiles('ppt/charts/')
|
|
815
|
-
.
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
});
|
|
852
|
+
const chartFiles = this.#zipManager.listFiles('ppt/charts/').filter(f => {
|
|
853
|
+
const name = f.split('/').pop()
|
|
854
|
+
return name.startsWith('chart') && name.endsWith('.xml') && !f.includes('_rels')
|
|
855
|
+
})
|
|
819
856
|
for (const chartPath of chartFiles) {
|
|
820
|
-
const rels = this.#relationshipManager.getRelationshipsByType(
|
|
857
|
+
const rels = this.#relationshipManager.getRelationshipsByType(
|
|
858
|
+
chartPath,
|
|
859
|
+
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package'
|
|
860
|
+
)
|
|
821
861
|
for (const rel of rels) {
|
|
822
|
-
const xlsxPath = this.#relationshipManager.resolveTarget(chartPath, rel.target)
|
|
862
|
+
const xlsxPath = this.#relationshipManager.resolveTarget(chartPath, rel.target)
|
|
823
863
|
if (!this.#zipManager.hasFile(xlsxPath)) {
|
|
824
|
-
logger.warn(
|
|
825
|
-
|
|
864
|
+
logger.warn(
|
|
865
|
+
`Chart ${chartPath} has broken workbook reference ${rel.id}, removing to prevent repair mode.`
|
|
866
|
+
)
|
|
867
|
+
this.#relationshipManager.removeRelationship(chartPath, rel.id)
|
|
826
868
|
|
|
827
869
|
// Also strip c:externalData from chart XML to prevent PowerPoint looking for it
|
|
828
|
-
const xml = await this.#zipManager.readFile(chartPath)
|
|
870
|
+
const xml = await this.#zipManager.readFile(chartPath)
|
|
829
871
|
if (xml) {
|
|
830
|
-
const updated = xml
|
|
831
|
-
|
|
872
|
+
const updated = xml
|
|
873
|
+
.replace(/<c:externalData[^>]*r:id="[^"]*"[^>]*>/, '')
|
|
874
|
+
.replace(/<\/c:externalData>/, '')
|
|
875
|
+
this.#zipManager.writeFile(chartPath, updated)
|
|
832
876
|
}
|
|
833
877
|
}
|
|
834
878
|
}
|
|
835
879
|
}
|
|
836
880
|
|
|
837
|
-
return this
|
|
881
|
+
return this
|
|
838
882
|
}
|
|
839
883
|
|
|
840
884
|
/**
|
|
@@ -843,25 +887,31 @@ class PPTXTemplater {
|
|
|
843
887
|
* @param {string} chartId
|
|
844
888
|
*/
|
|
845
889
|
inspectChart(chartId) {
|
|
846
|
-
this.#assertLoaded()
|
|
847
|
-
console.log(`=== Chart Inspection: ${chartId} ===`)
|
|
890
|
+
this.#assertLoaded()
|
|
891
|
+
console.log(`=== Chart Inspection: ${chartId} ===`)
|
|
848
892
|
// Find chart across all slides to get info
|
|
849
|
-
let found = false
|
|
893
|
+
let found = false
|
|
850
894
|
for (const i of this.#slideManager.getAllSlideIndices()) {
|
|
851
895
|
try {
|
|
852
|
-
const info = this.#chartManager.getChartsInSlide(
|
|
853
|
-
|
|
896
|
+
const info = this.#chartManager.getChartsInSlide(
|
|
897
|
+
i,
|
|
898
|
+
this.#slideManager,
|
|
899
|
+
this.#relationshipManager
|
|
900
|
+
)
|
|
901
|
+
const chart = info.find(
|
|
902
|
+
c => c.zipPath.toLowerCase().includes(chartId.toLowerCase()) || c.rId === chartId
|
|
903
|
+
)
|
|
854
904
|
if (chart) {
|
|
855
|
-
console.log(`Found on Slide ${i}`)
|
|
856
|
-
console.log(`ZIP Path: ${chart.zipPath}`)
|
|
857
|
-
console.log(`Relationship ID: ${chart.rId}`)
|
|
858
|
-
found = true
|
|
859
|
-
break
|
|
905
|
+
console.log(`Found on Slide ${i}`)
|
|
906
|
+
console.log(`ZIP Path: ${chart.zipPath}`)
|
|
907
|
+
console.log(`Relationship ID: ${chart.rId}`)
|
|
908
|
+
found = true
|
|
909
|
+
break
|
|
860
910
|
}
|
|
861
911
|
} catch (e) {}
|
|
862
912
|
}
|
|
863
|
-
if (!found) console.log('Chart not found.')
|
|
864
|
-
return this
|
|
913
|
+
if (!found) console.log('Chart not found.')
|
|
914
|
+
return this
|
|
865
915
|
}
|
|
866
916
|
|
|
867
917
|
/**
|
|
@@ -870,74 +920,580 @@ class PPTXTemplater {
|
|
|
870
920
|
* @param {string} chartFileName
|
|
871
921
|
*/
|
|
872
922
|
async inspectChartXML(chartFileName) {
|
|
873
|
-
const fullPath = chartFileName.includes('/') ? chartFileName : `ppt/charts/${chartFileName}
|
|
874
|
-
await this.inspectXML(fullPath)
|
|
875
|
-
return this
|
|
923
|
+
const fullPath = chartFileName.includes('/') ? chartFileName : `ppt/charts/${chartFileName}`
|
|
924
|
+
await this.inspectXML(fullPath)
|
|
925
|
+
return this
|
|
876
926
|
}
|
|
877
927
|
|
|
878
928
|
/**
|
|
879
929
|
* Logs all chart relationships.
|
|
880
930
|
*/
|
|
881
931
|
debugChartRelationships() {
|
|
882
|
-
this.#assertLoaded()
|
|
883
|
-
console.log('=== Chart Relationships ===')
|
|
884
|
-
const chartFiles = this.#zipManager.listFiles('ppt/charts/')
|
|
885
|
-
.
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
});
|
|
932
|
+
this.#assertLoaded()
|
|
933
|
+
console.log('=== Chart Relationships ===')
|
|
934
|
+
const chartFiles = this.#zipManager.listFiles('ppt/charts/').filter(f => {
|
|
935
|
+
const name = f.split('/').pop()
|
|
936
|
+
return name.startsWith('chart') && name.endsWith('.xml') && !f.includes('_rels')
|
|
937
|
+
})
|
|
889
938
|
for (const chartPath of chartFiles) {
|
|
890
|
-
console.log(`\n${chartPath}:`)
|
|
891
|
-
const rels = this.#relationshipManager.getRelationships(chartPath)
|
|
892
|
-
rels.forEach(r => console.log(` - ${r.id} [${r.type.split('/').pop()}] -> ${r.target}`))
|
|
939
|
+
console.log(`\n${chartPath}:`)
|
|
940
|
+
const rels = this.#relationshipManager.getRelationships(chartPath)
|
|
941
|
+
rels.forEach(r => console.log(` - ${r.id} [${r.type.split('/').pop()}] -> ${r.target}`))
|
|
893
942
|
}
|
|
894
|
-
return this
|
|
943
|
+
return this
|
|
895
944
|
}
|
|
896
945
|
|
|
897
946
|
/**
|
|
898
947
|
* Saves the modified PPTX to a file on disk.
|
|
899
948
|
*
|
|
900
|
-
* @param {string} filePath - Output file path
|
|
949
|
+
* @param {string} filePath - Output file path.
|
|
950
|
+
* @param {Object} [options] - Save options.
|
|
951
|
+
* @param {boolean} [options.strict=false] - Throw error on validation failure.
|
|
901
952
|
* @returns {Promise<void>}
|
|
902
|
-
*
|
|
903
|
-
* @example
|
|
904
|
-
* await ppt.saveToFile('./output/report.pptx');
|
|
905
953
|
*/
|
|
906
|
-
async saveToFile(filePath) {
|
|
907
|
-
this.#assertLoaded()
|
|
908
|
-
await this
|
|
909
|
-
|
|
954
|
+
async saveToFile(filePath, options = {}) {
|
|
955
|
+
this.#assertLoaded()
|
|
956
|
+
const result = await this.validatePresentation()
|
|
957
|
+
if (!result.valid) {
|
|
958
|
+
if (options.strict) {
|
|
959
|
+
throw new PPTXError(`Validation failed before save: ${result.errors.join(', ')}`)
|
|
960
|
+
} else {
|
|
961
|
+
logger.warn(
|
|
962
|
+
`Validation issues found before save:\n${result.errors.map(e => ` • ${e}`).join('\n')}`
|
|
963
|
+
)
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
await this.#outputWriter.saveToFile(filePath, this.#slideManager, this.#zipManager)
|
|
967
|
+
logger.info(`Saved PPTX to ${filePath}`)
|
|
910
968
|
}
|
|
911
969
|
|
|
912
970
|
/**
|
|
913
971
|
* Returns the PPTX content as a Node.js Buffer.
|
|
914
|
-
* Useful for HTTP responses, email attachments, etc.
|
|
915
|
-
*
|
|
916
|
-
* @returns {Promise<Buffer>} Buffer containing PPTX binary data.
|
|
917
972
|
*
|
|
918
|
-
* @
|
|
919
|
-
* const buffer = await ppt.toBuffer();
|
|
920
|
-
* res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.presentationml.presentation');
|
|
921
|
-
* res.send(buffer);
|
|
973
|
+
* @returns {Promise<Buffer>}
|
|
922
974
|
*/
|
|
923
975
|
async toBuffer() {
|
|
924
|
-
this.#assertLoaded()
|
|
925
|
-
return this.#outputWriter.toBuffer(this.#slideManager, this.#zipManager)
|
|
976
|
+
this.#assertLoaded()
|
|
977
|
+
return this.#outputWriter.toBuffer(this.#slideManager, this.#zipManager)
|
|
926
978
|
}
|
|
927
979
|
|
|
928
980
|
/**
|
|
929
981
|
* Returns the PPTX content as a readable Node.js Stream.
|
|
930
|
-
* Ideal for streaming large presentations to HTTP responses.
|
|
931
|
-
*
|
|
932
|
-
* @returns {Promise<NodeJS.ReadableStream>} Readable stream of PPTX data.
|
|
933
982
|
*
|
|
934
|
-
* @
|
|
935
|
-
* const stream = await ppt.toStream();
|
|
936
|
-
* stream.pipe(res);
|
|
983
|
+
* @returns {Promise<NodeJS.ReadableStream>}
|
|
937
984
|
*/
|
|
938
985
|
async toStream() {
|
|
939
|
-
this.#assertLoaded()
|
|
940
|
-
return this.#outputWriter.toStream(this.#slideManager, this.#zipManager)
|
|
986
|
+
this.#assertLoaded()
|
|
987
|
+
return this.#outputWriter.toStream(this.#slideManager, this.#zipManager)
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// === Slide Features ===
|
|
991
|
+
duplicateSlide(slideIndex, atPosition) {
|
|
992
|
+
this.#assertLoaded()
|
|
993
|
+
this.#slideManager.duplicateSlide(slideIndex, atPosition, this.#relationshipManager)
|
|
994
|
+
return this
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
deleteSlide(slideIndex) {
|
|
998
|
+
this.#assertLoaded()
|
|
999
|
+
this.#slideManager.removeSlide(slideIndex)
|
|
1000
|
+
return this
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
moveSlide(fromIndex, toIndex) {
|
|
1004
|
+
this.#assertLoaded()
|
|
1005
|
+
this.#slideManager.moveSlide(fromIndex, toIndex)
|
|
1006
|
+
return this
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
insertSlide(slideIndex, options = {}) {
|
|
1010
|
+
this.#assertLoaded()
|
|
1011
|
+
this.#slideManager.insertSlide(
|
|
1012
|
+
slideIndex,
|
|
1013
|
+
options,
|
|
1014
|
+
this.#relationshipManager,
|
|
1015
|
+
this.#mediaManager
|
|
1016
|
+
)
|
|
1017
|
+
return this
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
getSlides() {
|
|
1021
|
+
this.#assertLoaded()
|
|
1022
|
+
return this.#slideManager.getSlides()
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// === Table Features ===
|
|
1026
|
+
addTableRow(tableId, rowData) {
|
|
1027
|
+
this.#assertLoaded()
|
|
1028
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1029
|
+
for (const idx of targetIndices) {
|
|
1030
|
+
this.#tableManager.addTableRow(idx, tableId, rowData, this.#slideManager)
|
|
1031
|
+
}
|
|
1032
|
+
return this
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
removeTableRow(tableId, rowIndex) {
|
|
1036
|
+
this.#assertLoaded()
|
|
1037
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1038
|
+
for (const idx of targetIndices) {
|
|
1039
|
+
this.#tableManager.removeTableRow(idx, tableId, rowIndex, this.#slideManager)
|
|
1040
|
+
}
|
|
1041
|
+
return this
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
insertTableRow(tableId, rowIndex, rowData) {
|
|
1045
|
+
this.#assertLoaded()
|
|
1046
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1047
|
+
for (const idx of targetIndices) {
|
|
1048
|
+
this.#tableManager.insertTableRow(idx, tableId, rowIndex, rowData, this.#slideManager)
|
|
1049
|
+
}
|
|
1050
|
+
return this
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
cloneTableRow(tableId, sourceRowIndex, targetRowIndex) {
|
|
1054
|
+
this.#assertLoaded()
|
|
1055
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1056
|
+
for (const idx of targetIndices) {
|
|
1057
|
+
this.#tableManager.cloneTableRow(
|
|
1058
|
+
idx,
|
|
1059
|
+
tableId,
|
|
1060
|
+
sourceRowIndex,
|
|
1061
|
+
targetRowIndex,
|
|
1062
|
+
this.#slideManager
|
|
1063
|
+
)
|
|
1064
|
+
}
|
|
1065
|
+
return this
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
updateCell(tableId, rowIndex, colIndex, value, options = {}) {
|
|
1069
|
+
this.#assertLoaded()
|
|
1070
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1071
|
+
for (const idx of targetIndices) {
|
|
1072
|
+
this.#tableManager.updateCell(
|
|
1073
|
+
idx,
|
|
1074
|
+
tableId,
|
|
1075
|
+
rowIndex,
|
|
1076
|
+
colIndex,
|
|
1077
|
+
value,
|
|
1078
|
+
options,
|
|
1079
|
+
this.#slideManager
|
|
1080
|
+
)
|
|
1081
|
+
}
|
|
1082
|
+
return this
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
mergeCells(tableIdOrOptions, startRow, startCol, endRow, endCol) {
|
|
1086
|
+
this.#assertLoaded()
|
|
1087
|
+
let tableId = tableIdOrOptions
|
|
1088
|
+
let sRow = startRow
|
|
1089
|
+
let sCol = startCol
|
|
1090
|
+
let eRow = endRow
|
|
1091
|
+
let eCol = endCol
|
|
1092
|
+
let targetIndices = this.#getTargetSlideIndices()
|
|
1093
|
+
|
|
1094
|
+
if (tableIdOrOptions && typeof tableIdOrOptions === 'object') {
|
|
1095
|
+
const opt = tableIdOrOptions
|
|
1096
|
+
tableId = opt.tableId
|
|
1097
|
+
sRow = opt.startRow
|
|
1098
|
+
sCol = opt.startCol
|
|
1099
|
+
eRow = opt.endRow
|
|
1100
|
+
eCol = opt.endCol
|
|
1101
|
+
if (opt.slide !== undefined) {
|
|
1102
|
+
targetIndices = [opt.slide]
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
for (const idx of targetIndices) {
|
|
1107
|
+
this.#tableManager.mergeCells(idx, tableId, sRow, sCol, eRow, eCol, this.#slideManager)
|
|
1108
|
+
}
|
|
1109
|
+
return this
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
unmergeCells(tableIdOrOptions, startRow, startCol, endRow, endCol) {
|
|
1113
|
+
this.#assertLoaded()
|
|
1114
|
+
let tableId = tableIdOrOptions
|
|
1115
|
+
let sRow = startRow
|
|
1116
|
+
let sCol = startCol
|
|
1117
|
+
let eRow = endRow
|
|
1118
|
+
let eCol = endCol
|
|
1119
|
+
let targetIndices = this.#getTargetSlideIndices()
|
|
1120
|
+
let isCellCoord = false
|
|
1121
|
+
let cellRow, cellCol
|
|
1122
|
+
|
|
1123
|
+
if (tableIdOrOptions && typeof tableIdOrOptions === 'object') {
|
|
1124
|
+
const opt = tableIdOrOptions
|
|
1125
|
+
tableId = opt.tableId
|
|
1126
|
+
sRow = opt.startRow
|
|
1127
|
+
sCol = opt.startCol
|
|
1128
|
+
eRow = opt.endRow
|
|
1129
|
+
eCol = opt.endCol
|
|
1130
|
+
if (opt.slide !== undefined) {
|
|
1131
|
+
targetIndices = [opt.slide]
|
|
1132
|
+
}
|
|
1133
|
+
if (opt.row !== undefined && opt.col !== undefined) {
|
|
1134
|
+
isCellCoord = true
|
|
1135
|
+
cellRow = opt.row
|
|
1136
|
+
cellCol = opt.col
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
for (const idx of targetIndices) {
|
|
1141
|
+
if (isCellCoord) {
|
|
1142
|
+
this.#tableManager.unmergeCells(idx, tableId, cellRow, cellCol, this.#slideManager)
|
|
1143
|
+
} else {
|
|
1144
|
+
this.#tableManager.unmergeCells(idx, tableId, sRow, sCol, eRow, eCol, this.#slideManager)
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return this
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
getMergedCells(tableId) {
|
|
1151
|
+
this.#assertLoaded()
|
|
1152
|
+
const slideIndex = this.#getTargetSlideIndices()[0] || 1
|
|
1153
|
+
return this.#tableManager.getMergedCells(slideIndex, tableId || 'first', this.#slideManager)
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
validateMergeRegion(tableId, startRow, startCol, endRow, endCol) {
|
|
1157
|
+
this.#assertLoaded()
|
|
1158
|
+
const slideIndex = this.#getTargetSlideIndices()[0] || 1
|
|
1159
|
+
return this.#tableManager.validateMergeRegion(
|
|
1160
|
+
slideIndex,
|
|
1161
|
+
tableId || 'first',
|
|
1162
|
+
startRow,
|
|
1163
|
+
startCol,
|
|
1164
|
+
endRow,
|
|
1165
|
+
endCol,
|
|
1166
|
+
this.#slideManager
|
|
1167
|
+
)
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
isMergedCell(tableId, row, col) {
|
|
1171
|
+
this.#assertLoaded()
|
|
1172
|
+
const slideIndex = this.#getTargetSlideIndices()[0] || 1
|
|
1173
|
+
return this.#tableManager.isMergedCell(
|
|
1174
|
+
slideIndex,
|
|
1175
|
+
tableId || 'first',
|
|
1176
|
+
row,
|
|
1177
|
+
col,
|
|
1178
|
+
this.#slideManager
|
|
1179
|
+
)
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
getMergeParent(tableId, row, col) {
|
|
1183
|
+
this.#assertLoaded()
|
|
1184
|
+
const slideIndex = this.#getTargetSlideIndices()[0] || 1
|
|
1185
|
+
return this.#tableManager.getMergeParent(
|
|
1186
|
+
slideIndex,
|
|
1187
|
+
tableId || 'first',
|
|
1188
|
+
row,
|
|
1189
|
+
col,
|
|
1190
|
+
this.#slideManager
|
|
1191
|
+
)
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
getMergeRegion(tableId, row, col) {
|
|
1195
|
+
this.#assertLoaded()
|
|
1196
|
+
const slideIndex = this.#getTargetSlideIndices()[0] || 1
|
|
1197
|
+
return this.#tableManager.getMergeRegion(
|
|
1198
|
+
slideIndex,
|
|
1199
|
+
tableId || 'first',
|
|
1200
|
+
row,
|
|
1201
|
+
col,
|
|
1202
|
+
this.#slideManager
|
|
1203
|
+
)
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
splitMergedRegion(tableId, row, col) {
|
|
1207
|
+
this.#assertLoaded()
|
|
1208
|
+
const slideIndex = this.#getTargetSlideIndices()[0] || 1
|
|
1209
|
+
this.#tableManager.splitMergedRegion(
|
|
1210
|
+
slideIndex,
|
|
1211
|
+
tableId || 'first',
|
|
1212
|
+
row,
|
|
1213
|
+
col,
|
|
1214
|
+
this.#slideManager
|
|
1215
|
+
)
|
|
1216
|
+
return this
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
cloneMergedRegion(tableId, row, col, targetRow, targetCol) {
|
|
1220
|
+
this.#assertLoaded()
|
|
1221
|
+
const slideIndex = this.#getTargetSlideIndices()[0] || 1
|
|
1222
|
+
this.#tableManager.cloneMergedRegion(
|
|
1223
|
+
slideIndex,
|
|
1224
|
+
tableId || 'first',
|
|
1225
|
+
row,
|
|
1226
|
+
col,
|
|
1227
|
+
targetRow,
|
|
1228
|
+
targetCol,
|
|
1229
|
+
this.#slideManager
|
|
1230
|
+
)
|
|
1231
|
+
return this
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
autoFitTable(tableId) {
|
|
1235
|
+
this.#assertLoaded()
|
|
1236
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1237
|
+
for (const idx of targetIndices) {
|
|
1238
|
+
this.#tableManager.autoFitTable(idx, tableId, this.#slideManager)
|
|
1239
|
+
}
|
|
1240
|
+
return this
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
resizeTable(tableId, width, height) {
|
|
1244
|
+
this.#assertLoaded()
|
|
1245
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1246
|
+
for (const idx of targetIndices) {
|
|
1247
|
+
this.#tableManager.resizeTable(idx, tableId, width, height, this.#slideManager)
|
|
1248
|
+
}
|
|
1249
|
+
return this
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
getTables() {
|
|
1253
|
+
this.#assertLoaded()
|
|
1254
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1255
|
+
const tables = []
|
|
1256
|
+
for (const idx of targetIndices) {
|
|
1257
|
+
tables.push(...this.#tableManager.inspectTables(idx, this.#slideManager))
|
|
1258
|
+
}
|
|
1259
|
+
return tables
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// === Chart Features ===
|
|
1263
|
+
updateChartData(chartId, data) {
|
|
1264
|
+
return this.updateChart(chartId, data)
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
replaceChartSeries(chartId, seriesIndex, newSeriesData) {
|
|
1268
|
+
this.#assertLoaded()
|
|
1269
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1270
|
+
for (const idx of targetIndices) {
|
|
1271
|
+
this.#chartManager.replaceChartSeries(
|
|
1272
|
+
idx,
|
|
1273
|
+
chartId,
|
|
1274
|
+
seriesIndex,
|
|
1275
|
+
newSeriesData,
|
|
1276
|
+
this.#slideManager,
|
|
1277
|
+
this.#relationshipManager
|
|
1278
|
+
)
|
|
1279
|
+
}
|
|
1280
|
+
return this
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
updateChartTitle(chartId, title) {
|
|
1284
|
+
this.#assertLoaded()
|
|
1285
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1286
|
+
for (const idx of targetIndices) {
|
|
1287
|
+
this.#chartManager.updateChartTitle(
|
|
1288
|
+
idx,
|
|
1289
|
+
chartId,
|
|
1290
|
+
title,
|
|
1291
|
+
this.#slideManager,
|
|
1292
|
+
this.#relationshipManager
|
|
1293
|
+
)
|
|
1294
|
+
}
|
|
1295
|
+
return this
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
updateChartCategories(chartId, categories) {
|
|
1299
|
+
this.#assertLoaded()
|
|
1300
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1301
|
+
for (const idx of targetIndices) {
|
|
1302
|
+
this.#chartManager.updateChartCategories(
|
|
1303
|
+
idx,
|
|
1304
|
+
chartId,
|
|
1305
|
+
categories,
|
|
1306
|
+
this.#slideManager,
|
|
1307
|
+
this.#relationshipManager
|
|
1308
|
+
)
|
|
1309
|
+
}
|
|
1310
|
+
return this
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
getCharts() {
|
|
1314
|
+
this.#assertLoaded()
|
|
1315
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1316
|
+
const charts = []
|
|
1317
|
+
for (const idx of targetIndices) {
|
|
1318
|
+
charts.push(
|
|
1319
|
+
...this.#chartManager.getChartsInSlide(idx, this.#slideManager, this.#relationshipManager)
|
|
1320
|
+
)
|
|
1321
|
+
}
|
|
1322
|
+
return charts
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// === Text Features ===
|
|
1326
|
+
replaceTextByTag(tag, value, options = {}) {
|
|
1327
|
+
this.#assertLoaded()
|
|
1328
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1329
|
+
for (const idx of targetIndices) {
|
|
1330
|
+
this.#textManager.replaceTextByTag(
|
|
1331
|
+
idx,
|
|
1332
|
+
tag,
|
|
1333
|
+
value,
|
|
1334
|
+
options,
|
|
1335
|
+
this.#slideManager,
|
|
1336
|
+
this.#templateEngine
|
|
1337
|
+
)
|
|
1338
|
+
}
|
|
1339
|
+
return this
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
replaceMultiple(replacements, options = {}) {
|
|
1343
|
+
this.#assertLoaded()
|
|
1344
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1345
|
+
for (const idx of targetIndices) {
|
|
1346
|
+
this.#textManager.replaceMultiple(
|
|
1347
|
+
idx,
|
|
1348
|
+
replacements,
|
|
1349
|
+
options,
|
|
1350
|
+
this.#slideManager,
|
|
1351
|
+
this.#templateEngine
|
|
1352
|
+
)
|
|
1353
|
+
}
|
|
1354
|
+
return this
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
findText(text) {
|
|
1358
|
+
this.#assertLoaded()
|
|
1359
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1360
|
+
const matches = []
|
|
1361
|
+
for (const idx of targetIndices) {
|
|
1362
|
+
matches.push(...this.#textManager.findText(idx, text, this.#slideManager))
|
|
1363
|
+
}
|
|
1364
|
+
return matches
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
getTextElements() {
|
|
1368
|
+
this.#assertLoaded()
|
|
1369
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1370
|
+
const elements = []
|
|
1371
|
+
for (const idx of targetIndices) {
|
|
1372
|
+
elements.push(...this.#textManager.getTextElements(idx, this.#slideManager))
|
|
1373
|
+
}
|
|
1374
|
+
return elements
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// === Shape Features ===
|
|
1378
|
+
updateShapeText(shapeId, text) {
|
|
1379
|
+
this.#assertLoaded()
|
|
1380
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1381
|
+
for (const idx of targetIndices) {
|
|
1382
|
+
this.#shapeManager.updateShapeText(idx, shapeId, text, this.#slideManager)
|
|
1383
|
+
}
|
|
1384
|
+
return this
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
cloneShape(shapeId, newShapeId, options = {}) {
|
|
1388
|
+
this.#assertLoaded()
|
|
1389
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1390
|
+
for (const idx of targetIndices) {
|
|
1391
|
+
this.#shapeManager.cloneShape(idx, shapeId, newShapeId, options, this.#slideManager)
|
|
1392
|
+
}
|
|
1393
|
+
return this
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
deleteShape(shapeId) {
|
|
1397
|
+
this.#assertLoaded()
|
|
1398
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1399
|
+
for (const idx of targetIndices) {
|
|
1400
|
+
this.#shapeManager.deleteShape(idx, shapeId, this.#slideManager)
|
|
1401
|
+
}
|
|
1402
|
+
return this
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
getShapes() {
|
|
1406
|
+
this.#assertLoaded()
|
|
1407
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1408
|
+
const shapes = []
|
|
1409
|
+
for (const idx of targetIndices) {
|
|
1410
|
+
shapes.push(...this.#shapeManager.getShapes(idx, this.#slideManager))
|
|
1411
|
+
}
|
|
1412
|
+
return shapes
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// === Image Features ===
|
|
1416
|
+
async replaceImage(imageIdOrName, sourcePathOrBuffer) {
|
|
1417
|
+
this.#assertLoaded()
|
|
1418
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1419
|
+
for (const idx of targetIndices) {
|
|
1420
|
+
await this.#imageManager.replaceImage(
|
|
1421
|
+
idx,
|
|
1422
|
+
imageIdOrName,
|
|
1423
|
+
sourcePathOrBuffer,
|
|
1424
|
+
this.#slideManager,
|
|
1425
|
+
this.#mediaManager,
|
|
1426
|
+
this.#relationshipManager
|
|
1427
|
+
)
|
|
1428
|
+
}
|
|
1429
|
+
return this
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
async addImage(sourcePathOrBuffer, options = {}) {
|
|
1433
|
+
this.#assertLoaded()
|
|
1434
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1435
|
+
for (const idx of targetIndices) {
|
|
1436
|
+
await this.#imageManager.addImage(
|
|
1437
|
+
idx,
|
|
1438
|
+
sourcePathOrBuffer,
|
|
1439
|
+
options,
|
|
1440
|
+
this.#slideManager,
|
|
1441
|
+
this.#mediaManager,
|
|
1442
|
+
this.#relationshipManager
|
|
1443
|
+
)
|
|
1444
|
+
}
|
|
1445
|
+
return this
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
removeImage(imageIdOrName) {
|
|
1449
|
+
this.#assertLoaded()
|
|
1450
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1451
|
+
for (const idx of targetIndices) {
|
|
1452
|
+
this.#imageManager.removeImage(
|
|
1453
|
+
idx,
|
|
1454
|
+
imageIdOrName,
|
|
1455
|
+
this.#slideManager,
|
|
1456
|
+
this.#relationshipManager
|
|
1457
|
+
)
|
|
1458
|
+
}
|
|
1459
|
+
return this
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
getImages() {
|
|
1463
|
+
this.#assertLoaded()
|
|
1464
|
+
const targetIndices = this.#getTargetSlideIndices()
|
|
1465
|
+
const images = []
|
|
1466
|
+
for (const idx of targetIndices) {
|
|
1467
|
+
images.push(
|
|
1468
|
+
...this.#imageManager.getImages(idx, this.#slideManager, this.#relationshipManager)
|
|
1469
|
+
)
|
|
1470
|
+
}
|
|
1471
|
+
return images
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// === Validation Features ===
|
|
1475
|
+
async validatePresentation() {
|
|
1476
|
+
this.#assertLoaded()
|
|
1477
|
+
return await ValidationEngine.validatePresentation(this)
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
async validateSlide(slideIndex) {
|
|
1481
|
+
this.#assertLoaded()
|
|
1482
|
+
return await ValidationEngine.validateSlide(this, slideIndex)
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
async validateTable(tableId) {
|
|
1486
|
+
this.#assertLoaded()
|
|
1487
|
+
return await ValidationEngine.validateTable(
|
|
1488
|
+
this,
|
|
1489
|
+
this.#getTargetSlideIndices()[0] || 1,
|
|
1490
|
+
tableId
|
|
1491
|
+
)
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
validateRelationships(partPath) {
|
|
1495
|
+
this.#assertLoaded()
|
|
1496
|
+
return ValidationEngine.validateRelationships(this, partPath)
|
|
941
1497
|
}
|
|
942
1498
|
|
|
943
1499
|
/**
|
|
@@ -945,19 +1501,46 @@ class PPTXTemplater {
|
|
|
945
1501
|
* @type {number}
|
|
946
1502
|
*/
|
|
947
1503
|
get slideCount() {
|
|
948
|
-
return this.#slideManager.slideCount
|
|
1504
|
+
return this.#slideManager.slideCount
|
|
949
1505
|
}
|
|
950
1506
|
|
|
951
1507
|
// --- Public Getters for Internal Managers ---
|
|
952
|
-
get zipManager() {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
get
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
get
|
|
959
|
-
|
|
960
|
-
|
|
1508
|
+
get zipManager() {
|
|
1509
|
+
return this.#zipManager
|
|
1510
|
+
}
|
|
1511
|
+
get xmlParser() {
|
|
1512
|
+
return this.#xmlParser
|
|
1513
|
+
}
|
|
1514
|
+
get contentTypesManager() {
|
|
1515
|
+
return this.#contentTypesManager
|
|
1516
|
+
}
|
|
1517
|
+
get relationshipManager() {
|
|
1518
|
+
return this.#relationshipManager
|
|
1519
|
+
}
|
|
1520
|
+
get slideManager() {
|
|
1521
|
+
return this.#slideManager
|
|
1522
|
+
}
|
|
1523
|
+
get chartManager() {
|
|
1524
|
+
return this.#chartManager
|
|
1525
|
+
}
|
|
1526
|
+
get tableManager() {
|
|
1527
|
+
return this.#tableManager
|
|
1528
|
+
}
|
|
1529
|
+
get shapeManager() {
|
|
1530
|
+
return this.#shapeManager
|
|
1531
|
+
}
|
|
1532
|
+
get imageManager() {
|
|
1533
|
+
return this.#imageManager
|
|
1534
|
+
}
|
|
1535
|
+
get textManager() {
|
|
1536
|
+
return this.#textManager
|
|
1537
|
+
}
|
|
1538
|
+
get hyperlinkManager() {
|
|
1539
|
+
return this.#hyperlinkManager
|
|
1540
|
+
}
|
|
1541
|
+
get mediaManager() {
|
|
1542
|
+
return this.#mediaManager
|
|
1543
|
+
}
|
|
961
1544
|
}
|
|
962
1545
|
|
|
963
|
-
module.exports = { PPTXTemplater }
|
|
1546
|
+
module.exports = { PPTXTemplater }
|