node-pptx-templater 1.0.18 → 1.0.20
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 +121 -0
- package/package.json +1 -1
- package/src/core/PPTXTemplater.js +186 -1
- package/src/managers/MediaManager.js +26 -98
- package/src/managers/ShapeManager.js +700 -0
- package/src/managers/TableManager.js +759 -69
|
@@ -66,8 +66,12 @@ class TableManager {
|
|
|
66
66
|
* @param {SlideManager} slideManager
|
|
67
67
|
* @throws {TableNotFoundError} If the table is not found.
|
|
68
68
|
*/
|
|
69
|
-
updateTable(slideIndex, tableId, data, slideManager) {
|
|
70
|
-
const { tblObj } = this.#getTableContext(
|
|
69
|
+
updateTable(slideIndex, tableId, data, slideManager, shapeManager) {
|
|
70
|
+
const { tblObj, frameObj, resolvedTableId } = this.#getTableContext(
|
|
71
|
+
slideIndex,
|
|
72
|
+
tableId,
|
|
73
|
+
slideManager
|
|
74
|
+
)
|
|
71
75
|
|
|
72
76
|
const trs = tblObj['a:tr'] || []
|
|
73
77
|
if (trs.length === 0) {
|
|
@@ -77,12 +81,14 @@ class TableManager {
|
|
|
77
81
|
|
|
78
82
|
let rowsData = []
|
|
79
83
|
let templateMerges = []
|
|
84
|
+
let cellShapes = null
|
|
80
85
|
|
|
81
86
|
if (Array.isArray(data)) {
|
|
82
87
|
rowsData = data
|
|
83
88
|
} else if (data && typeof data === 'object') {
|
|
84
89
|
rowsData = data.rows || []
|
|
85
90
|
templateMerges = data.merge || []
|
|
91
|
+
cellShapes = data.cellShapes || null
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
const headerTemplate = trs[0]
|
|
@@ -91,80 +97,113 @@ class TableManager {
|
|
|
91
97
|
const newRows = []
|
|
92
98
|
const generatedMerges = []
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
endCol: j + colSpan - 1,
|
|
122
|
-
})
|
|
100
|
+
const headerNames = (trs[0]['a:tc'] || []).map(cell => this.#getCellText(cell).trim())
|
|
101
|
+
const isObjectRows =
|
|
102
|
+
rowsData.length > 0 && !Array.isArray(rowsData[0]) && typeof rowsData[0] === 'object'
|
|
103
|
+
|
|
104
|
+
if (isObjectRows) {
|
|
105
|
+
// 1. Keep/clone the header row
|
|
106
|
+
const headerRow = this.#xmlParser.deepClone(headerTemplate)
|
|
107
|
+
this.#updateRowId(headerRow)
|
|
108
|
+
newRows.push(headerRow)
|
|
109
|
+
|
|
110
|
+
// 2. Map objects to data rows
|
|
111
|
+
for (let i = 0; i < rowsData.length; i++) {
|
|
112
|
+
const newRow = this.#xmlParser.deepClone(dataTemplate)
|
|
113
|
+
this.#updateRowId(newRow)
|
|
114
|
+
|
|
115
|
+
const tcs = newRow['a:tc'] || []
|
|
116
|
+
const rowObj = rowsData[i]
|
|
117
|
+
|
|
118
|
+
for (let j = 0; j < tcs.length; j++) {
|
|
119
|
+
const headerName = headerNames[j]
|
|
120
|
+
let rawCell = undefined
|
|
121
|
+
if (headerName) {
|
|
122
|
+
if (rowObj[headerName] !== undefined) {
|
|
123
|
+
rawCell = rowObj[headerName]
|
|
124
|
+
} else if (rowObj[headerName.toLowerCase()] !== undefined) {
|
|
125
|
+
rawCell = rowObj[headerName.toLowerCase()]
|
|
126
|
+
}
|
|
123
127
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
this.#setCellTextObj(tcs[j], val)
|
|
130
|
-
|
|
131
|
-
// Apply style properties if specified on the cell object
|
|
132
|
-
if (cellOptions.fill) {
|
|
133
|
-
if (!tcs[j]['a:tcPr']) tcs[j]['a:tcPr'] = {}
|
|
134
|
-
tcs[j]['a:tcPr']['a:solidFill'] = {
|
|
135
|
-
'a:srgbClr': { '@_val': cellOptions.fill },
|
|
128
|
+
if (rawCell === undefined && rowObj[j] !== undefined) {
|
|
129
|
+
rawCell = rowObj[j]
|
|
130
|
+
}
|
|
131
|
+
if (rawCell === undefined) {
|
|
132
|
+
rawCell = ''
|
|
136
133
|
}
|
|
137
|
-
}
|
|
138
134
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
135
|
+
let val = ''
|
|
136
|
+
let cellOptions = {}
|
|
137
|
+
|
|
138
|
+
if (tcs[j]['@_hMerge']) delete tcs[j]['@_hMerge']
|
|
139
|
+
if (tcs[j]['@_vMerge']) delete tcs[j]['@_vMerge']
|
|
140
|
+
if (tcs[j]['@_gridSpan']) delete tcs[j]['@_gridSpan']
|
|
141
|
+
if (tcs[j]['@_rowSpan']) delete tcs[j]['@_rowSpan']
|
|
142
|
+
|
|
143
|
+
if (rawCell && typeof rawCell === 'object') {
|
|
144
|
+
val = rawCell.value !== undefined ? rawCell.value : ''
|
|
145
|
+
const rowSpan = parseInt(rawCell.rowSpan || 1, 10)
|
|
146
|
+
const colSpan = parseInt(rawCell.colSpan || rawCell.gridSpan || 1, 10)
|
|
147
|
+
if (rowSpan > 1 || colSpan > 1) {
|
|
148
|
+
generatedMerges.push({
|
|
149
|
+
startRow: i + 1,
|
|
150
|
+
startCol: j,
|
|
151
|
+
endRow: i + 1 + rowSpan - 1,
|
|
152
|
+
endCol: j + colSpan - 1,
|
|
153
|
+
})
|
|
146
154
|
}
|
|
155
|
+
cellOptions = rawCell
|
|
156
|
+
} else {
|
|
157
|
+
val = String(rawCell)
|
|
147
158
|
}
|
|
148
|
-
}
|
|
149
159
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
this.#setCellTextObj(tcs[j], val)
|
|
161
|
+
this.#applyCellOptions(tcs[j], cellOptions)
|
|
162
|
+
}
|
|
163
|
+
newRows.push(newRow)
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
// 2D array mapping
|
|
167
|
+
for (let i = 0; i < rowsData.length; i++) {
|
|
168
|
+
const template = i === 0 ? headerTemplate : trs[i] || dataTemplate
|
|
169
|
+
const newRow = this.#xmlParser.deepClone(template)
|
|
170
|
+
this.#updateRowId(newRow)
|
|
171
|
+
|
|
172
|
+
const tcs = newRow['a:tc'] || []
|
|
173
|
+
const rowData = rowsData[i]
|
|
174
|
+
|
|
175
|
+
for (let j = 0; j < tcs.length; j++) {
|
|
176
|
+
const rawCell = rowData && rowData[j] !== undefined ? rowData[j] : ''
|
|
177
|
+
let val = ''
|
|
178
|
+
let cellOptions = {}
|
|
179
|
+
|
|
180
|
+
if (tcs[j]['@_hMerge']) delete tcs[j]['@_hMerge']
|
|
181
|
+
if (tcs[j]['@_vMerge']) delete tcs[j]['@_vMerge']
|
|
182
|
+
if (tcs[j]['@_gridSpan']) delete tcs[j]['@_gridSpan']
|
|
183
|
+
if (tcs[j]['@_rowSpan']) delete tcs[j]['@_rowSpan']
|
|
184
|
+
|
|
185
|
+
if (rawCell && typeof rawCell === 'object') {
|
|
186
|
+
val = rawCell.value !== undefined ? rawCell.value : ''
|
|
187
|
+
const rowSpan = parseInt(rawCell.rowSpan || 1, 10)
|
|
188
|
+
const colSpan = parseInt(rawCell.colSpan || rawCell.gridSpan || 1, 10)
|
|
189
|
+
if (rowSpan > 1 || colSpan > 1) {
|
|
190
|
+
generatedMerges.push({
|
|
191
|
+
startRow: i,
|
|
192
|
+
startCol: j,
|
|
193
|
+
endRow: i + rowSpan - 1,
|
|
194
|
+
endCol: j + colSpan - 1,
|
|
195
|
+
})
|
|
163
196
|
}
|
|
197
|
+
cellOptions = rawCell
|
|
198
|
+
} else {
|
|
199
|
+
val = String(rawCell)
|
|
164
200
|
}
|
|
201
|
+
|
|
202
|
+
this.#setCellTextObj(tcs[j], val)
|
|
203
|
+
this.#applyCellOptions(tcs[j], cellOptions)
|
|
165
204
|
}
|
|
205
|
+
newRows.push(newRow)
|
|
166
206
|
}
|
|
167
|
-
newRows.push(newRow)
|
|
168
207
|
}
|
|
169
208
|
|
|
170
209
|
tblObj['a:tr'] = newRows
|
|
@@ -184,6 +223,21 @@ class TableManager {
|
|
|
184
223
|
)
|
|
185
224
|
}
|
|
186
225
|
|
|
226
|
+
if (cellShapes) {
|
|
227
|
+
this.#processCellShapes(
|
|
228
|
+
slideIndex,
|
|
229
|
+
tableId,
|
|
230
|
+
resolvedTableId,
|
|
231
|
+
rowsData,
|
|
232
|
+
isObjectRows,
|
|
233
|
+
cellShapes,
|
|
234
|
+
slideManager,
|
|
235
|
+
shapeManager,
|
|
236
|
+
tblObj,
|
|
237
|
+
frameObj
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
187
241
|
logger.debug(
|
|
188
242
|
`Updated table "${tableId}" with ${rowsData.length} rows and ${finalMerges.length} merges`
|
|
189
243
|
)
|
|
@@ -1051,11 +1105,647 @@ class TableManager {
|
|
|
1051
1105
|
|
|
1052
1106
|
#getTableContext(slideIndex, tableId, slideManager) {
|
|
1053
1107
|
const slideObj = slideManager.getSlideObj(slideIndex)
|
|
1054
|
-
const
|
|
1055
|
-
if (!
|
|
1108
|
+
const res = slideManager.getSlideTable(slideIndex, tableId)
|
|
1109
|
+
if (!res || !res.table) {
|
|
1056
1110
|
throw new TableNotFoundError(`Table "${tableId}" not found in slide ${slideIndex}`)
|
|
1057
1111
|
}
|
|
1058
|
-
|
|
1112
|
+
const cNvPr = res.frame?.['p:nvGraphicFramePr']?.['p:cNvPr']
|
|
1113
|
+
const resolvedTableId = cNvPr ? cNvPr['@_name'] || String(cNvPr['@_id']) : tableId
|
|
1114
|
+
return { slideObj, tblObj: res.table, frameObj: res.frame, resolvedTableId }
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
#applyCellOptions(cellObj, cellOptions) {
|
|
1118
|
+
if (cellOptions.fill) {
|
|
1119
|
+
if (!cellObj['a:tcPr']) cellObj['a:tcPr'] = {}
|
|
1120
|
+
cellObj['a:tcPr']['a:solidFill'] = {
|
|
1121
|
+
'a:srgbClr': { '@_val': cellOptions.fill },
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
if (cellOptions.align) {
|
|
1126
|
+
const txBody = cellObj['a:txBody']
|
|
1127
|
+
if (txBody && txBody['a:p']) {
|
|
1128
|
+
const paras = Array.isArray(txBody['a:p']) ? txBody['a:p'] : [txBody['a:p']]
|
|
1129
|
+
for (const p of paras) {
|
|
1130
|
+
if (!p['a:pPr']) p['a:pPr'] = {}
|
|
1131
|
+
p['a:pPr']['@_algn'] = cellOptions.align
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (cellOptions.fontSize) {
|
|
1137
|
+
const sizeVal = cellOptions.fontSize * 100
|
|
1138
|
+
const txBody = cellObj['a:txBody']
|
|
1139
|
+
if (txBody && txBody['a:p']) {
|
|
1140
|
+
const paras = Array.isArray(txBody['a:p']) ? txBody['a:p'] : [txBody['a:p']]
|
|
1141
|
+
for (const p of paras) {
|
|
1142
|
+
if (p['a:r']) {
|
|
1143
|
+
const runs = Array.isArray(p['a:r']) ? p['a:r'] : [p['a:r']]
|
|
1144
|
+
for (const r of runs) {
|
|
1145
|
+
if (!r['a:rPr']) r['a:rPr'] = {}
|
|
1146
|
+
r['a:rPr']['@_sz'] = String(sizeVal)
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
#expandCellShape(config, cellBounds) {
|
|
1155
|
+
const cellLeft_px = cellBounds.left / 9525
|
|
1156
|
+
const cellTop_px = cellBounds.top / 9525
|
|
1157
|
+
const cellWidth_px = cellBounds.width / 9525
|
|
1158
|
+
const cellHeight_px = cellBounds.height / 9525
|
|
1159
|
+
|
|
1160
|
+
if (config.type === 'progressBar') {
|
|
1161
|
+
const value = config.value !== undefined ? config.value : 0
|
|
1162
|
+
const max = config.max !== undefined ? config.max : 100
|
|
1163
|
+
const fill = config.fill || '#3B82F6'
|
|
1164
|
+
const bgFill = config.backgroundFill || '#E5E7EB'
|
|
1165
|
+
const pbHeight = config.height !== undefined ? config.height : 8
|
|
1166
|
+
const pbWidth = config.width !== undefined ? config.width : cellWidth_px - 10
|
|
1167
|
+
|
|
1168
|
+
const pbX = cellLeft_px + (config.x !== undefined ? config.x : (cellWidth_px - pbWidth) / 2)
|
|
1169
|
+
const pbY = cellTop_px + (config.y !== undefined ? config.y : (cellHeight_px - pbHeight) / 2)
|
|
1170
|
+
|
|
1171
|
+
const shapes = []
|
|
1172
|
+
shapes.push({
|
|
1173
|
+
type: 'roundedRectangle',
|
|
1174
|
+
fill: bgFill,
|
|
1175
|
+
x: pbX,
|
|
1176
|
+
y: pbY,
|
|
1177
|
+
width: pbWidth,
|
|
1178
|
+
height: pbHeight,
|
|
1179
|
+
borderRadius: pbHeight / 2,
|
|
1180
|
+
zIndex: config.zIndex,
|
|
1181
|
+
})
|
|
1182
|
+
|
|
1183
|
+
const pct = Math.min(1, Math.max(0, value / max))
|
|
1184
|
+
if (pct > 0) {
|
|
1185
|
+
const filledWidth = pbWidth * pct
|
|
1186
|
+
shapes.push({
|
|
1187
|
+
type: 'roundedRectangle',
|
|
1188
|
+
fill: fill,
|
|
1189
|
+
x: pbX,
|
|
1190
|
+
y: pbY,
|
|
1191
|
+
width: filledWidth,
|
|
1192
|
+
height: pbHeight,
|
|
1193
|
+
borderRadius: pbHeight / 2,
|
|
1194
|
+
zIndex: (config.zIndex || 0) + 1,
|
|
1195
|
+
})
|
|
1196
|
+
}
|
|
1197
|
+
return shapes
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
if (config.type === 'badge') {
|
|
1201
|
+
const text = String(config.text !== undefined ? config.text : '')
|
|
1202
|
+
const fontSize = config.textStyle?.fontSize || 10
|
|
1203
|
+
const textWidth = text.length * fontSize * 0.6
|
|
1204
|
+
const paddingX = 12
|
|
1205
|
+
const badgeWidth = config.width !== undefined ? config.width : textWidth + paddingX * 2
|
|
1206
|
+
const badgeHeight = config.height !== undefined ? config.height : fontSize + 12
|
|
1207
|
+
|
|
1208
|
+
const x = cellLeft_px + (config.x !== undefined ? config.x : (cellWidth_px - badgeWidth) / 2)
|
|
1209
|
+
const y = cellTop_px + (config.y !== undefined ? config.y : (cellHeight_px - badgeHeight) / 2)
|
|
1210
|
+
|
|
1211
|
+
return [
|
|
1212
|
+
{
|
|
1213
|
+
type: 'roundedRectangle',
|
|
1214
|
+
fill: config.fill || '#10B981',
|
|
1215
|
+
borderRadius: badgeHeight / 2,
|
|
1216
|
+
x: x,
|
|
1217
|
+
y: y,
|
|
1218
|
+
width: badgeWidth,
|
|
1219
|
+
height: badgeHeight,
|
|
1220
|
+
text: text,
|
|
1221
|
+
textStyle: {
|
|
1222
|
+
color: config.textStyle?.color || '#FFFFFF',
|
|
1223
|
+
fontSize: fontSize,
|
|
1224
|
+
bold: config.textStyle?.bold !== undefined ? config.textStyle.bold : true,
|
|
1225
|
+
align: 'center',
|
|
1226
|
+
},
|
|
1227
|
+
border: config.border,
|
|
1228
|
+
transparency: config.transparency,
|
|
1229
|
+
shadow: config.shadow,
|
|
1230
|
+
rotation: config.rotation,
|
|
1231
|
+
zIndex: config.zIndex,
|
|
1232
|
+
},
|
|
1233
|
+
]
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
if (config.type === 'icon') {
|
|
1237
|
+
const size = config.size || 16
|
|
1238
|
+
const iconFill = config.fill
|
|
1239
|
+
const fontSize = Math.round(size * 0.8)
|
|
1240
|
+
|
|
1241
|
+
let baseConfig = null
|
|
1242
|
+
switch (config.icon) {
|
|
1243
|
+
case 'check':
|
|
1244
|
+
baseConfig = {
|
|
1245
|
+
type: 'rectangle',
|
|
1246
|
+
fill: 'none',
|
|
1247
|
+
border: null,
|
|
1248
|
+
width: size,
|
|
1249
|
+
height: size,
|
|
1250
|
+
text: '✔',
|
|
1251
|
+
textStyle: {
|
|
1252
|
+
color: iconFill || '#10B981',
|
|
1253
|
+
bold: true,
|
|
1254
|
+
fontSize: fontSize,
|
|
1255
|
+
align: 'center',
|
|
1256
|
+
},
|
|
1257
|
+
}
|
|
1258
|
+
break
|
|
1259
|
+
case 'cross':
|
|
1260
|
+
baseConfig = {
|
|
1261
|
+
type: 'rectangle',
|
|
1262
|
+
fill: 'none',
|
|
1263
|
+
border: null,
|
|
1264
|
+
width: size,
|
|
1265
|
+
height: size,
|
|
1266
|
+
text: '✘',
|
|
1267
|
+
textStyle: {
|
|
1268
|
+
color: iconFill || '#EF4444',
|
|
1269
|
+
bold: true,
|
|
1270
|
+
fontSize: fontSize,
|
|
1271
|
+
align: 'center',
|
|
1272
|
+
},
|
|
1273
|
+
}
|
|
1274
|
+
break
|
|
1275
|
+
case 'warning':
|
|
1276
|
+
baseConfig = {
|
|
1277
|
+
type: 'triangle',
|
|
1278
|
+
fill: iconFill || '#F59E0B',
|
|
1279
|
+
border: null,
|
|
1280
|
+
width: size,
|
|
1281
|
+
height: size,
|
|
1282
|
+
text: '!',
|
|
1283
|
+
textStyle: {
|
|
1284
|
+
color: '#FFFFFF',
|
|
1285
|
+
bold: true,
|
|
1286
|
+
fontSize: Math.round(fontSize * 0.7),
|
|
1287
|
+
align: 'center',
|
|
1288
|
+
},
|
|
1289
|
+
}
|
|
1290
|
+
break
|
|
1291
|
+
case 'info':
|
|
1292
|
+
baseConfig = {
|
|
1293
|
+
type: 'circle',
|
|
1294
|
+
fill: iconFill || '#3B82F6',
|
|
1295
|
+
border: null,
|
|
1296
|
+
radius: size / 2,
|
|
1297
|
+
text: 'i',
|
|
1298
|
+
textStyle: {
|
|
1299
|
+
color: '#FFFFFF',
|
|
1300
|
+
bold: true,
|
|
1301
|
+
fontSize: Math.round(fontSize * 0.7),
|
|
1302
|
+
align: 'center',
|
|
1303
|
+
},
|
|
1304
|
+
}
|
|
1305
|
+
break
|
|
1306
|
+
case 'star':
|
|
1307
|
+
baseConfig = {
|
|
1308
|
+
type: 'star5',
|
|
1309
|
+
fill: iconFill || '#FBBF24',
|
|
1310
|
+
border: null,
|
|
1311
|
+
width: size,
|
|
1312
|
+
height: size,
|
|
1313
|
+
}
|
|
1314
|
+
break
|
|
1315
|
+
case 'up':
|
|
1316
|
+
baseConfig = {
|
|
1317
|
+
type: 'upArrow',
|
|
1318
|
+
fill: iconFill || '#10B981',
|
|
1319
|
+
border: null,
|
|
1320
|
+
width: size,
|
|
1321
|
+
height: size,
|
|
1322
|
+
}
|
|
1323
|
+
break
|
|
1324
|
+
case 'down':
|
|
1325
|
+
baseConfig = {
|
|
1326
|
+
type: 'downArrow',
|
|
1327
|
+
fill: iconFill || '#EF4444',
|
|
1328
|
+
border: null,
|
|
1329
|
+
width: size,
|
|
1330
|
+
height: size,
|
|
1331
|
+
}
|
|
1332
|
+
break
|
|
1333
|
+
case 'arrow-right':
|
|
1334
|
+
baseConfig = {
|
|
1335
|
+
type: 'rightArrow',
|
|
1336
|
+
fill: iconFill || '#3B82F6',
|
|
1337
|
+
border: null,
|
|
1338
|
+
width: size,
|
|
1339
|
+
height: size,
|
|
1340
|
+
}
|
|
1341
|
+
break
|
|
1342
|
+
case 'arrow-left':
|
|
1343
|
+
baseConfig = {
|
|
1344
|
+
type: 'leftArrow',
|
|
1345
|
+
fill: iconFill || '#3B82F6',
|
|
1346
|
+
border: null,
|
|
1347
|
+
width: size,
|
|
1348
|
+
height: size,
|
|
1349
|
+
}
|
|
1350
|
+
break
|
|
1351
|
+
default:
|
|
1352
|
+
return []
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
const x = cellLeft_px + (config.x !== undefined ? config.x : (cellWidth_px - size) / 2)
|
|
1356
|
+
const y = cellTop_px + (config.y !== undefined ? config.y : (cellHeight_px - size) / 2)
|
|
1357
|
+
|
|
1358
|
+
baseConfig.x = x
|
|
1359
|
+
baseConfig.y = y
|
|
1360
|
+
baseConfig.zIndex = config.zIndex
|
|
1361
|
+
if (config.border) baseConfig.border = config.border
|
|
1362
|
+
if (config.transparency !== undefined) baseConfig.transparency = config.transparency
|
|
1363
|
+
if (config.shadow !== undefined) baseConfig.shadow = config.shadow
|
|
1364
|
+
if (config.rotation !== undefined) baseConfig.rotation = config.rotation
|
|
1365
|
+
|
|
1366
|
+
return [baseConfig]
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const shapeWidth =
|
|
1370
|
+
config.width !== undefined
|
|
1371
|
+
? config.width
|
|
1372
|
+
: config.size !== undefined
|
|
1373
|
+
? config.size
|
|
1374
|
+
: config.radius !== undefined
|
|
1375
|
+
? config.radius * 2
|
|
1376
|
+
: 12
|
|
1377
|
+
const shapeHeight =
|
|
1378
|
+
config.height !== undefined
|
|
1379
|
+
? config.height
|
|
1380
|
+
: config.size !== undefined
|
|
1381
|
+
? config.size
|
|
1382
|
+
: config.radius !== undefined
|
|
1383
|
+
? config.radius * 2
|
|
1384
|
+
: 12
|
|
1385
|
+
|
|
1386
|
+
let shapeLeft = cellLeft_px
|
|
1387
|
+
let shapeTop = cellTop_px
|
|
1388
|
+
|
|
1389
|
+
if (config.position === 'center') {
|
|
1390
|
+
shapeLeft = cellLeft_px + (cellWidth_px - shapeWidth) / 2
|
|
1391
|
+
shapeTop = cellTop_px + (cellHeight_px - shapeHeight) / 2
|
|
1392
|
+
} else if (config.position === 'left') {
|
|
1393
|
+
shapeLeft = cellLeft_px + 5
|
|
1394
|
+
shapeTop = cellTop_px + (cellHeight_px - shapeHeight) / 2
|
|
1395
|
+
} else if (config.position === 'right') {
|
|
1396
|
+
shapeLeft = cellLeft_px + cellWidth_px - shapeWidth - 5
|
|
1397
|
+
shapeTop = cellTop_px + (cellHeight_px - shapeHeight) / 2
|
|
1398
|
+
} else if (config.position === 'top') {
|
|
1399
|
+
shapeLeft = cellLeft_px + (cellWidth_px - shapeWidth) / 2
|
|
1400
|
+
shapeTop = cellTop_px + 5
|
|
1401
|
+
} else if (config.position === 'bottom') {
|
|
1402
|
+
shapeLeft = cellLeft_px + (cellWidth_px - shapeWidth) / 2
|
|
1403
|
+
shapeTop = cellTop_px + cellHeight_px - shapeHeight - 5
|
|
1404
|
+
} else {
|
|
1405
|
+
if (config.x === undefined && config.y === undefined) {
|
|
1406
|
+
shapeLeft = cellLeft_px + (cellWidth_px - shapeWidth) / 2
|
|
1407
|
+
shapeTop = cellTop_px + (cellHeight_px - shapeHeight) / 2
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
if (config.x !== undefined) {
|
|
1412
|
+
shapeLeft += config.x
|
|
1413
|
+
}
|
|
1414
|
+
if (config.y !== undefined) {
|
|
1415
|
+
shapeTop += config.y
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
const expanded = Object.assign({}, config, {
|
|
1419
|
+
x: shapeLeft,
|
|
1420
|
+
y: shapeTop,
|
|
1421
|
+
})
|
|
1422
|
+
|
|
1423
|
+
if (expanded.type === 'circle' && expanded.radius === undefined) {
|
|
1424
|
+
expanded.radius = shapeWidth / 2
|
|
1425
|
+
}
|
|
1426
|
+
if (expanded.type === 'square' && expanded.size === undefined) {
|
|
1427
|
+
expanded.size = shapeWidth
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
return [expanded]
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
#processCellShapes(
|
|
1434
|
+
slideIndex,
|
|
1435
|
+
tableId,
|
|
1436
|
+
resolvedTableId,
|
|
1437
|
+
rowsData,
|
|
1438
|
+
isObjectRows,
|
|
1439
|
+
cellShapes,
|
|
1440
|
+
slideManager,
|
|
1441
|
+
shapeManager,
|
|
1442
|
+
tblObj,
|
|
1443
|
+
frameObj
|
|
1444
|
+
) {
|
|
1445
|
+
if (!cellShapes || !shapeManager) return
|
|
1446
|
+
|
|
1447
|
+
const shapes = shapeManager.getShapes(slideIndex, slideManager)
|
|
1448
|
+
const prefixToDelete = `cellshape_${resolvedTableId}_`
|
|
1449
|
+
const existingNames = shapes
|
|
1450
|
+
.map(s => s.name)
|
|
1451
|
+
.filter(name => name && name.startsWith(prefixToDelete))
|
|
1452
|
+
|
|
1453
|
+
for (const name of existingNames) {
|
|
1454
|
+
try {
|
|
1455
|
+
shapeManager.deleteShape(slideIndex, name, slideManager)
|
|
1456
|
+
} catch (err) {
|
|
1457
|
+
logger.warn(`Failed to delete existing cell shape "${name}": ${err.message}`)
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
const xfrm = frameObj['p:xfrm']
|
|
1462
|
+
const tableX = xfrm?.['a:off']?.['@_x'] ? parseInt(xfrm['a:off']['@_x'], 10) : 0
|
|
1463
|
+
const tableY = xfrm?.['a:off']?.['@_y'] ? parseInt(xfrm['a:off']['@_y'], 10) : 0
|
|
1464
|
+
|
|
1465
|
+
const gridCols = tblObj['a:tblGrid']?.['a:gridCol'] || []
|
|
1466
|
+
const gridColsArr = Array.isArray(gridCols) ? gridCols : [gridCols]
|
|
1467
|
+
const colWidths = gridColsArr.map(col => parseInt(col['@_w'] || 0, 10))
|
|
1468
|
+
|
|
1469
|
+
const trsArr = tblObj['a:tr'] || []
|
|
1470
|
+
const rowHeights = trsArr.map(row => parseInt(row['@_h'] || 0, 10))
|
|
1471
|
+
|
|
1472
|
+
const getCellBounds = (r, c) => {
|
|
1473
|
+
const parent = this.getMergeParent(slideIndex, tableId, r, c, slideManager)
|
|
1474
|
+
const pr = parent.row
|
|
1475
|
+
const pc = parent.col
|
|
1476
|
+
|
|
1477
|
+
let cellLeft = tableX
|
|
1478
|
+
for (let idx = 0; idx < pc; idx++) {
|
|
1479
|
+
cellLeft += colWidths[idx] || 0
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
let cellTop = tableY
|
|
1483
|
+
for (let idx = 0; idx < pr; idx++) {
|
|
1484
|
+
cellTop += rowHeights[idx] || 0
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
const parentCell = trsArr[pr]?.['a:tc']?.[pc]
|
|
1488
|
+
const gridSpan = parentCell?.['@_gridSpan'] ? parseInt(parentCell['@_gridSpan'], 10) : 1
|
|
1489
|
+
const rowSpan = parentCell?.['@_rowSpan'] ? parseInt(parentCell['@_rowSpan'], 10) : 1
|
|
1490
|
+
|
|
1491
|
+
let cellWidth = 0
|
|
1492
|
+
for (let idx = 0; idx < gridSpan; idx++) {
|
|
1493
|
+
cellWidth += colWidths[pc + idx] || 0
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
let cellHeight = 0
|
|
1497
|
+
for (let idx = 0; idx < rowSpan; idx++) {
|
|
1498
|
+
cellHeight += rowHeights[pr + idx] || 0
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
return {
|
|
1502
|
+
left: cellLeft,
|
|
1503
|
+
top: cellTop,
|
|
1504
|
+
width: cellWidth,
|
|
1505
|
+
height: cellHeight,
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
const shapesToCreate = []
|
|
1510
|
+
const headerNames = (trsArr[0]?.['a:tc'] || []).map(cell => this.#getCellText(cell).trim())
|
|
1511
|
+
|
|
1512
|
+
for (let i = 0; i < rowsData.length; i++) {
|
|
1513
|
+
const rowData = rowsData[i]
|
|
1514
|
+
const finalRowIndex = isObjectRows ? i + 1 : i
|
|
1515
|
+
|
|
1516
|
+
const numCols = trsArr[finalRowIndex]?.['a:tc']?.length || 0
|
|
1517
|
+
for (let j = 0; j < numCols; j++) {
|
|
1518
|
+
const headerName = headerNames[j]
|
|
1519
|
+
let shapeFn = null
|
|
1520
|
+
|
|
1521
|
+
if (headerName) {
|
|
1522
|
+
shapeFn = cellShapes[headerName] || cellShapes[headerName.toLowerCase()]
|
|
1523
|
+
}
|
|
1524
|
+
if (!shapeFn) {
|
|
1525
|
+
shapeFn = cellShapes[j]
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
if (typeof shapeFn !== 'function') continue
|
|
1529
|
+
|
|
1530
|
+
let configs = shapeFn(rowData, i)
|
|
1531
|
+
if (!configs) continue
|
|
1532
|
+
|
|
1533
|
+
if (!Array.isArray(configs)) {
|
|
1534
|
+
configs = [configs]
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
configs.forEach((config, shapeIdx) => {
|
|
1538
|
+
shapesToCreate.push({
|
|
1539
|
+
config,
|
|
1540
|
+
rowIndex: finalRowIndex,
|
|
1541
|
+
colIndex: j,
|
|
1542
|
+
shapeIndex: shapeIdx,
|
|
1543
|
+
})
|
|
1544
|
+
})
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
shapesToCreate.sort((a, b) => (a.config.zIndex || 0) - (b.config.zIndex || 0))
|
|
1549
|
+
|
|
1550
|
+
shapesToCreate.forEach(item => {
|
|
1551
|
+
const bounds = getCellBounds(item.rowIndex, item.colIndex)
|
|
1552
|
+
const expandedConfigs = this.#expandCellShape(item.config, bounds)
|
|
1553
|
+
|
|
1554
|
+
expandedConfigs.forEach((expandedConfig, expIdx) => {
|
|
1555
|
+
const finalShapeIndex =
|
|
1556
|
+
expandedConfigs.length > 1 ? `${item.shapeIndex}_${expIdx}` : item.shapeIndex
|
|
1557
|
+
expandedConfig.id = `cellshape_${resolvedTableId}_${item.rowIndex}_${item.colIndex}_${finalShapeIndex}`
|
|
1558
|
+
|
|
1559
|
+
shapeManager.addShape(slideIndex, expandedConfig, slideManager)
|
|
1560
|
+
})
|
|
1561
|
+
})
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
addCellShape(slideIndex, tableId, rowIndex, colIndex, options, slideManager, shapeManager) {
|
|
1565
|
+
const { tblObj, frameObj, resolvedTableId } = this.#getTableContext(
|
|
1566
|
+
slideIndex,
|
|
1567
|
+
tableId,
|
|
1568
|
+
slideManager
|
|
1569
|
+
)
|
|
1570
|
+
|
|
1571
|
+
const xfrm = frameObj['p:xfrm']
|
|
1572
|
+
const tableX = xfrm?.['a:off']?.['@_x'] ? parseInt(xfrm['a:off']['@_x'], 10) : 0
|
|
1573
|
+
const tableY = xfrm?.['a:off']?.['@_y'] ? parseInt(xfrm['a:off']['@_y'], 10) : 0
|
|
1574
|
+
|
|
1575
|
+
const gridCols = tblObj['a:tblGrid']?.['a:gridCol'] || []
|
|
1576
|
+
const gridColsArr = Array.isArray(gridCols) ? gridCols : [gridCols]
|
|
1577
|
+
const colWidths = gridColsArr.map(col => parseInt(col['@_w'] || 0, 10))
|
|
1578
|
+
|
|
1579
|
+
const trsArr = tblObj['a:tr'] || []
|
|
1580
|
+
const rowHeights = trsArr.map(row => parseInt(row['@_h'] || 0, 10))
|
|
1581
|
+
|
|
1582
|
+
const parent = this.getMergeParent(slideIndex, tableId, rowIndex, colIndex, slideManager)
|
|
1583
|
+
const pr = parent.row
|
|
1584
|
+
const pc = parent.col
|
|
1585
|
+
|
|
1586
|
+
let cellLeft = tableX
|
|
1587
|
+
for (let idx = 0; idx < pc; idx++) {
|
|
1588
|
+
cellLeft += colWidths[idx] || 0
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
let cellTop = tableY
|
|
1592
|
+
for (let idx = 0; idx < pr; idx++) {
|
|
1593
|
+
cellTop += rowHeights[idx] || 0
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
const parentCell = trsArr[pr]?.['a:tc']?.[pc]
|
|
1597
|
+
const gridSpan = parentCell?.['@_gridSpan'] ? parseInt(parentCell['@_gridSpan'], 10) : 1
|
|
1598
|
+
const rowSpan = parentCell?.['@_rowSpan'] ? parseInt(parentCell['@_rowSpan'], 10) : 1
|
|
1599
|
+
|
|
1600
|
+
let cellWidth = 0
|
|
1601
|
+
for (let idx = 0; idx < gridSpan; idx++) {
|
|
1602
|
+
cellWidth += colWidths[pc + idx] || 0
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
let cellHeight = 0
|
|
1606
|
+
for (let idx = 0; idx < rowSpan; idx++) {
|
|
1607
|
+
cellHeight += rowHeights[pr + idx] || 0
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
const bounds = { left: cellLeft, top: cellTop, width: cellWidth, height: cellHeight }
|
|
1611
|
+
|
|
1612
|
+
const shapes = shapeManager.getShapes(slideIndex, slideManager)
|
|
1613
|
+
const prefix = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_`
|
|
1614
|
+
let maxShapeIndex = -1
|
|
1615
|
+
for (const s of shapes) {
|
|
1616
|
+
if (s.name && s.name.startsWith(prefix)) {
|
|
1617
|
+
const remaining = s.name.slice(prefix.length)
|
|
1618
|
+
const parts = remaining.split('_')
|
|
1619
|
+
const idxVal = parseInt(parts[0], 10)
|
|
1620
|
+
if (!isNaN(idxVal) && idxVal > maxShapeIndex) {
|
|
1621
|
+
maxShapeIndex = idxVal
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
const nextShapeIndex = maxShapeIndex + 1
|
|
1626
|
+
|
|
1627
|
+
const expandedConfigs = this.#expandCellShape(options, bounds)
|
|
1628
|
+
|
|
1629
|
+
expandedConfigs.forEach((expandedConfig, expIdx) => {
|
|
1630
|
+
const finalShapeIndex =
|
|
1631
|
+
expandedConfigs.length > 1 ? `${nextShapeIndex}_${expIdx}` : nextShapeIndex
|
|
1632
|
+
expandedConfig.id = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_${finalShapeIndex}`
|
|
1633
|
+
|
|
1634
|
+
shapeManager.addShape(slideIndex, expandedConfig, slideManager)
|
|
1635
|
+
})
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
updateCellShape(
|
|
1639
|
+
slideIndex,
|
|
1640
|
+
tableId,
|
|
1641
|
+
rowIndex,
|
|
1642
|
+
colIndex,
|
|
1643
|
+
shapeIndex,
|
|
1644
|
+
options,
|
|
1645
|
+
slideManager,
|
|
1646
|
+
shapeManager
|
|
1647
|
+
) {
|
|
1648
|
+
const { tblObj, frameObj, resolvedTableId } = this.#getTableContext(
|
|
1649
|
+
slideIndex,
|
|
1650
|
+
tableId,
|
|
1651
|
+
slideManager
|
|
1652
|
+
)
|
|
1653
|
+
|
|
1654
|
+
const shapes = shapeManager.getShapes(slideIndex, slideManager)
|
|
1655
|
+
const prefix = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_${shapeIndex}`
|
|
1656
|
+
const matchingShapes = shapes.filter(
|
|
1657
|
+
s => s.name && (s.name === prefix || s.name.startsWith(prefix + '_'))
|
|
1658
|
+
)
|
|
1659
|
+
|
|
1660
|
+
if (matchingShapes.length === 0) {
|
|
1661
|
+
throw new PPTXError(`Cell shape "${shapeIndex}" not found in cell (${rowIndex}, ${colIndex})`)
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
for (const s of matchingShapes) {
|
|
1665
|
+
shapeManager.deleteShape(slideIndex, s.name, slideManager)
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
const xfrm = frameObj['p:xfrm']
|
|
1669
|
+
const tableX = xfrm?.['a:off']?.['@_x'] ? parseInt(xfrm['a:off']['@_x'], 10) : 0
|
|
1670
|
+
const tableY = xfrm?.['a:off']?.['@_y'] ? parseInt(xfrm['a:off']['@_y'], 10) : 0
|
|
1671
|
+
|
|
1672
|
+
const gridCols = tblObj['a:tblGrid']?.['a:gridCol'] || []
|
|
1673
|
+
const gridColsArr = Array.isArray(gridCols) ? gridCols : [gridCols]
|
|
1674
|
+
const colWidths = gridColsArr.map(col => parseInt(col['@_w'] || 0, 10))
|
|
1675
|
+
|
|
1676
|
+
const trsArr = tblObj['a:tr'] || []
|
|
1677
|
+
const rowHeights = trsArr.map(row => parseInt(row['@_h'] || 0, 10))
|
|
1678
|
+
|
|
1679
|
+
const parent = this.getMergeParent(slideIndex, tableId, rowIndex, colIndex, slideManager)
|
|
1680
|
+
const pr = parent.row
|
|
1681
|
+
const pc = parent.col
|
|
1682
|
+
|
|
1683
|
+
let cellLeft = tableX
|
|
1684
|
+
for (let idx = 0; idx < pc; idx++) {
|
|
1685
|
+
cellLeft += colWidths[idx] || 0
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
let cellTop = tableY
|
|
1689
|
+
for (let idx = 0; idx < pr; idx++) {
|
|
1690
|
+
cellTop += rowHeights[idx] || 0
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
const parentCell = trsArr[pr]?.['a:tc']?.[pc]
|
|
1694
|
+
const gridSpan = parentCell?.['@_gridSpan'] ? parseInt(parentCell['@_gridSpan'], 10) : 1
|
|
1695
|
+
const rowSpan = parentCell?.['@_rowSpan'] ? parseInt(parentCell['@_rowSpan'], 10) : 1
|
|
1696
|
+
|
|
1697
|
+
let cellWidth = 0
|
|
1698
|
+
for (let idx = 0; idx < gridSpan; idx++) {
|
|
1699
|
+
cellWidth += colWidths[pc + idx] || 0
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
let cellHeight = 0
|
|
1703
|
+
for (let idx = 0; idx < rowSpan; idx++) {
|
|
1704
|
+
cellHeight += rowHeights[pr + idx] || 0
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
const bounds = { left: cellLeft, top: cellTop, width: cellWidth, height: cellHeight }
|
|
1708
|
+
|
|
1709
|
+
const expandedConfigs = this.#expandCellShape(options, bounds)
|
|
1710
|
+
|
|
1711
|
+
expandedConfigs.forEach((expandedConfig, expIdx) => {
|
|
1712
|
+
const finalShapeIndex = expandedConfigs.length > 1 ? `${shapeIndex}_${expIdx}` : shapeIndex
|
|
1713
|
+
expandedConfig.id = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_${finalShapeIndex}`
|
|
1714
|
+
|
|
1715
|
+
shapeManager.addShape(slideIndex, expandedConfig, slideManager)
|
|
1716
|
+
})
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
removeCellShape(slideIndex, tableId, rowIndex, colIndex, shapeIndex, slideManager, shapeManager) {
|
|
1720
|
+
const { resolvedTableId } = this.#getTableContext(slideIndex, tableId, slideManager)
|
|
1721
|
+
|
|
1722
|
+
const shapes = shapeManager.getShapes(slideIndex, slideManager)
|
|
1723
|
+
const prefix = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_${shapeIndex}`
|
|
1724
|
+
const matchingShapes = shapes.filter(
|
|
1725
|
+
s => s.name && (s.name === prefix || s.name.startsWith(prefix + '_'))
|
|
1726
|
+
)
|
|
1727
|
+
|
|
1728
|
+
if (matchingShapes.length === 0) {
|
|
1729
|
+
throw new PPTXError(`Cell shape "${shapeIndex}" not found in cell (${rowIndex}, ${colIndex})`)
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
for (const s of matchingShapes) {
|
|
1733
|
+
shapeManager.deleteShape(slideIndex, s.name, slideManager)
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
getCellShape(slideIndex, tableId, rowIndex, colIndex, shapeIndex, slideManager, shapeManager) {
|
|
1738
|
+
const { resolvedTableId } = this.#getTableContext(slideIndex, tableId, slideManager)
|
|
1739
|
+
|
|
1740
|
+
const prefix = `cellshape_${resolvedTableId}_${rowIndex}_${colIndex}_${shapeIndex}`
|
|
1741
|
+
const shapes = shapeManager.getShapes(slideIndex, slideManager)
|
|
1742
|
+
const primaryShape = shapes.find(
|
|
1743
|
+
s => s.name === prefix || s.name === `${prefix}_0` || s.name === `${prefix}_1`
|
|
1744
|
+
)
|
|
1745
|
+
|
|
1746
|
+
if (!primaryShape) return null
|
|
1747
|
+
|
|
1748
|
+
return shapeManager.getShape(slideIndex, primaryShape.name, slideManager)
|
|
1059
1749
|
}
|
|
1060
1750
|
|
|
1061
1751
|
/**
|