node-pptx-templater 1.0.17 → 1.0.18

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.
@@ -71,6 +71,12 @@ class SlideManager {
71
71
  */
72
72
  #slideXmlCache = new Map()
73
73
 
74
+ /**
75
+ * Slide states: maps 1-based index → state object.
76
+ * @private @type {Map<number, Object>}
77
+ */
78
+ #slideStates = new Map()
79
+
74
80
  /**
75
81
  * Custom tags: maps tag → array of 1-based indices.
76
82
  * @private @type {Map<string, number[]>}
@@ -177,6 +183,16 @@ class SlideManager {
177
183
  }
178
184
 
179
185
  this.#slides.set(slideIndex, slideInfo)
186
+ this.#slideStates.set(slideIndex, {
187
+ xmlStr: null,
188
+ xmlObj: null,
189
+ dirty: false,
190
+ indexBuilt: false,
191
+ shapeMap: new Map(),
192
+ picMap: new Map(),
193
+ tableMap: new Map(),
194
+ chartMap: new Map(),
195
+ })
180
196
  slideIndex++
181
197
  }
182
198
 
@@ -237,9 +253,27 @@ class SlideManager {
237
253
  getSlideXml(slideIndex) {
238
254
  this.#assertSlideExists(slideIndex)
239
255
  const info = this.#slides.get(slideIndex)
256
+ const state = this.#slideStates.get(slideIndex)
257
+
258
+ if (state.dirty && state.xmlObj) {
259
+ const decl = this.#xmlParser.extractDeclaration(
260
+ state.xmlStr || '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
261
+ )
262
+ state.xmlStr = this.#xmlParser.build(state.xmlObj, decl)
263
+ this.#zipManager.writeFile(info.zipPath, state.xmlStr)
264
+ this.#slideXmlCache.set(info.zipPath, state.xmlStr)
265
+ state.dirty = false
266
+ }
267
+
268
+ if (state.xmlStr) {
269
+ return state.xmlStr
270
+ }
240
271
 
241
- if (this.#slideXmlCache.has(info.zipPath)) {
242
- return this.#slideXmlCache.get(info.zipPath)
272
+ const cached = this.#zipManager.readCachedFile(info.zipPath)
273
+ if (cached) {
274
+ state.xmlStr = cached
275
+ this.#slideXmlCache.set(info.zipPath, cached)
276
+ return cached
243
277
  }
244
278
 
245
279
  // This is sync because we pre-load; async callers should use getSlideXmlAsync
@@ -255,14 +289,16 @@ class SlideManager {
255
289
  async getSlideXmlAsync(slideIndex) {
256
290
  this.#assertSlideExists(slideIndex)
257
291
  const info = this.#slides.get(slideIndex)
292
+ const state = this.#slideStates.get(slideIndex)
258
293
 
259
- if (!this.#slideXmlCache.has(info.zipPath)) {
294
+ if (!state.xmlStr) {
260
295
  const xml = await this.#zipManager.readFile(info.zipPath)
261
296
  if (!xml) throw new SlideNotFoundError(`Slide ${slideIndex} XML not found at ${info.zipPath}`)
297
+ state.xmlStr = xml
262
298
  this.#slideXmlCache.set(info.zipPath, xml)
263
299
  }
264
300
 
265
- return this.#slideXmlCache.get(info.zipPath)
301
+ return state.xmlStr
266
302
  }
267
303
 
268
304
  /**
@@ -274,6 +310,13 @@ class SlideManager {
274
310
  setSlideXml(slideIndex, xml) {
275
311
  this.#assertSlideExists(slideIndex)
276
312
  const info = this.#slides.get(slideIndex)
313
+ const state = this.#slideStates.get(slideIndex)
314
+
315
+ state.xmlStr = xml
316
+ state.xmlObj = null
317
+ state.dirty = false
318
+ state.indexBuilt = false
319
+
277
320
  this.#slideXmlCache.set(info.zipPath, xml)
278
321
  this.#zipManager.writeFile(info.zipPath, xml)
279
322
  }
@@ -360,6 +403,16 @@ class SlideManager {
360
403
  }
361
404
 
362
405
  this.#slides.set(newIndex, slideInfo)
406
+ this.#slideStates.set(newIndex, {
407
+ xmlStr: slideXml,
408
+ xmlObj: null,
409
+ dirty: false,
410
+ indexBuilt: false,
411
+ shapeMap: new Map(),
412
+ picMap: new Map(),
413
+ tableMap: new Map(),
414
+ chartMap: new Map(),
415
+ })
363
416
 
364
417
  // Update presentation.xml sldIdLst
365
418
  this.#addSlideToPresentation(rId, newSlideId)
@@ -424,6 +477,16 @@ class SlideManager {
424
477
  }
425
478
 
426
479
  this.#slides.set(newIndex, slideInfo)
480
+ this.#slideStates.set(newIndex, {
481
+ xmlStr: sourceXml,
482
+ xmlObj: null,
483
+ dirty: false,
484
+ indexBuilt: false,
485
+ shapeMap: new Map(),
486
+ picMap: new Map(),
487
+ tableMap: new Map(),
488
+ chartMap: new Map(),
489
+ })
427
490
  this.#addSlideToPresentation(rId, newSlideId)
428
491
  this.#registerSlideContentType(slideFileName)
429
492
 
@@ -448,6 +511,7 @@ class SlideManager {
448
511
 
449
512
  // Remove from cache
450
513
  this.#slideXmlCache.delete(info.zipPath)
514
+ this.#slideStates.delete(slideIndex)
451
515
 
452
516
  // Remove relationship from presentation.xml
453
517
  this.#relationshipManager.removeRelationship('ppt/presentation.xml', info.relationshipId)
@@ -479,13 +543,18 @@ class SlideManager {
479
543
  }
480
544
 
481
545
  const slidesCopy = new Map(this.#slides)
546
+ const statesCopy = new Map(this.#slideStates)
482
547
  this.#slides.clear()
548
+ this.#slideStates.clear()
483
549
 
484
550
  order.forEach((oldIndex, newPos) => {
485
551
  const info = slidesCopy.get(oldIndex)
486
552
  if (!info) throw new SlideNotFoundError(`Slide ${oldIndex} not found`)
487
553
  info.index = newPos + 1
488
554
  this.#slides.set(newPos + 1, info)
555
+
556
+ const state = statesCopy.get(oldIndex)
557
+ this.#slideStates.set(newPos + 1, state)
489
558
  })
490
559
 
491
560
  // Rebuild presentation sldIdLst
@@ -741,6 +810,16 @@ class SlideManager {
741
810
  }
742
811
 
743
812
  this.#slides.set(newIndex, slideInfo)
813
+ this.#slideStates.set(newIndex, {
814
+ xmlStr: slideXml,
815
+ xmlObj: null,
816
+ dirty: false,
817
+ indexBuilt: false,
818
+ shapeMap: new Map(),
819
+ picMap: new Map(),
820
+ tableMap: new Map(),
821
+ chartMap: new Map(),
822
+ })
744
823
 
745
824
  // Add entry in presentation.xml sldIdLst
746
825
  this.#addSlideToPresentation(rId, newSlideId)
@@ -917,10 +996,18 @@ class SlideManager {
917
996
  #reindexSlides() {
918
997
  const sorted = Array.from(this.#slides.entries()).sort(([a], [b]) => a - b)
919
998
  this.#slides.clear()
999
+
1000
+ const sortedStates = Array.from(this.#slideStates.entries()).sort(([a], [b]) => a - b)
1001
+ this.#slideStates.clear()
1002
+
920
1003
  sorted.forEach(([, info], i) => {
921
1004
  info.index = i + 1
922
1005
  this.#slides.set(i + 1, info)
923
1006
  })
1007
+
1008
+ sortedStates.forEach(([, state], i) => {
1009
+ this.#slideStates.set(i + 1, state)
1010
+ })
924
1011
  }
925
1012
 
926
1013
  /**
@@ -1044,6 +1131,162 @@ class SlideManager {
1044
1131
  )
1045
1132
  }
1046
1133
  }
1134
+
1135
+ getSlideObj(slideIndex) {
1136
+ this.#assertSlideExists(slideIndex)
1137
+ const state = this.#slideStates.get(slideIndex)
1138
+
1139
+ if (!state.xmlObj) {
1140
+ const xml = this.getSlideXml(slideIndex)
1141
+ const info = this.#slides.get(slideIndex)
1142
+ state.xmlObj = this.#xmlParser.parse(xml, info.zipPath.split('/').pop())
1143
+ state.indexBuilt = false
1144
+ }
1145
+
1146
+ if (!state.indexBuilt) {
1147
+ state.shapeMap.clear()
1148
+ state.picMap.clear()
1149
+ state.tableMap.clear()
1150
+ state.chartMap.clear()
1151
+
1152
+ const spTree =
1153
+ state.xmlObj?.['p:sld']?.['p:cSld']?.['p:spTree'] ||
1154
+ state.xmlObj?.['p:sldLayout']?.['p:cSld']?.['p:spTree'] ||
1155
+ state.xmlObj?.['p:sldMaster']?.['p:cSld']?.['p:spTree']
1156
+
1157
+ this.#buildSlideIndexRecursive(
1158
+ spTree,
1159
+ state.shapeMap,
1160
+ state.picMap,
1161
+ state.tableMap,
1162
+ state.chartMap
1163
+ )
1164
+ state.indexBuilt = true
1165
+ }
1166
+
1167
+ return state.xmlObj
1168
+ }
1169
+
1170
+ #buildSlideIndexRecursive(container, shapeMap, picMap, tableMap, chartMap) {
1171
+ if (!container) return
1172
+
1173
+ // Shapes
1174
+ let shapes = container['p:sp'] || []
1175
+ if (!Array.isArray(shapes)) shapes = [shapes]
1176
+ for (const shape of shapes) {
1177
+ const cNvPr = shape?.['p:nvSpPr']?.['p:cNvPr']
1178
+ if (cNvPr) {
1179
+ const name = cNvPr['@_name']
1180
+ const id = String(cNvPr['@_id'])
1181
+ const entry = { shape, parent: container, type: 'sp' }
1182
+ if (name) shapeMap.set(name, entry)
1183
+ if (id) shapeMap.set(id, entry)
1184
+ }
1185
+ }
1186
+
1187
+ // Pictures
1188
+ let pics = container['p:pic'] || []
1189
+ if (!Array.isArray(pics)) pics = [pics]
1190
+ for (const pic of pics) {
1191
+ const cNvPr = pic?.['p:nvPicPr']?.['p:cNvPr']
1192
+ if (cNvPr) {
1193
+ const name = cNvPr['@_name']
1194
+ const id = String(cNvPr['@_id'])
1195
+ const embedId = pic?.['p:blipFill']?.['a:blip']?.['@_r:embed']
1196
+ const entry = { pic, parent: container, type: 'pic' }
1197
+ if (name) picMap.set(name, entry)
1198
+ if (id) picMap.set(id, entry)
1199
+ if (embedId) picMap.set(embedId, entry)
1200
+ }
1201
+ }
1202
+
1203
+ // Graphic frames
1204
+ let frames = container['p:graphicFrame'] || []
1205
+ if (!Array.isArray(frames)) frames = [frames]
1206
+ for (const frame of frames) {
1207
+ const cNvPr = frame?.['p:nvGraphicFramePr']?.['p:cNvPr']
1208
+ const name = cNvPr ? cNvPr['@_name'] : null
1209
+ const id = cNvPr ? String(cNvPr['@_id']) : null
1210
+
1211
+ const tbl = frame?.['a:graphic']?.['a:graphicData']?.['a:tbl']
1212
+ if (tbl) {
1213
+ const entry = { table: tbl, frame, parent: container, type: 'table' }
1214
+ if (name) tableMap.set(name, entry)
1215
+ if (id) tableMap.set(id, entry)
1216
+ }
1217
+
1218
+ const chart = frame?.['a:graphic']?.['a:graphicData']?.['c:chart']
1219
+ if (chart) {
1220
+ const embedId = chart['@_r:id']
1221
+ const entry = { chart, frame, parent: container, type: 'chart' }
1222
+ if (name) chartMap.set(name, entry)
1223
+ if (id) chartMap.set(id, entry)
1224
+ if (embedId) chartMap.set(embedId, entry)
1225
+ }
1226
+ }
1227
+
1228
+ // Groups
1229
+ let groups = container['p:grpSp'] || []
1230
+ if (!Array.isArray(groups)) groups = [groups]
1231
+ for (const group of groups) {
1232
+ const cNvPr = group?.['p:nvGrpSpPr']?.['p:cNvPr']
1233
+ if (cNvPr) {
1234
+ const name = cNvPr['@_name']
1235
+ const id = String(cNvPr['@_id'])
1236
+ const entry = { shape: group, parent: container, type: 'grpSp' }
1237
+ if (name) shapeMap.set(name, entry)
1238
+ if (id) shapeMap.set(id, entry)
1239
+ }
1240
+ this.#buildSlideIndexRecursive(group, shapeMap, picMap, tableMap, chartMap)
1241
+ }
1242
+ }
1243
+
1244
+ flush() {
1245
+ for (const [index, state] of this.#slideStates) {
1246
+ if (state.dirty && state.xmlObj) {
1247
+ this.getSlideXml(index)
1248
+ }
1249
+ }
1250
+ }
1251
+
1252
+ markSlideObjDirty(slideIndex) {
1253
+ this.#assertSlideExists(slideIndex)
1254
+ const state = this.#slideStates.get(slideIndex)
1255
+ state.dirty = true
1256
+ state.indexBuilt = false
1257
+ }
1258
+
1259
+ getSlideShape(slideIndex, shapeId) {
1260
+ this.getSlideObj(slideIndex)
1261
+ return this.#slideStates.get(slideIndex).shapeMap.get(String(shapeId)) || null
1262
+ }
1263
+
1264
+ getSlidePic(slideIndex, picId) {
1265
+ this.getSlideObj(slideIndex)
1266
+ const state = this.#slideStates.get(slideIndex)
1267
+ if (picId === 'first') {
1268
+ return Array.from(state.picMap.values())[0] || null
1269
+ }
1270
+ return state.picMap.get(String(picId)) || null
1271
+ }
1272
+
1273
+ getSlideTable(slideIndex, tableId) {
1274
+ this.getSlideObj(slideIndex)
1275
+ const state = this.#slideStates.get(slideIndex)
1276
+ if (tableId === 'first') {
1277
+ return Array.from(state.tableMap.values())[0] || null
1278
+ }
1279
+ return state.tableMap.get(String(tableId)) || null
1280
+ }
1281
+
1282
+ getSlideChart(slideIndex, chartId) {
1283
+ this.getSlideObj(slideIndex)
1284
+ const state = this.#slideStates.get(slideIndex)
1285
+ if (chartId === 'first') {
1286
+ return Array.from(state.chartMap.values())[0] || null
1287
+ }
1288
+ return state.chartMap.get(String(chartId)) || null
1289
+ }
1047
1290
  }
1048
1291
 
1049
1292
  module.exports = { SlideManager }
@@ -67,12 +67,7 @@ class TableManager {
67
67
  * @throws {TableNotFoundError} If the table is not found.
68
68
  */
69
69
  updateTable(slideIndex, tableId, data, slideManager) {
70
- const slideXml = slideManager.getSlideXml(slideIndex)
71
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
72
- const tblObj = this.#findTableObj(slideObj, tableId)
73
- if (!tblObj) {
74
- throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
75
- }
70
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
76
71
 
77
72
  const trs = tblObj['a:tr'] || []
78
73
  if (trs.length === 0) {
@@ -174,8 +169,7 @@ class TableManager {
174
169
 
175
170
  tblObj['a:tr'] = newRows
176
171
 
177
- const decl = this.#xmlParser.extractDeclaration(slideXml)
178
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
172
+ slideManager.markSlideObjDirty(slideIndex)
179
173
 
180
174
  const finalMerges = [...templateMerges, ...generatedMerges]
181
175
  for (const merge of finalMerges) {
@@ -204,12 +198,7 @@ class TableManager {
204
198
  * @param {SlideManager} slideManager
205
199
  */
206
200
  addTableRow(slideIndex, tableId, rowData, slideManager) {
207
- const slideXml = slideManager.getSlideXml(slideIndex)
208
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
209
- const tblObj = this.#findTableObj(slideObj, tableId)
210
- if (!tblObj) {
211
- throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
212
- }
201
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
213
202
 
214
203
  const trs = tblObj['a:tr'] || []
215
204
  if (trs.length === 0) {
@@ -230,8 +219,7 @@ class TableManager {
230
219
 
231
220
  trs.push(newRow)
232
221
 
233
- const decl = this.#xmlParser.extractDeclaration(slideXml)
234
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
222
+ slideManager.markSlideObjDirty(slideIndex)
235
223
  }
236
224
 
237
225
  /**
@@ -243,12 +231,7 @@ class TableManager {
243
231
  * @param {SlideManager} slideManager
244
232
  */
245
233
  removeTableRow(slideIndex, tableId, rowIndex, slideManager) {
246
- const slideXml = slideManager.getSlideXml(slideIndex)
247
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
248
- const tblObj = this.#findTableObj(slideObj, tableId)
249
- if (!tblObj) {
250
- throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
251
- }
234
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
252
235
 
253
236
  const trs = tblObj['a:tr'] || []
254
237
  if (rowIndex < 0 || rowIndex >= trs.length) {
@@ -257,8 +240,7 @@ class TableManager {
257
240
 
258
241
  trs.splice(rowIndex, 1)
259
242
 
260
- const decl = this.#xmlParser.extractDeclaration(slideXml)
261
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
243
+ slideManager.markSlideObjDirty(slideIndex)
262
244
  }
263
245
 
264
246
  /**
@@ -271,12 +253,7 @@ class TableManager {
271
253
  * @param {SlideManager} slideManager
272
254
  */
273
255
  insertTableRow(slideIndex, tableId, rowIndex, rowData, slideManager) {
274
- const slideXml = slideManager.getSlideXml(slideIndex)
275
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
276
- const tblObj = this.#findTableObj(slideObj, tableId)
277
- if (!tblObj) {
278
- throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
279
- }
256
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
280
257
 
281
258
  const trs = tblObj['a:tr'] || []
282
259
  if (rowIndex < 0 || rowIndex > trs.length) {
@@ -302,8 +279,7 @@ class TableManager {
302
279
 
303
280
  trs.splice(rowIndex, 0, newRow)
304
281
 
305
- const decl = this.#xmlParser.extractDeclaration(slideXml)
306
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
282
+ slideManager.markSlideObjDirty(slideIndex)
307
283
  }
308
284
 
309
285
  /**
@@ -316,12 +292,7 @@ class TableManager {
316
292
  * @param {SlideManager} slideManager
317
293
  */
318
294
  cloneTableRow(slideIndex, tableId, sourceRowIndex, targetRowIndex, slideManager) {
319
- const slideXml = slideManager.getSlideXml(slideIndex)
320
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
321
- const tblObj = this.#findTableObj(slideObj, tableId)
322
- if (!tblObj) {
323
- throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
324
- }
295
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
325
296
 
326
297
  const trs = tblObj['a:tr'] || []
327
298
  if (sourceRowIndex < 0 || sourceRowIndex >= trs.length) {
@@ -337,8 +308,7 @@ class TableManager {
337
308
 
338
309
  trs.splice(targetRowIndex, 0, newRow)
339
310
 
340
- const decl = this.#xmlParser.extractDeclaration(slideXml)
341
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
311
+ slideManager.markSlideObjDirty(slideIndex)
342
312
  }
343
313
 
344
314
  /**
@@ -353,12 +323,7 @@ class TableManager {
353
323
  * @param {SlideManager} slideManager
354
324
  */
355
325
  updateCell(slideIndex, tableId, rowIndex, colIndex, value, options = {}, slideManager) {
356
- const slideXml = slideManager.getSlideXml(slideIndex)
357
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
358
- const tblObj = this.#findTableObj(slideObj, tableId)
359
- if (!tblObj) {
360
- throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
361
- }
326
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
362
327
 
363
328
  const row = tblObj['a:tr']?.[rowIndex]
364
329
  if (!row) {
@@ -407,8 +372,7 @@ class TableManager {
407
372
  }
408
373
  }
409
374
 
410
- const decl = this.#xmlParser.extractDeclaration(slideXml)
411
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
375
+ slideManager.markSlideObjDirty(slideIndex)
412
376
  }
413
377
 
414
378
  /**
@@ -425,9 +389,8 @@ class TableManager {
425
389
  */
426
390
  validateMergeRegion(slideIndex, tableId, startRow, startCol, endRow, endCol, slideManager) {
427
391
  const errors = []
428
- const slideXml = slideManager.getSlideXml(slideIndex)
429
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
430
- const tblObj = this.#findTableObj(slideObj, tableId)
392
+ const slideObj = slideManager.getSlideObj(slideIndex)
393
+ const tblObj = this.#findTableObj(slideObj, tableId, slideManager, slideIndex)
431
394
  if (!tblObj) {
432
395
  errors.push(`Table "${tableId}" not found in slide ${slideIndex}`)
433
396
  return { valid: false, errors }
@@ -507,9 +470,7 @@ class TableManager {
507
470
  throw new PPTXError(`Invalid merge region: ${validation.errors.join('; ')}`)
508
471
  }
509
472
 
510
- const slideXml = slideManager.getSlideXml(slideIndex)
511
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
512
- const tblObj = this.#findTableObj(slideObj, tableId)
473
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
513
474
  const trs = tblObj['a:tr'] || []
514
475
 
515
476
  const allTexts = []
@@ -561,8 +522,7 @@ class TableManager {
561
522
  const combinedText = allTexts.filter(t => t.trim() !== '').join('\n')
562
523
  this.#setCellTextObj(originCell, combinedText)
563
524
 
564
- const decl = this.#xmlParser.extractDeclaration(slideXml)
565
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
525
+ slideManager.markSlideObjDirty(slideIndex)
566
526
  }
567
527
 
568
528
  /**
@@ -587,9 +547,8 @@ class TableManager {
587
547
  actualEndCol = undefined
588
548
  }
589
549
 
590
- const slideXml = actualSlideManager.getSlideXml(slideIndex)
591
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
592
- const tblObj = this.#findTableObj(slideObj, tableId)
550
+ const slideObj = actualSlideManager.getSlideObj(slideIndex)
551
+ const tblObj = this.#findTableObj(slideObj, tableId, actualSlideManager, slideIndex)
593
552
  if (!tblObj) {
594
553
  throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
595
554
  }
@@ -629,8 +588,7 @@ class TableManager {
629
588
  }
630
589
  }
631
590
 
632
- const decl = this.#xmlParser.extractDeclaration(slideXml)
633
- actualSlideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
591
+ actualSlideManager.markSlideObjDirty(slideIndex)
634
592
  }
635
593
 
636
594
  /**
@@ -642,9 +600,8 @@ class TableManager {
642
600
  * @returns {Array<Object>} List of merged region coordinates
643
601
  */
644
602
  getMergedCells(slideIndex, tableId, slideManager) {
645
- const slideXml = slideManager.getSlideXml(slideIndex)
646
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
647
- const tblObj = this.#findTableObj(slideObj, tableId)
603
+ const slideObj = slideManager.getSlideObj(slideIndex)
604
+ const tblObj = this.#findTableObj(slideObj, tableId, slideManager, slideIndex)
648
605
  if (!tblObj) {
649
606
  throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
650
607
  }
@@ -759,9 +716,8 @@ class TableManager {
759
716
  slideManager
760
717
  )
761
718
 
762
- const slideXml = slideManager.getSlideXml(slideIndex)
763
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
764
- const tblObj = this.#findTableObj(slideObj, tableId)
719
+ const slideObj = slideManager.getSlideObj(slideIndex)
720
+ const tblObj = this.#findTableObj(slideObj, tableId, slideManager, slideIndex)
765
721
  if (!tblObj) return
766
722
 
767
723
  const trs = tblObj['a:tr'] || []
@@ -777,8 +733,7 @@ class TableManager {
777
733
  }
778
734
  }
779
735
 
780
- const decl = this.#xmlParser.extractDeclaration(slideXml)
781
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
736
+ slideManager.markSlideObjDirty(slideIndex)
782
737
  }
783
738
 
784
739
  /**
@@ -789,12 +744,7 @@ class TableManager {
789
744
  * @param {SlideManager} slideManager
790
745
  */
791
746
  autoFitTable(slideIndex, tableId, slideManager) {
792
- const slideXml = slideManager.getSlideXml(slideIndex)
793
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
794
- const tblObj = this.#findTableObj(slideObj, tableId)
795
- if (!tblObj) {
796
- throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
797
- }
747
+ const { tblObj } = this.#getTableContext(slideIndex, tableId, slideManager)
798
748
 
799
749
  const trs = tblObj['a:tr'] || []
800
750
  const gridCols = tblObj['a:tblGrid']?.['a:gridCol']
@@ -835,8 +785,7 @@ class TableManager {
835
785
  gridCols[c]['@_w'] = String(width)
836
786
  }
837
787
 
838
- const decl = this.#xmlParser.extractDeclaration(slideXml)
839
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
788
+ slideManager.markSlideObjDirty(slideIndex)
840
789
  }
841
790
 
842
791
  /**
@@ -849,10 +798,12 @@ class TableManager {
849
798
  * @param {SlideManager} slideManager
850
799
  */
851
800
  resizeTable(slideIndex, tableId, width, height, slideManager) {
852
- const slideXml = slideManager.getSlideXml(slideIndex)
853
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
801
+ const slideObj = slideManager.getSlideObj(slideIndex)
854
802
 
855
- const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
803
+ const spTree =
804
+ slideObj?.['p:sld']?.['p:cSld']?.['p:spTree'] ||
805
+ slideObj?.['p:sldLayout']?.['p:cSld']?.['p:spTree'] ||
806
+ slideObj?.['p:sldMaster']?.['p:cSld']?.['p:spTree']
856
807
  if (!spTree) return
857
808
 
858
809
  let frames = spTree['p:graphicFrame'] || []
@@ -931,8 +882,7 @@ class TableManager {
931
882
  }
932
883
  }
933
884
 
934
- const decl = this.#xmlParser.extractDeclaration(slideXml)
935
- slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
885
+ slideManager.markSlideObjDirty(slideIndex)
936
886
  }
937
887
 
938
888
  /**
@@ -943,11 +893,13 @@ class TableManager {
943
893
  * @returns {Array<{name: string, id: string, rows: number, cols: number}>}
944
894
  */
945
895
  inspectTables(slideIndex, slideManager) {
946
- const slideXml = slideManager.getSlideXml(slideIndex)
947
- const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
896
+ const slideObj = slideManager.getSlideObj(slideIndex)
948
897
  const tables = []
949
898
 
950
- const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
899
+ const spTree =
900
+ slideObj?.['p:sld']?.['p:cSld']?.['p:spTree'] ||
901
+ slideObj?.['p:sldLayout']?.['p:cSld']?.['p:spTree'] ||
902
+ slideObj?.['p:sldMaster']?.['p:cSld']?.['p:spTree']
951
903
  if (!spTree) return []
952
904
 
953
905
  let frames = spTree['p:graphicFrame'] || []
@@ -1064,8 +1016,16 @@ class TableManager {
1064
1016
  /**
1065
1017
  * Helper to find a table element inside a slide parsed object.
1066
1018
  */
1067
- #findTableObj(slideObj, tableId) {
1068
- const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
1019
+ #findTableObj(slideObj, tableId, slideManager, slideIndex) {
1020
+ if (slideManager && slideIndex !== undefined) {
1021
+ const res = slideManager.getSlideTable(slideIndex, tableId)
1022
+ return res ? res.table : null
1023
+ }
1024
+
1025
+ const spTree =
1026
+ slideObj?.['p:sld']?.['p:cSld']?.['p:spTree'] ||
1027
+ slideObj?.['p:sldLayout']?.['p:cSld']?.['p:spTree'] ||
1028
+ slideObj?.['p:sldMaster']?.['p:cSld']?.['p:spTree']
1069
1029
  if (!spTree) return null
1070
1030
 
1071
1031
  let frames = spTree['p:graphicFrame'] || []
@@ -1089,6 +1049,15 @@ class TableManager {
1089
1049
  return null
1090
1050
  }
1091
1051
 
1052
+ #getTableContext(slideIndex, tableId, slideManager) {
1053
+ const slideObj = slideManager.getSlideObj(slideIndex)
1054
+ const tblObj = this.#findTableObj(slideObj, tableId, slideManager, slideIndex)
1055
+ if (!tblObj) {
1056
+ throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
1057
+ }
1058
+ return { slideObj, tblObj }
1059
+ }
1060
+
1092
1061
  /**
1093
1062
  * Generates a new rowId for the given row object.
1094
1063
  */
@@ -2,14 +2,9 @@
2
2
  * @fileoverview ZOrderManager - Handles slide element Z-order (layer stacking) operations.
3
3
  */
4
4
 
5
- const { createLogger } = require('../utils/logger.js')
6
5
  const { PPTXError } = require('../utils/errors.js')
7
6
  const { Z_ORDER_SYMBOL } = require('../parsers/XMLParser.js')
8
7
 
9
- const logger = createLogger('ZOrderManager')
10
-
11
- const drawingTags = new Set(['p:sp', 'p:pic', 'p:graphicFrame', 'p:grpSp', 'p:cxnSp'])
12
-
13
8
  function detectElementType(tag, item) {
14
9
  if (tag === 'p:sp') {
15
10
  const isTxBox =