node-pptx-templater 1.0.16 → 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.
- package/README.md +178 -4
- package/package.json +1 -1
- package/src/core/OutputWriter.js +12 -14
- package/src/core/PPTXTemplater.js +205 -5
- package/src/managers/ChartManager.js +0 -3
- package/src/managers/ImageManager.js +14 -18
- package/src/managers/MediaManager.js +151 -35
- package/src/managers/ShapeManager.js +19 -28
- package/src/managers/SlideManager.js +247 -4
- package/src/managers/TableManager.js +56 -87
- package/src/managers/ZOrderManager.js +0 -5
- package/src/managers/ZipManager.js +120 -14
- package/src/managers/charts/ChartWorkbookUpdater.js +1 -1
- package/src/utils/contentTypesHelper.js +6 -9
- package/src/utils/imageMetadata.js +227 -0
|
@@ -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
|
-
|
|
242
|
-
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
429
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
|
591
|
-
const
|
|
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
|
-
|
|
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
|
|
646
|
-
const
|
|
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
|
|
763
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
853
|
-
const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
|
|
801
|
+
const slideObj = slideManager.getSlideObj(slideIndex)
|
|
854
802
|
|
|
855
|
-
const 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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|