node-pptx-templater 1.0.4 → 1.0.6
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/CHANGELOG.md +65 -0
- package/README.md +1207 -151
- package/package.json +95 -3
- package/src/core/PPTXTemplater.js +270 -0
- package/src/core/ValidationEngine.js +77 -0
- package/src/index.js +16 -2
- package/src/managers/ZOrderManager.js +434 -0
- package/src/parsers/XMLParser.js +267 -3
- package/src/utils/xmlUtils.js +285 -30
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ZOrderManager - Handles slide element Z-order (layer stacking) operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { createLogger } = require('../utils/logger.js')
|
|
6
|
+
const { PPTXError } = require('../utils/errors.js')
|
|
7
|
+
const { Z_ORDER_SYMBOL } = require('../parsers/XMLParser.js')
|
|
8
|
+
|
|
9
|
+
const logger = createLogger('ZOrderManager')
|
|
10
|
+
|
|
11
|
+
const drawingTags = new Set(['p:sp', 'p:pic', 'p:graphicFrame', 'p:grpSp', 'p:cxnSp'])
|
|
12
|
+
|
|
13
|
+
function detectElementType(tag, item) {
|
|
14
|
+
if (tag === 'p:sp') {
|
|
15
|
+
const isTxBox =
|
|
16
|
+
item?.['p:nvSpPr']?.['p:cNvSpPr']?.['@_txBox'] === '1' ||
|
|
17
|
+
item?.['p:nvSpPr']?.['p:cNvSpPr']?.['@_txBox'] === true
|
|
18
|
+
const phType = item?.['p:nvSpPr']?.['p:nvPr']?.['p:ph']?.['@_type']
|
|
19
|
+
if (isTxBox || phType === 'title' || phType === 'body' || item?.['p:txBody']) {
|
|
20
|
+
return 'text'
|
|
21
|
+
}
|
|
22
|
+
return 'shape'
|
|
23
|
+
}
|
|
24
|
+
if (tag === 'p:pic') {
|
|
25
|
+
return 'image'
|
|
26
|
+
}
|
|
27
|
+
if (tag === 'p:graphicFrame') {
|
|
28
|
+
const uri = item?.['a:graphic']?.['a:graphicData']?.['@_uri'] || ''
|
|
29
|
+
if (uri.includes('chart')) return 'chart'
|
|
30
|
+
if (uri.includes('table')) return 'table'
|
|
31
|
+
if (uri.includes('diagram')) return 'smartart'
|
|
32
|
+
return 'graphicFrame'
|
|
33
|
+
}
|
|
34
|
+
if (tag === 'p:grpSp') {
|
|
35
|
+
return 'group'
|
|
36
|
+
}
|
|
37
|
+
if (tag === 'p:cxnSp') {
|
|
38
|
+
return 'connector'
|
|
39
|
+
}
|
|
40
|
+
return 'unknown'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @class ZOrderManager
|
|
45
|
+
* @description Manages stacking layers and z-index of shapes on slides.
|
|
46
|
+
*/
|
|
47
|
+
class ZOrderManager {
|
|
48
|
+
/** @private @type {XMLParser} */
|
|
49
|
+
#xmlParser
|
|
50
|
+
|
|
51
|
+
constructor(xmlParser) {
|
|
52
|
+
this.#xmlParser = xmlParser
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Helper to parse Z-order array for a container or initialize it if missing.
|
|
57
|
+
*/
|
|
58
|
+
getOrInitZOrder(container) {
|
|
59
|
+
if (!container[Z_ORDER_SYMBOL]) {
|
|
60
|
+
const list = []
|
|
61
|
+
for (const tag of ['p:sp', 'p:pic', 'p:graphicFrame', 'p:grpSp', 'p:cxnSp']) {
|
|
62
|
+
let items = container[tag] || []
|
|
63
|
+
if (!Array.isArray(items)) items = [items]
|
|
64
|
+
for (const item of items) {
|
|
65
|
+
let id = null
|
|
66
|
+
if (tag === 'p:sp') id = item?.['p:nvSpPr']?.['p:cNvPr']?.['@_id']
|
|
67
|
+
else if (tag === 'p:pic') id = item?.['p:nvPicPr']?.['p:cNvPr']?.['@_id']
|
|
68
|
+
else if (tag === 'p:graphicFrame')
|
|
69
|
+
id = item?.['p:nvGraphicFramePr']?.['p:cNvPr']?.['@_id']
|
|
70
|
+
else if (tag === 'p:grpSp') id = item?.['p:nvGrpSpPr']?.['p:cNvPr']?.['@_id']
|
|
71
|
+
else if (tag === 'p:cxnSp') id = item?.['p:nvCxnSpPr']?.['p:cNvPr']?.['@_id']
|
|
72
|
+
|
|
73
|
+
if (id !== undefined && id !== null) {
|
|
74
|
+
list.push(String(id))
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
container[Z_ORDER_SYMBOL] = list
|
|
79
|
+
}
|
|
80
|
+
return container[Z_ORDER_SYMBOL]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Helper to find drawing element and its parent container.
|
|
85
|
+
*/
|
|
86
|
+
findObjectByIdOrName(container, targetId) {
|
|
87
|
+
if (!container) return null
|
|
88
|
+
|
|
89
|
+
for (const tag of ['p:sp', 'p:pic', 'p:graphicFrame', 'p:grpSp', 'p:cxnSp']) {
|
|
90
|
+
let items = container[tag] || []
|
|
91
|
+
if (!Array.isArray(items)) items = [items]
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
let id = null
|
|
94
|
+
let name = null
|
|
95
|
+
if (tag === 'p:sp') {
|
|
96
|
+
id = item?.['p:nvSpPr']?.['p:cNvPr']?.['@_id']
|
|
97
|
+
name = item?.['p:nvSpPr']?.['p:cNvPr']?.['@_name']
|
|
98
|
+
} else if (tag === 'p:pic') {
|
|
99
|
+
id = item?.['p:nvPicPr']?.['p:cNvPr']?.['@_id']
|
|
100
|
+
name = item?.['p:nvPicPr']?.['p:cNvPr']?.['@_name']
|
|
101
|
+
} else if (tag === 'p:graphicFrame') {
|
|
102
|
+
id = item?.['p:nvGraphicFramePr']?.['p:cNvPr']?.['@_id']
|
|
103
|
+
name = item?.['p:nvGraphicFramePr']?.['p:cNvPr']?.['@_name']
|
|
104
|
+
} else if (tag === 'p:grpSp') {
|
|
105
|
+
id = item?.['p:nvGrpSpPr']?.['p:cNvPr']?.['@_id']
|
|
106
|
+
name = item?.['p:nvGrpSpPr']?.['p:cNvPr']?.['@_name']
|
|
107
|
+
} else if (tag === 'p:cxnSp') {
|
|
108
|
+
id = item?.['p:nvCxnSpPr']?.['p:cNvPr']?.['@_id']
|
|
109
|
+
name = item?.['p:nvCxnSpPr']?.['p:cNvPr']?.['@_name']
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (String(id) === String(targetId) || name === targetId) {
|
|
113
|
+
return { tag, obj: item, id: String(id), name, parent: container }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (tag === 'p:grpSp') {
|
|
117
|
+
const res = this.findObjectByIdOrName(item, targetId)
|
|
118
|
+
if (res) return res
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Retrieves the Z-order sequence of objects on a slide.
|
|
127
|
+
*
|
|
128
|
+
* @param {number} slideIndex
|
|
129
|
+
* @param {SlideManager} slideManager
|
|
130
|
+
* @returns {Array<Object>} List of object metadata in stacking order.
|
|
131
|
+
*/
|
|
132
|
+
getObjectOrder(slideIndex, slideManager) {
|
|
133
|
+
const slideXml = slideManager.getSlideXml(slideIndex)
|
|
134
|
+
const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
|
|
135
|
+
const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
|
|
136
|
+
if (!spTree) return []
|
|
137
|
+
|
|
138
|
+
const zOrder = this.getOrInitZOrder(spTree)
|
|
139
|
+
|
|
140
|
+
const drawingElements = new Map()
|
|
141
|
+
for (const tag of ['p:sp', 'p:pic', 'p:graphicFrame', 'p:grpSp', 'p:cxnSp']) {
|
|
142
|
+
let items = spTree[tag] || []
|
|
143
|
+
if (!Array.isArray(items)) items = [items]
|
|
144
|
+
for (const item of items) {
|
|
145
|
+
let id = null
|
|
146
|
+
let name = null
|
|
147
|
+
if (tag === 'p:sp') {
|
|
148
|
+
id = item?.['p:nvSpPr']?.['p:cNvPr']?.['@_id']
|
|
149
|
+
name = item?.['p:nvSpPr']?.['p:cNvPr']?.['@_name']
|
|
150
|
+
} else if (tag === 'p:pic') {
|
|
151
|
+
id = item?.['p:nvPicPr']?.['p:cNvPr']?.['@_id']
|
|
152
|
+
name = item?.['p:nvPicPr']?.['p:cNvPr']?.['@_name']
|
|
153
|
+
} else if (tag === 'p:graphicFrame') {
|
|
154
|
+
id = item?.['p:nvGraphicFramePr']?.['p:cNvPr']?.['@_id']
|
|
155
|
+
name = item?.['p:nvGraphicFramePr']?.['p:cNvPr']?.['@_name']
|
|
156
|
+
} else if (tag === 'p:grpSp') {
|
|
157
|
+
id = item?.['p:nvGrpSpPr']?.['p:cNvPr']?.['@_id']
|
|
158
|
+
name = item?.['p:nvGrpSpPr']?.['p:cNvPr']?.['@_name']
|
|
159
|
+
} else if (tag === 'p:cxnSp') {
|
|
160
|
+
id = item?.['p:nvCxnSpPr']?.['p:cNvPr']?.['@_id']
|
|
161
|
+
name = item?.['p:nvCxnSpPr']?.['p:cNvPr']?.['@_name']
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (id !== undefined && id !== null) {
|
|
165
|
+
drawingElements.set(String(id), { tag, obj: item, name })
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const fullZOrder = [...zOrder]
|
|
171
|
+
for (const id of drawingElements.keys()) {
|
|
172
|
+
if (!fullZOrder.includes(id)) {
|
|
173
|
+
fullZOrder.push(id)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const result = []
|
|
178
|
+
let zIndex = 1
|
|
179
|
+
for (const id of fullZOrder) {
|
|
180
|
+
const el = drawingElements.get(id)
|
|
181
|
+
if (!el) continue
|
|
182
|
+
|
|
183
|
+
result.push({
|
|
184
|
+
id: el.name || id,
|
|
185
|
+
type: detectElementType(el.tag, el.obj),
|
|
186
|
+
zIndex: zIndex++,
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return result
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Core reordering orchestrator.
|
|
195
|
+
* Runs the reorder callback on the correct container and saves the slide XML.
|
|
196
|
+
*/
|
|
197
|
+
#modifyZOrder(slideIndex, objectId, slideManager, callback) {
|
|
198
|
+
const slideXml = slideManager.getSlideXml(slideIndex)
|
|
199
|
+
const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
|
|
200
|
+
const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
|
|
201
|
+
if (!spTree) {
|
|
202
|
+
throw new PPTXError(`Invalid slide structure for slide ${slideIndex}`)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const res = this.findObjectByIdOrName(spTree, objectId)
|
|
206
|
+
if (!res) {
|
|
207
|
+
throw new PPTXError(`Object "${objectId}" not found on slide ${slideIndex}`)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const container = res.parent
|
|
211
|
+
const zOrder = this.getOrInitZOrder(container)
|
|
212
|
+
|
|
213
|
+
callback(zOrder, res.id, spTree)
|
|
214
|
+
|
|
215
|
+
const decl = this.#xmlParser.extractDeclaration(slideXml)
|
|
216
|
+
slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
bringForward(slideIndex, objectId, slideManager) {
|
|
220
|
+
this.#modifyZOrder(slideIndex, objectId, slideManager, (zOrder, elementId) => {
|
|
221
|
+
const idx = zOrder.indexOf(elementId)
|
|
222
|
+
if (idx !== -1 && idx < zOrder.length - 1) {
|
|
223
|
+
zOrder[idx] = zOrder[idx + 1]
|
|
224
|
+
zOrder[idx + 1] = elementId
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
sendBackward(slideIndex, objectId, slideManager) {
|
|
230
|
+
this.#modifyZOrder(slideIndex, objectId, slideManager, (zOrder, elementId) => {
|
|
231
|
+
const idx = zOrder.indexOf(elementId)
|
|
232
|
+
if (idx > 0) {
|
|
233
|
+
zOrder[idx] = zOrder[idx - 1]
|
|
234
|
+
zOrder[idx - 1] = elementId
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
bringToFront(slideIndex, objectId, slideManager) {
|
|
240
|
+
this.#modifyZOrder(slideIndex, objectId, slideManager, (zOrder, elementId) => {
|
|
241
|
+
const idx = zOrder.indexOf(elementId)
|
|
242
|
+
if (idx !== -1) {
|
|
243
|
+
zOrder.splice(idx, 1)
|
|
244
|
+
zOrder.push(elementId)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
sendToBack(slideIndex, objectId, slideManager) {
|
|
250
|
+
this.#modifyZOrder(slideIndex, objectId, slideManager, (zOrder, elementId) => {
|
|
251
|
+
const idx = zOrder.indexOf(elementId)
|
|
252
|
+
if (idx !== -1) {
|
|
253
|
+
zOrder.splice(idx, 1)
|
|
254
|
+
zOrder.unshift(elementId)
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
setZIndex(slideIndex, objectId, zIndex, slideManager) {
|
|
260
|
+
this.#modifyZOrder(slideIndex, objectId, slideManager, (zOrder, elementId) => {
|
|
261
|
+
const idx = zOrder.indexOf(elementId)
|
|
262
|
+
if (idx !== -1) {
|
|
263
|
+
zOrder.splice(idx, 1)
|
|
264
|
+
const targetIdx = Math.max(0, Math.min(zIndex - 1, zOrder.length))
|
|
265
|
+
zOrder.splice(targetIdx, 0, elementId)
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
moveObjectBefore(slideIndex, objectId, targetId, slideManager) {
|
|
271
|
+
this.#modifyZOrder(slideIndex, objectId, slideManager, (zOrder, elementId, spTree) => {
|
|
272
|
+
const targetRes = this.findObjectByIdOrName(spTree, targetId)
|
|
273
|
+
if (!targetRes) {
|
|
274
|
+
throw new PPTXError(`Target object "${targetId}" not found on slide ${slideIndex}`)
|
|
275
|
+
}
|
|
276
|
+
if (targetRes.parent !== targetRes.parent) {
|
|
277
|
+
throw new PPTXError('Cannot move elements across different group containers')
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const idx = zOrder.indexOf(elementId)
|
|
281
|
+
if (idx !== -1) {
|
|
282
|
+
zOrder.splice(idx, 1)
|
|
283
|
+
}
|
|
284
|
+
const targetIdx = zOrder.indexOf(targetRes.id)
|
|
285
|
+
if (targetIdx !== -1) {
|
|
286
|
+
zOrder.splice(targetIdx, 0, elementId)
|
|
287
|
+
} else {
|
|
288
|
+
zOrder.push(elementId)
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
moveObjectAfter(slideIndex, objectId, targetId, slideManager) {
|
|
294
|
+
this.#modifyZOrder(slideIndex, objectId, slideManager, (zOrder, elementId, spTree) => {
|
|
295
|
+
const targetRes = this.findObjectByIdOrName(spTree, targetId)
|
|
296
|
+
if (!targetRes) {
|
|
297
|
+
throw new PPTXError(`Target object "${targetId}" not found on slide ${slideIndex}`)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const idx = zOrder.indexOf(elementId)
|
|
301
|
+
if (idx !== -1) {
|
|
302
|
+
zOrder.splice(idx, 1)
|
|
303
|
+
}
|
|
304
|
+
const targetIdx = zOrder.indexOf(targetRes.id)
|
|
305
|
+
if (targetIdx !== -1) {
|
|
306
|
+
zOrder.splice(targetIdx + 1, 0, elementId)
|
|
307
|
+
} else {
|
|
308
|
+
zOrder.push(elementId)
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
reorderObjects(slideIndex, order, slideManager) {
|
|
314
|
+
const slideXml = slideManager.getSlideXml(slideIndex)
|
|
315
|
+
const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
|
|
316
|
+
const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
|
|
317
|
+
if (!spTree) return
|
|
318
|
+
|
|
319
|
+
const zOrder = this.getOrInitZOrder(spTree)
|
|
320
|
+
|
|
321
|
+
// Resolve ordered names/IDs to existing drawing IDs
|
|
322
|
+
const resolvedIds = []
|
|
323
|
+
for (const item of order) {
|
|
324
|
+
const res = this.findObjectByIdOrName(spTree, item)
|
|
325
|
+
if (res && res.parent === spTree) {
|
|
326
|
+
resolvedIds.push(res.id)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Keep track of unspecified IDs
|
|
331
|
+
const unspecifiedIds = zOrder.filter(id => !resolvedIds.includes(id))
|
|
332
|
+
|
|
333
|
+
// Reconstruct the z-order: unspecified bottom, specified top
|
|
334
|
+
spTree[Z_ORDER_SYMBOL] = [...unspecifiedIds, ...resolvedIds]
|
|
335
|
+
|
|
336
|
+
const decl = this.#xmlParser.extractDeclaration(slideXml)
|
|
337
|
+
slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
applyZOrder(slideIndex, configs, slideManager) {
|
|
341
|
+
if (!Array.isArray(configs)) return
|
|
342
|
+
|
|
343
|
+
for (const config of configs) {
|
|
344
|
+
if (!config.id) continue
|
|
345
|
+
|
|
346
|
+
if (config.bringToFront) {
|
|
347
|
+
this.bringToFront(slideIndex, config.id, slideManager)
|
|
348
|
+
} else if (config.sendToBack) {
|
|
349
|
+
this.sendToBack(slideIndex, config.id, slideManager)
|
|
350
|
+
} else if (config.bringForward) {
|
|
351
|
+
this.bringForward(slideIndex, config.id, slideManager)
|
|
352
|
+
} else if (config.sendBackward) {
|
|
353
|
+
this.sendBackward(slideIndex, config.id, slideManager)
|
|
354
|
+
} else if (config.zIndex !== undefined) {
|
|
355
|
+
this.setZIndex(slideIndex, config.id, config.zIndex, slideManager)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Layer Utilities
|
|
361
|
+
getTopMostObject(slideIndex, slideManager) {
|
|
362
|
+
const order = this.getObjectOrder(slideIndex, slideManager)
|
|
363
|
+
return order.length > 0 ? order[order.length - 1] : null
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
getBottomMostObject(slideIndex, slideManager) {
|
|
367
|
+
const order = this.getObjectOrder(slideIndex, slideManager)
|
|
368
|
+
return order.length > 0 ? order[0] : null
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
swapObjects(slideIndex, objectId1, objectId2, slideManager) {
|
|
372
|
+
this.#modifyZOrder(slideIndex, objectId1, slideManager, (zOrder, elementId1, spTree) => {
|
|
373
|
+
const res2 = this.findObjectByIdOrName(spTree, objectId2)
|
|
374
|
+
if (!res2) {
|
|
375
|
+
throw new PPTXError(`Object "${objectId2}" not found on slide ${slideIndex}`)
|
|
376
|
+
}
|
|
377
|
+
const elementId2 = res2.id
|
|
378
|
+
const idx1 = zOrder.indexOf(elementId1)
|
|
379
|
+
const idx2 = zOrder.indexOf(elementId2)
|
|
380
|
+
if (idx1 !== -1 && idx2 !== -1) {
|
|
381
|
+
zOrder[idx1] = elementId2
|
|
382
|
+
zOrder[idx2] = elementId1
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
sortObjects(slideIndex, compareFn, slideManager) {
|
|
388
|
+
const slideXml = slideManager.getSlideXml(slideIndex)
|
|
389
|
+
const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
|
|
390
|
+
const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
|
|
391
|
+
if (!spTree) return
|
|
392
|
+
|
|
393
|
+
const zOrder = this.getOrInitZOrder(spTree)
|
|
394
|
+
|
|
395
|
+
// Build complete info list
|
|
396
|
+
const order = this.getObjectOrder(slideIndex, slideManager)
|
|
397
|
+
|
|
398
|
+
// Sort order using compareFn
|
|
399
|
+
order.sort(compareFn)
|
|
400
|
+
|
|
401
|
+
// Map sorted IDs back
|
|
402
|
+
const sortedIds = []
|
|
403
|
+
for (const item of order) {
|
|
404
|
+
// Find ID by name or matching ID in container
|
|
405
|
+
const res = this.findObjectByIdOrName(spTree, item.id)
|
|
406
|
+
if (res && res.parent === spTree) {
|
|
407
|
+
sortedIds.push(res.id)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Preserve unspecified ones at bottom
|
|
412
|
+
const unspecifiedIds = zOrder.filter(id => !sortedIds.includes(id))
|
|
413
|
+
spTree[Z_ORDER_SYMBOL] = [...unspecifiedIds, ...sortedIds]
|
|
414
|
+
|
|
415
|
+
const decl = this.#xmlParser.extractDeclaration(slideXml)
|
|
416
|
+
slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
normalizeZOrder(slideIndex, slideManager) {
|
|
420
|
+
const slideXml = slideManager.getSlideXml(slideIndex)
|
|
421
|
+
const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
|
|
422
|
+
const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
|
|
423
|
+
if (!spTree) return
|
|
424
|
+
|
|
425
|
+
// Re-initialize and clean up
|
|
426
|
+
delete spTree[Z_ORDER_SYMBOL]
|
|
427
|
+
this.getOrInitZOrder(spTree)
|
|
428
|
+
|
|
429
|
+
const decl = this.#xmlParser.extractDeclaration(slideXml)
|
|
430
|
+
slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
module.exports = { ZOrderManager }
|