node-pptx-templater 1.0.10 → 1.0.12
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 +192 -0
- package/package.json +2 -2
- package/src/core/PPTXTemplater.js +157 -0
- package/src/core/ValidationEngine.js +170 -0
- package/src/managers/ChartManager.js +1431 -7
- package/src/managers/ShapeManager.js +89 -0
- package/src/managers/charts/ChartCacheGenerator.js +148 -59
|
@@ -47,6 +47,95 @@ class ShapeManager {
|
|
|
47
47
|
logger.debug(`Updated text for shape "${shapeId}" on slide ${slideIndex}`)
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Updates an existing shape's position and/or dimensions.
|
|
52
|
+
*
|
|
53
|
+
* @param {number} slideIndex
|
|
54
|
+
* @param {string} shapeId
|
|
55
|
+
* @param {Object} options Position and dimensions configuration.
|
|
56
|
+
* @param {number} [options.x] Absolute X offset coordinate (in EMUs).
|
|
57
|
+
* @param {number} [options.y] Absolute Y offset coordinate (in EMUs).
|
|
58
|
+
* @param {number} [options.width] Bounding box width (in EMUs).
|
|
59
|
+
* @param {number} [options.height] Bounding box height (in EMUs).
|
|
60
|
+
* @param {SlideManager} slideManager
|
|
61
|
+
*/
|
|
62
|
+
updateShapePosition(slideIndex, shapeId, options = {}, slideManager) {
|
|
63
|
+
const slideXml = slideManager.getSlideXml(slideIndex)
|
|
64
|
+
const slideObj = this.#xmlParser.parse(slideXml, `slide${slideIndex}.xml`)
|
|
65
|
+
const spTree = slideObj?.['p:sld']?.['p:cSld']?.['p:spTree']
|
|
66
|
+
const res = this.findShapeRecursive(spTree, shapeId)
|
|
67
|
+
|
|
68
|
+
if (!res) {
|
|
69
|
+
throw new PPTXError(`Shape "${shapeId}" not found in slide ${slideIndex}`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!res.shape['p:spPr']) {
|
|
73
|
+
res.shape['p:spPr'] = {}
|
|
74
|
+
}
|
|
75
|
+
if (!res.shape['p:spPr']['a:xfrm']) {
|
|
76
|
+
res.shape['p:spPr']['a:xfrm'] = {}
|
|
77
|
+
}
|
|
78
|
+
const xfrm = res.shape['p:spPr']['a:xfrm']
|
|
79
|
+
|
|
80
|
+
if (!xfrm['a:off']) {
|
|
81
|
+
xfrm['a:off'] = {}
|
|
82
|
+
}
|
|
83
|
+
if (!xfrm['a:ext']) {
|
|
84
|
+
xfrm['a:ext'] = {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (options.x !== undefined) {
|
|
88
|
+
xfrm['a:off']['@_x'] = String(Math.round(options.x))
|
|
89
|
+
} else if (xfrm['a:off']['@_x'] === undefined) {
|
|
90
|
+
xfrm['a:off']['@_x'] = '0'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (options.y !== undefined) {
|
|
94
|
+
xfrm['a:off']['@_y'] = String(Math.round(options.y))
|
|
95
|
+
} else if (xfrm['a:off']['@_y'] === undefined) {
|
|
96
|
+
xfrm['a:off']['@_y'] = '0'
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (options.width !== undefined) {
|
|
100
|
+
xfrm['a:ext']['@_cx'] = String(Math.round(options.width))
|
|
101
|
+
} else if (xfrm['a:ext']['@_cx'] === undefined) {
|
|
102
|
+
xfrm['a:ext']['@_cx'] = '0'
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (options.height !== undefined) {
|
|
106
|
+
xfrm['a:ext']['@_cy'] = String(Math.round(options.height))
|
|
107
|
+
} else if (xfrm['a:ext']['@_cy'] === undefined) {
|
|
108
|
+
xfrm['a:ext']['@_cy'] = '0'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const decl = this.#xmlParser.extractDeclaration(slideXml)
|
|
112
|
+
slideManager.setSlideXml(slideIndex, this.#xmlParser.build(slideObj, decl))
|
|
113
|
+
logger.debug(`Updated position/dimensions for shape "${shapeId}" on slide ${slideIndex}`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Updates an existing textbox shape's position and/or dimensions.
|
|
118
|
+
*
|
|
119
|
+
* @param {number} slideIndex
|
|
120
|
+
* @param {string} textBoxId
|
|
121
|
+
* @param {Object} options Position and dimensions configuration.
|
|
122
|
+
* @param {number} [options.x] Absolute X offset coordinate (in EMUs).
|
|
123
|
+
* @param {number} [options.y] Absolute Y offset coordinate (in EMUs).
|
|
124
|
+
* @param {number} [options.width] Bounding box width (in EMUs).
|
|
125
|
+
* @param {number} [options.height] Bounding box height (in EMUs).
|
|
126
|
+
* @param {SlideManager} slideManager
|
|
127
|
+
*/
|
|
128
|
+
updateTextBoxPosition(slideIndex, textBoxId, options = {}, slideManager) {
|
|
129
|
+
try {
|
|
130
|
+
this.updateShapePosition(slideIndex, textBoxId, options, slideManager)
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (err.message.includes('not found')) {
|
|
133
|
+
throw new PPTXError(`Textbox "${textBoxId}" not found in slide ${slideIndex}`)
|
|
134
|
+
}
|
|
135
|
+
throw err
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
50
139
|
/**
|
|
51
140
|
* Clones a shape and adds it with offsets.
|
|
52
141
|
*
|
|
@@ -170,6 +170,58 @@ class ChartCacheGenerator {
|
|
|
170
170
|
*
|
|
171
171
|
* 3. If no <c:title> block exists at all: create a minimal one.
|
|
172
172
|
*/
|
|
173
|
+
/**
|
|
174
|
+
* Extracts parts of <c:txPr> block (bodyPr, pPr, and converts defRPr to rPr)
|
|
175
|
+
* to use when creating custom rich text components.
|
|
176
|
+
*
|
|
177
|
+
* @param {string} txPrXml - The <c:txPr> XML string or block content.
|
|
178
|
+
* @returns {Object} { bodyPr, pPrXml, rPrXml }
|
|
179
|
+
*/
|
|
180
|
+
static extractTxPrParts(txPrXml) {
|
|
181
|
+
let bodyPr = '<a:bodyPr/>'
|
|
182
|
+
let pPrXml = ''
|
|
183
|
+
let rPrXml = ''
|
|
184
|
+
|
|
185
|
+
if (!txPrXml) {
|
|
186
|
+
return { bodyPr, pPrXml, rPrXml }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let txPrContent = txPrXml
|
|
190
|
+
const txPrInnerMatch = /<c:txPr>([\s\S]*?)<\/c:txPr>/.exec(txPrXml)
|
|
191
|
+
if (txPrInnerMatch) {
|
|
192
|
+
txPrContent = txPrInnerMatch[1]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Extract <a:bodyPr>
|
|
196
|
+
const bodyPrMatch = /(<a:bodyPr[^>]*\/>|<a:bodyPr[^>]*>[\s\S]*?<\/a:bodyPr>)/.exec(txPrContent)
|
|
197
|
+
if (bodyPrMatch) bodyPr = bodyPrMatch[1]
|
|
198
|
+
|
|
199
|
+
// Extract <a:defRPr> → convert to <a:rPr> for the run
|
|
200
|
+
const defRPrMatch = /(<a:defRPr[\s\S]*?<\/a:defRPr>|<a:defRPr[^>]*\/>)/.exec(txPrContent)
|
|
201
|
+
if (defRPrMatch) {
|
|
202
|
+
rPrXml = defRPrMatch[1].replace(/^<a:defRPr/, '<a:rPr').replace(/<\/a:defRPr>$/, '</a:rPr>')
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Extract <a:pPr> (keeps algn, indent, etc.) but strip <a:defRPr> from it
|
|
206
|
+
const pPrBlockMatch = /(<a:pPr[^>]*>)([\s\S]*?)(<\/a:pPr>)/.exec(txPrContent)
|
|
207
|
+
if (pPrBlockMatch) {
|
|
208
|
+
const innerContent = pPrBlockMatch[2]
|
|
209
|
+
.replace(/<a:defRPr(?:[^>]*\/>|[\s\S]*?<\/a:defRPr>)/g, '')
|
|
210
|
+
.trim()
|
|
211
|
+
const attrs = pPrBlockMatch[1].slice(7, -1).trim()
|
|
212
|
+
if (attrs || innerContent) {
|
|
213
|
+
pPrXml = innerContent
|
|
214
|
+
? `${pPrBlockMatch[1]}${innerContent}${pPrBlockMatch[3]}`
|
|
215
|
+
: `<a:pPr ${attrs}/>`
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
const scPPrMatch = /(<a:pPr[^>]*\/>)/.exec(txPrContent)
|
|
219
|
+
if (scPPrMatch) pPrXml = scPPrMatch[1]
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { bodyPr, pPrXml, rPrXml }
|
|
223
|
+
}
|
|
224
|
+
|
|
173
225
|
static updateTitle(xml, title) {
|
|
174
226
|
// Split by \n so callers can drive multi-paragraph titles
|
|
175
227
|
const titleLines = title.split('\n')
|
|
@@ -205,48 +257,7 @@ class ChartCacheGenerator {
|
|
|
205
257
|
|
|
206
258
|
// ── Strategy 2 ──────────────────────────────────────────────────────────────
|
|
207
259
|
// No <c:tx> yet – build one from <c:txPr> styles.
|
|
208
|
-
|
|
209
|
-
let pPrXml = '' // paragraph properties (alignment etc.) without defRPr
|
|
210
|
-
let rPr = '' // run properties from defRPr
|
|
211
|
-
|
|
212
|
-
const txPrMatch = /<c:txPr>([\s\S]*?)<\/c:txPr>/.exec(titleContent)
|
|
213
|
-
if (txPrMatch) {
|
|
214
|
-
const txPrContent = txPrMatch[1]
|
|
215
|
-
|
|
216
|
-
// Extract <a:bodyPr>
|
|
217
|
-
const bodyPrMatch = /(<a:bodyPr[^>]*\/>|<a:bodyPr[^>]*>[\s\S]*?<\/a:bodyPr>)/.exec(
|
|
218
|
-
txPrContent
|
|
219
|
-
)
|
|
220
|
-
if (bodyPrMatch) bodyPr = bodyPrMatch[1]
|
|
221
|
-
|
|
222
|
-
// Extract <a:defRPr> → convert to <a:rPr> for the run
|
|
223
|
-
const defRPrMatch = /(<a:defRPr[\s\S]*?<\/a:defRPr>|<a:defRPr[^>]*\/>)/.exec(txPrContent)
|
|
224
|
-
if (defRPrMatch) {
|
|
225
|
-
rPr = defRPrMatch[1].replace(/^<a:defRPr/, '<a:rPr').replace(/<\/a:defRPr>$/, '</a:rPr>')
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Extract <a:pPr> (keeps algn, indent, etc.) but strip <a:defRPr> from it
|
|
229
|
-
// since that is now expressed as <a:rPr> in the run.
|
|
230
|
-
// We must handle both <a:defRPr .../> (self-closing) and
|
|
231
|
-
// <a:defRPr ...>...</a:defRPr> (element with children).
|
|
232
|
-
const pPrBlockMatch = /(<a:pPr[^>]*>)([\s\S]*?)(<\/a:pPr>)/.exec(txPrContent)
|
|
233
|
-
if (pPrBlockMatch) {
|
|
234
|
-
const innerContent = pPrBlockMatch[2]
|
|
235
|
-
.replace(/<a:defRPr(?:[^>]*\/>|[\s\S]*?<\/a:defRPr>)/g, '')
|
|
236
|
-
.trim()
|
|
237
|
-
// Only emit pPr tag if it has attributes or remaining child content
|
|
238
|
-
const attrs = pPrBlockMatch[1].slice(7, -1).trim() // strip '<a:pPr' and '>'
|
|
239
|
-
if (attrs || innerContent) {
|
|
240
|
-
pPrXml = innerContent
|
|
241
|
-
? `${pPrBlockMatch[1]}${innerContent}${pPrBlockMatch[3]}`
|
|
242
|
-
: `<a:pPr ${attrs}/>`
|
|
243
|
-
}
|
|
244
|
-
} else {
|
|
245
|
-
// Self-closing <a:pPr .../> (no children)
|
|
246
|
-
const scPPrMatch = /(<a:pPr[^>]*\/>)/.exec(txPrContent)
|
|
247
|
-
if (scPPrMatch) pPrXml = scPPrMatch[1]
|
|
248
|
-
}
|
|
249
|
-
}
|
|
260
|
+
const { bodyPr, pPrXml, rPrXml: rPr } = this.extractTxPrParts(titleContent)
|
|
250
261
|
|
|
251
262
|
// Build one <a:p> per title line, each with the same pPr + rPr
|
|
252
263
|
const paragraphs = titleLines
|
|
@@ -282,16 +293,17 @@ class ChartCacheGenerator {
|
|
|
282
293
|
if (countMatch) pointsCount = parseInt(countMatch[1], 10)
|
|
283
294
|
}
|
|
284
295
|
}
|
|
285
|
-
if (pointsCount === 0 && options.labels) {
|
|
286
|
-
pointsCount = options.labels.length
|
|
287
|
-
}
|
|
288
296
|
|
|
289
|
-
// Parse existing styling and flags from the current <c:dLbls> block
|
|
290
297
|
let existingTxPr = ''
|
|
291
298
|
let existingDLblPos = ''
|
|
292
299
|
let existingNumFmt = ''
|
|
293
300
|
let existingSpPr = ''
|
|
301
|
+
let existingExtLst = ''
|
|
302
|
+
let existingShowLeaderLines = ''
|
|
294
303
|
const existingDLblSpPrs = {}
|
|
304
|
+
const existingDLblTxPrs = {}
|
|
305
|
+
const existingDLblLayouts = {}
|
|
306
|
+
const existingDLblPositions = {}
|
|
295
307
|
const existingShowTags = {}
|
|
296
308
|
|
|
297
309
|
const dLblsMatch = /<c:dLbls>([\s\S]*?)<\/c:dLbls>/.exec(content)
|
|
@@ -313,8 +325,16 @@ class ChartCacheGenerator {
|
|
|
313
325
|
if (spPrMatch) {
|
|
314
326
|
existingSpPr = spPrMatch[1]
|
|
315
327
|
}
|
|
328
|
+
const extLstMatch = /(<c:extLst>[\s\S]*?<\/c:extLst>)/.exec(dLblsContent)
|
|
329
|
+
if (extLstMatch) {
|
|
330
|
+
existingExtLst = extLstMatch[1]
|
|
331
|
+
}
|
|
332
|
+
const showLeaderMatch = /(<c:showLeaderLines\s+[^>]*\/>)/.exec(dLblsContent)
|
|
333
|
+
if (showLeaderMatch) {
|
|
334
|
+
existingShowLeaderLines = showLeaderMatch[1]
|
|
335
|
+
}
|
|
316
336
|
|
|
317
|
-
// Parse individual <c:dLbl>
|
|
337
|
+
// Parse individual <c:dLbl> properties: fills, text styling, layouts, and positions
|
|
318
338
|
const dLblPattern = /<c:dLbl>([\s\S]*?)<\/c:dLbl>/g
|
|
319
339
|
let dLblMatch
|
|
320
340
|
while ((dLblMatch = dLblPattern.exec(dLblsContent)) !== null) {
|
|
@@ -326,6 +346,18 @@ class ChartCacheGenerator {
|
|
|
326
346
|
if (dLblSpPrMatch) {
|
|
327
347
|
existingDLblSpPrs[idx] = dLblSpPrMatch[1]
|
|
328
348
|
}
|
|
349
|
+
const dLblTxPrMatch = /(<c:txPr>[\s\S]*?<\/c:txPr>)/.exec(dLblContent)
|
|
350
|
+
if (dLblTxPrMatch) {
|
|
351
|
+
existingDLblTxPrs[idx] = dLblTxPrMatch[1]
|
|
352
|
+
}
|
|
353
|
+
const dLblLayoutMatch = /(<c:layout>[\s\S]*?<\/c:layout>|<c:layout\/>)/.exec(dLblContent)
|
|
354
|
+
if (dLblLayoutMatch) {
|
|
355
|
+
existingDLblLayouts[idx] = dLblLayoutMatch[1]
|
|
356
|
+
}
|
|
357
|
+
const dLblPosMatch = /(<c:dLblPos\s+[^>]*\/>)/.exec(dLblContent)
|
|
358
|
+
if (dLblPosMatch) {
|
|
359
|
+
existingDLblPositions[idx] = dLblPosMatch[1]
|
|
360
|
+
}
|
|
329
361
|
}
|
|
330
362
|
}
|
|
331
363
|
|
|
@@ -356,7 +388,12 @@ class ChartCacheGenerator {
|
|
|
356
388
|
existingNumFmt,
|
|
357
389
|
existingShowTags,
|
|
358
390
|
existingSpPr,
|
|
359
|
-
existingDLblSpPrs
|
|
391
|
+
existingDLblSpPrs,
|
|
392
|
+
existingDLblTxPrs,
|
|
393
|
+
existingDLblLayouts,
|
|
394
|
+
existingDLblPositions,
|
|
395
|
+
existingExtLst,
|
|
396
|
+
existingShowLeaderLines
|
|
360
397
|
)
|
|
361
398
|
|
|
362
399
|
let updatedContent = content
|
|
@@ -386,9 +423,22 @@ class ChartCacheGenerator {
|
|
|
386
423
|
existingNumFmt = '',
|
|
387
424
|
existingShowTags = {},
|
|
388
425
|
existingSpPr = '',
|
|
389
|
-
existingDLblSpPrs = {}
|
|
426
|
+
existingDLblSpPrs = {},
|
|
427
|
+
existingDLblTxPrs = {},
|
|
428
|
+
existingDLblLayouts = {},
|
|
429
|
+
existingDLblPositions = {},
|
|
430
|
+
existingExtLst = '',
|
|
431
|
+
existingShowLeaderLines = ''
|
|
390
432
|
) {
|
|
391
|
-
const {
|
|
433
|
+
const {
|
|
434
|
+
labels,
|
|
435
|
+
labelsFromCells,
|
|
436
|
+
template,
|
|
437
|
+
position,
|
|
438
|
+
labelStyle,
|
|
439
|
+
labelMap,
|
|
440
|
+
showSeriesNameInBar,
|
|
441
|
+
} = options
|
|
392
442
|
|
|
393
443
|
let xml = '<c:dLbls>'
|
|
394
444
|
|
|
@@ -403,7 +453,10 @@ class ChartCacheGenerator {
|
|
|
403
453
|
top: 't',
|
|
404
454
|
bottom: 'b',
|
|
405
455
|
}
|
|
406
|
-
|
|
456
|
+
let openxmlPos = position ? posMap[position] : null
|
|
457
|
+
if (!openxmlPos && showSeriesNameInBar) {
|
|
458
|
+
openxmlPos = 'ctr'
|
|
459
|
+
}
|
|
407
460
|
|
|
408
461
|
const values = seriesData.values || []
|
|
409
462
|
const sumValues = values.reduce((sum, v) => sum + (Number(v) || 0), 0)
|
|
@@ -441,6 +494,11 @@ class ChartCacheGenerator {
|
|
|
441
494
|
xml += `<c:dLbl>`
|
|
442
495
|
xml += `<c:idx val="${i}"/>`
|
|
443
496
|
|
|
497
|
+
// Restore per-point layout (manual position offsets) from template
|
|
498
|
+
if (existingDLblLayouts && existingDLblLayouts[i]) {
|
|
499
|
+
xml += existingDLblLayouts[i]
|
|
500
|
+
}
|
|
501
|
+
|
|
444
502
|
if (labelsFromCells && !template) {
|
|
445
503
|
const range = ChartWorkbookUpdater.parseCellRange(labelsFromCells)
|
|
446
504
|
const startColNum = ChartWorkbookUpdater.colLetterToNum(range.startCol)
|
|
@@ -485,15 +543,30 @@ class ChartCacheGenerator {
|
|
|
485
543
|
xml += `</c:strRef>`
|
|
486
544
|
xml += `</c:tx>`
|
|
487
545
|
} else {
|
|
546
|
+
let bodyPr = '<a:bodyPr/>'
|
|
547
|
+
let pPrXml = ''
|
|
548
|
+
let rPrXml = ''
|
|
549
|
+
const dLblTxPr = (existingDLblTxPrs && existingDLblTxPrs[i]) || existingTxPr
|
|
550
|
+
if (dLblTxPr) {
|
|
551
|
+
const extracted = this.extractTxPrParts(dLblTxPr)
|
|
552
|
+
bodyPr = extracted.bodyPr
|
|
553
|
+
pPrXml = extracted.pPrXml
|
|
554
|
+
rPrXml = extracted.rPrXml
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const labelLines = String(textContent).split('\n')
|
|
558
|
+
const paragraphs = labelLines
|
|
559
|
+
.map(line => {
|
|
560
|
+
const escapedLine = this.#escapeXml(line)
|
|
561
|
+
return `<a:p>${pPrXml}<a:r>${rPrXml}<a:t>${escapedLine}</a:t></a:r></a:p>`
|
|
562
|
+
})
|
|
563
|
+
.join('')
|
|
564
|
+
|
|
488
565
|
xml += `<c:tx>`
|
|
489
566
|
xml += `<c:rich>`
|
|
490
|
-
xml +=
|
|
567
|
+
xml += bodyPr
|
|
491
568
|
xml += `<a:lstStyle/>`
|
|
492
|
-
xml +=
|
|
493
|
-
xml += `<a:r>`
|
|
494
|
-
xml += `<a:t>${this.#escapeXml(textContent)}</a:t>`
|
|
495
|
-
xml += `</a:r>`
|
|
496
|
-
xml += `</a:p>`
|
|
569
|
+
xml += paragraphs
|
|
497
570
|
xml += `</c:rich>`
|
|
498
571
|
xml += `</c:tx>`
|
|
499
572
|
}
|
|
@@ -520,12 +593,16 @@ class ChartCacheGenerator {
|
|
|
520
593
|
|
|
521
594
|
if (labelStyle) {
|
|
522
595
|
xml += this.generateTxPrXml(labelStyle)
|
|
596
|
+
} else if (existingDLblTxPrs && existingDLblTxPrs[i]) {
|
|
597
|
+
xml += existingDLblTxPrs[i]
|
|
523
598
|
} else if (existingTxPr) {
|
|
524
599
|
xml += existingTxPr
|
|
525
600
|
}
|
|
526
601
|
|
|
527
602
|
if (openxmlPos) {
|
|
528
603
|
xml += `<c:dLblPos val="${openxmlPos}"/>`
|
|
604
|
+
} else if (existingDLblPositions && existingDLblPositions[i]) {
|
|
605
|
+
xml += existingDLblPositions[i]
|
|
529
606
|
} else if (existingDLblPos) {
|
|
530
607
|
xml += existingDLblPos
|
|
531
608
|
}
|
|
@@ -571,7 +648,9 @@ class ChartCacheGenerator {
|
|
|
571
648
|
|
|
572
649
|
// showVal
|
|
573
650
|
const defaultShowVal = hasCustomLabels ? '0' : '1'
|
|
574
|
-
if (
|
|
651
|
+
if (showSeriesNameInBar) {
|
|
652
|
+
xml += `<c:showVal val="0"/>`
|
|
653
|
+
} else if (existingShowTags['showVal'] && !hasCustomLabels) {
|
|
575
654
|
xml += existingShowTags['showVal']
|
|
576
655
|
} else {
|
|
577
656
|
xml += `<c:showVal val="${defaultShowVal}"/>`
|
|
@@ -585,7 +664,9 @@ class ChartCacheGenerator {
|
|
|
585
664
|
}
|
|
586
665
|
|
|
587
666
|
// showSerName
|
|
588
|
-
if (
|
|
667
|
+
if (showSeriesNameInBar) {
|
|
668
|
+
xml += `<c:showSerName val="1"/>`
|
|
669
|
+
} else if (existingShowTags['showSerName']) {
|
|
589
670
|
xml += existingShowTags['showSerName']
|
|
590
671
|
} else {
|
|
591
672
|
xml += `<c:showSerName val="0"/>`
|
|
@@ -608,6 +689,14 @@ class ChartCacheGenerator {
|
|
|
608
689
|
xml += `<c:showBubbleSize val="0"/>`
|
|
609
690
|
}
|
|
610
691
|
|
|
692
|
+
// Restore showLeaderLines and extension list from template
|
|
693
|
+
if (existingShowLeaderLines) {
|
|
694
|
+
xml += existingShowLeaderLines
|
|
695
|
+
}
|
|
696
|
+
if (existingExtLst) {
|
|
697
|
+
xml += existingExtLst
|
|
698
|
+
}
|
|
699
|
+
|
|
611
700
|
xml += '</c:dLbls>'
|
|
612
701
|
return xml
|
|
613
702
|
}
|