@shbernal/ts-pptx 3.5.0 → 3.7.0

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.
Files changed (67) hide show
  1. package/README.md +8 -0
  2. package/dist/{autopage-BYlTzX-l.js → autopage-DkjYZUFD.js} +3 -3
  3. package/dist/{autopage-BYlTzX-l.js.map → autopage-DkjYZUFD.js.map} +1 -1
  4. package/dist/browser.d.ts +5 -5
  5. package/dist/browser.js +4 -4
  6. package/dist/browser.js.map +1 -1
  7. package/dist/{diagnostics-BDuR8UkG.d.ts → diagnostics-DfBdDHre.d.ts} +20 -5
  8. package/dist/diagnostics-DfBdDHre.d.ts.map +1 -0
  9. package/dist/{errors-CgnueTYL.d.ts → errors-PflMPWBn.d.ts} +2 -2
  10. package/dist/{errors-CgnueTYL.d.ts.map → errors-PflMPWBn.d.ts.map} +1 -1
  11. package/dist/{fit-TjpNbMru.js → fit-BKs0zxMI.js} +3 -3
  12. package/dist/{fit-TjpNbMru.js.map → fit-BKs0zxMI.js.map} +1 -1
  13. package/dist/{html-dom-yUSdCExA.js → html-dom-B0I0QSbU.js} +4 -4
  14. package/dist/{html-dom-yUSdCExA.js.map → html-dom-B0I0QSbU.js.map} +1 -1
  15. package/dist/html.d.ts +3 -3
  16. package/dist/html.js +1 -1
  17. package/dist/index.d.ts +5 -5
  18. package/dist/index.js +3 -3
  19. package/dist/inspect.d.ts +1 -1
  20. package/dist/inspect.js +4 -5
  21. package/dist/inspect.js.map +1 -1
  22. package/dist/math.d.ts +1 -1
  23. package/dist/measure.d.ts +2 -2
  24. package/dist/measure.js +1 -1
  25. package/dist/{namespaces-CcKuzeuE.js → namespaces-BVnNu7sg.js} +74 -3
  26. package/dist/namespaces-BVnNu7sg.js.map +1 -0
  27. package/dist/node.d.ts +5 -5
  28. package/dist/node.js +3 -3
  29. package/dist/{notes-DNqAggXx.js → notes-CJFJaG6M.js} +310 -313
  30. package/dist/notes-CJFJaG6M.js.map +1 -0
  31. package/dist/{pres-Dybf7ZUl.d.ts → pres-BcBR3CSM.d.ts} +2 -2
  32. package/dist/{pres-Dybf7ZUl.d.ts.map → pres-BcBR3CSM.d.ts.map} +1 -1
  33. package/dist/{presentation-VfJ_FT7E.js → presentation-DrJtj3-C.js} +414 -515
  34. package/dist/presentation-DrJtj3-C.js.map +1 -0
  35. package/dist/{presentation-types-CpkTx9Xa.d.ts → presentation-types-CDjScsev.d.ts} +351 -274
  36. package/dist/presentation-types-CDjScsev.d.ts.map +1 -0
  37. package/dist/read.d.ts +31 -48
  38. package/dist/read.d.ts.map +1 -1
  39. package/dist/read.js +3 -3
  40. package/dist/script.d.ts +2 -2
  41. package/dist/script.d.ts.map +1 -1
  42. package/dist/script.js +18 -47
  43. package/dist/script.js.map +1 -1
  44. package/dist/{shapes-Kt51vry6.js → shapes-BemroU-z.js} +625 -265
  45. package/dist/shapes-BemroU-z.js.map +1 -0
  46. package/dist/{slide-B_aInw3N.d.ts → slide-ZqAdVvHV.d.ts} +1135 -1058
  47. package/dist/slide-ZqAdVvHV.d.ts.map +1 -0
  48. package/dist/{text-D04NUMvA.js → text-B8SLEaZx.js} +2089 -1418
  49. package/dist/text-B8SLEaZx.js.map +1 -0
  50. package/dist/units-1kRzYShk.js +231 -0
  51. package/dist/units-1kRzYShk.js.map +1 -0
  52. package/dist/units-internal-Dg95w_YD.js +753 -0
  53. package/dist/units-internal-Dg95w_YD.js.map +1 -0
  54. package/dist/zip.d.ts +1 -1
  55. package/package.json +2 -1
  56. package/dist/diagnostics-BDuR8UkG.d.ts.map +0 -1
  57. package/dist/diagnostics-CPVADSL1.js +0 -90
  58. package/dist/diagnostics-CPVADSL1.js.map +0 -1
  59. package/dist/namespaces-CcKuzeuE.js.map +0 -1
  60. package/dist/notes-DNqAggXx.js.map +0 -1
  61. package/dist/presentation-VfJ_FT7E.js.map +0 -1
  62. package/dist/presentation-types-CpkTx9Xa.d.ts.map +0 -1
  63. package/dist/shapes-Kt51vry6.js.map +0 -1
  64. package/dist/slide-B_aInw3N.d.ts.map +0 -1
  65. package/dist/text-D04NUMvA.js.map +0 -1
  66. package/dist/units-internal-DNvK2I6o.js +0 -896
  67. package/dist/units-internal-DNvK2I6o.js.map +0 -1
@@ -1,10 +1,10 @@
1
- import { i as warnOnce, r as warn } from "./diagnostics-CPVADSL1.js";
1
+ import { S as warnOnce, a as FIXED_PCT_PER_PERCENT, d as emuToInches, g as pixelsToEmu, i as EMU_PER_POINT, l as STANDARD_LAYOUTS, m as inchesToEmu, r as EMU_PER_INCH, s as PERCENT_SCALE, v as ptToHundredths, x as warn } from "./units-1kRzYShk.js";
2
2
  import { n as InvalidOptionError, o as UnsupportedFeatureError, r as MediaError, t as InternalError } from "./errors-Bvln0H-7.js";
3
- import { $ as VALID_SHAPE_PRESETS, A as IMG_BROKEN, B as XML_DECL, C as DEF_PRES_LAYOUT, D as DEF_SLIDE_MARGIN_IN, E as DEF_SHAPE_SHADOW, G as ChartType, I as PIECHART_COLORS, J as SchemeColor, L as REGEX_HEX_COLOR, M as IMG_SVG_PLACEHOLDER, N as LAYOUT_IDX_SERIES_BASE, P as LETTERS, R as SLDNUMFLDID, S as DEF_FONT_COLOR, _ as BARCHART_COLORS, _t as inchesToEmu, a as inch2Emu, b as DEF_CHART_BORDER, ct as FIXED_PCT_PER_PERCENT, et as asChartType, f as AXIS_ID_CATEGORY_PRIMARY, ft as STANDARD_LAYOUTS, g as AXIS_ID_VALUE_SECONDARY, h as AXIS_ID_VALUE_PRIMARY, i as getSmartParseNumber, k as DEF_TEXT_SHADOW, l as ptsToEmuLenient, m as AXIS_ID_SERIES_PRIMARY, mt as emuToInches, n as convertArcAngle, nt as isChartExType, o as lineWidthToEmu, ot as EMU_PER_INCH, p as AXIS_ID_CATEGORY_SECONDARY, q as PlaceholderType, r as convertRotationDegrees, rt as isChartType, s as marginToEmu, st as EMU_PER_POINT, t as convertAngleUnits, tt as connectorPresetFor, u as resolveTableColWidthsEmu, ut as PERCENT_SCALE, v as DEF_CELL_BORDER, w as DEF_PRES_LAYOUT_NAME, x as DEF_CHART_GRIDLINE, xt as ptToHundredths, y as DEF_CELL_MARGIN_IN, yt as pixelsToEmu, z as SLDNUM_PLACEHOLDER_TEXT } from "./units-internal-DNvK2I6o.js";
4
- import { A as getDuplicateObjectNames, C as createShadowEffectLst, D as voidEl, E as raw, F as validateObjectName, M as getUuid, N as isHyperlinkRel, O as encodeXmlAttrValue, P as mediaSlideKey, S as correctShadowOptions, T as el, _ as flattenEmbeddedFaces, a as makeXmlNotesSlideRel, b as genXmlGradientFill, c as genXmlNormAutofit, d as renderTextParagraphsXml, f as clampFontSizeSz, g as FONT_REL_TYPE, h as FONT_DATA_EXTENSION, i as makeXmlNotesSlide, j as getNewRelId, k as encodeXmlEntities, l as genXmlParagraphProperties, m as FONT_DATA_CONTENT_TYPE, n as makeXmlNotesMaster, p as EMBEDDED_FONT_SLOTS, r as makeXmlNotesMasterRel, s as externalHyperlinkRel, t as buildNotesSlideRels, u as groupRunsIntoLines, v as serializeEmbeddedFontLst, w as createColorElement, x as genXmlPatternFill, y as genXmlColorSelection } from "./notes-DNqAggXx.js";
5
- import { A as PRESET_MATERIALS, C as decodeBase64ToBytes, D as LIGHT_RIGS, E as BEVEL_PRESETS, F as checkEnumOrWarn, M as TEXT_HORZ_OVERFLOW, O as LIGHT_RIG_DIRECTIONS, S as bytesToBase64, T as toMediaDataUri, b as getImageSizeFromBase64, k as PRESET_LINE_DASHES, l as OFFICE_REL, n as PML_ROOT_NS, t as OOXML_NS, u as PACKAGE_REL_NS, w as svgMarkupToDataUri, y as fitSrcRectPercents } from "./namespaces-CcKuzeuE.js";
6
- import { t as getSlidesForTableRows } from "./autopage-BYlTzX-l.js";
7
- import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit, v as isFontCollection } from "./fit-TjpNbMru.js";
3
+ import { A as getDuplicateObjectNames, C as createShadowEffectLst, D as voidEl, E as raw, F as validateObjectName, M as getUuid, N as isHyperlinkRel, O as decodeXmlAttrValue, P as mediaSlideKey, S as correctShadowOptions, T as el, _ as flattenEmbeddedFaces, a as makeXmlNotesSlideRel, b as genXmlGradientFill, c as genXmlNormAutofit, d as renderTextParagraphsXml, f as clampFontSizeSz, g as FONT_REL_TYPE, h as FONT_DATA_EXTENSION, i as makeXmlNotesSlide, j as getNewRelId, k as encodeXmlAttrValue, l as genXmlParagraphProperties, m as FONT_DATA_CONTENT_TYPE, n as makeXmlNotesMaster, p as EMBEDDED_FONT_SLOTS, r as makeXmlNotesMasterRel, s as externalHyperlinkRel, t as buildNotesSlideRels, u as groupRunsIntoLines, v as serializeEmbeddedFontLst, w as createColorElement, x as genXmlPatternFill, y as genXmlColorSelection } from "./notes-CJFJaG6M.js";
4
+ import { $ as isChartExType, A as LAYOUT_IDX_SERIES_BASE, C as DEF_PRES_LAYOUT, D as DEF_SLIDE_MARGIN_IN, E as DEF_SHAPE_SHADOW, F as SLDNUMFLDID, G as SchemeColor, H as ChartType, I as SLDNUM_PLACEHOLDER_TEXT, L as XML_DECL, N as PIECHART_COLORS, P as REGEX_HEX_COLOR, Q as connectorPresetFor, S as DEF_FONT_COLOR, W as PlaceholderType, X as VALID_SHAPE_PRESETS, Z as asChartType, _ as BARCHART_COLORS, a as inch2Emu, b as DEF_CHART_BORDER, et as isChartType, f as AXIS_ID_CATEGORY_PRIMARY, g as AXIS_ID_VALUE_SECONDARY, h as AXIS_ID_VALUE_PRIMARY, i as getSmartParseNumber, j as LETTERS, k as DEF_TEXT_SHADOW, l as ptsToEmuLenient, m as AXIS_ID_SERIES_PRIMARY, n as convertArcAngle, o as lineWidthToEmu, p as AXIS_ID_CATEGORY_SECONDARY, r as convertRotationDegrees, s as marginToEmu, t as convertAngleUnits, u as resolveTableColWidthsEmu, v as DEF_CELL_BORDER, w as DEF_PRES_LAYOUT_NAME, x as DEF_CHART_GRIDLINE, y as DEF_CELL_MARGIN_IN } from "./units-internal-Dg95w_YD.js";
5
+ import { A as LIGHT_RIG_DIRECTIONS, C as decodeBase64ToBytes, E as BEVEL_PRESETS, M as PRESET_MATERIALS, O as DEFAULT_COLOR_MAP, P as TEXT_HORZ_OVERFLOW, R as checkEnumOrWarn, S as bytesToBase64, T as toMediaDataUri, b as getImageSizeFromBase64, j as PRESET_LINE_DASHES, k as LIGHT_RIGS, l as OFFICE_REL, n as PML_ROOT_NS, t as OOXML_NS, u as PACKAGE_REL_NS, w as svgMarkupToDataUri, y as fitSrcRectPercents } from "./namespaces-BVnNu7sg.js";
6
+ import { t as getSlidesForTableRows } from "./autopage-DkjYZUFD.js";
7
+ import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit, v as isFontCollection } from "./fit-BKs0zxMI.js";
8
8
  import { ZipWriter } from "./zip.js";
9
9
  //#region src/media/content-type.ts
10
10
  /**
@@ -411,61 +411,49 @@ function createChartTextFonts(typeface) {
411
411
  return voidEl("a:latin", { typeface }) + voidEl("a:ea", { typeface }) + voidEl("a:cs", { typeface });
412
412
  }
413
413
  function genXmlTitle(opts, chartX, chartY) {
414
- const align = opts.titleAlign === "left" || opts.titleAlign === "right" ? `<a:pPr algn="${opts.titleAlign.slice(0, 1)}">` : "<a:pPr>";
415
- const rotate = opts.titleRotate ? `<a:bodyPr rot="${convertAngleUnits(opts.titleRotate, "titleRotate")}"/>` : "<a:bodyPr/>";
416
- const sizeAttr = opts.fontSize ? `sz="${ptToHundredths(opts.fontSize)}"` : "";
417
- const titleBold = opts.titleBold ? 1 : 0;
418
- const titleItalic = opts.titleItalic ? 1 : 0;
419
- const titleUnderline = opts.titleUnderline ? "sng" : "none";
420
- let layout = "<c:layout/>";
414
+ const runAttrs = ` ${opts.fontSize ? `sz="${ptToHundredths(opts.fontSize)}"` : ""} b="${opts.titleBold ? 1 : 0}" i="${opts.titleItalic ? 1 : 0}" u="${opts.titleUnderline ? "sng" : "none"}" strike="noStrike">`;
415
+ const runChildren = "\n " + genXmlColorSelection(opts.color || "000000") + "\n " + createChartTextFonts(opts.fontFace || "Arial") + "\n ";
421
416
  const hasX = opts.titlePos && typeof opts.titlePos.x === "number";
422
417
  const hasY = opts.titlePos && typeof opts.titlePos.y === "number";
418
+ /** Fold a slide-relative offset into the fraction-of-chart value `c:x`/`c:y` want. */
419
+ const edgeFraction = (offset) => {
420
+ let val = offset === 0 ? 0 : offset * (offset / 5) / 10;
421
+ if (val >= 1) val = val / 10;
422
+ if (val >= .1) val = val / 10;
423
+ return val;
424
+ };
425
+ let layout = voidEl("c:layout");
423
426
  if (hasX || hasY) {
424
- let modes = "";
425
- let vals = "";
426
- if (hasX) {
427
- const totalX = (opts.titlePos?.x ?? 0) + (chartX ?? 0);
428
- let valX = totalX === 0 ? 0 : totalX * (totalX / 5) / 10;
429
- if (valX >= 1) valX = valX / 10;
430
- if (valX >= .1) valX = valX / 10;
431
- modes += "<c:xMode val=\"edge\"/>";
432
- vals += `<c:x val="${valX}"/>`;
433
- }
434
- if (hasY) {
435
- const totalY = (opts.titlePos?.y ?? 0) + (chartY ?? 0);
436
- let valY = totalY === 0 ? 0 : totalY * (totalY / 5) / 10;
437
- if (valY >= 1) valY = valY / 10;
438
- if (valY >= .1) valY = valY / 10;
439
- modes += "<c:yMode val=\"edge\"/>";
440
- vals += `<c:y val="${valY}"/>`;
441
- }
442
- layout = `<c:layout><c:manualLayout>${modes}${vals}</c:manualLayout></c:layout>`;
443
- }
444
- return `<c:title>
445
- <c:tx>
446
- <c:rich>
447
- ${rotate}
448
- <a:lstStyle/>
449
- <a:p>
450
- ${align}
451
- <a:defRPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
452
- ${genXmlColorSelection(opts.color || "000000")}
453
- ${createChartTextFonts(opts.fontFace || "Arial")}
454
- </a:defRPr>
455
- </a:pPr>
456
- <a:r>
457
- <a:rPr ${sizeAttr} b="${titleBold}" i="${titleItalic}" u="${titleUnderline}" strike="noStrike">
458
- ${genXmlColorSelection(opts.color || "000000")}
459
- ${createChartTextFonts(opts.fontFace || "Arial")}
460
- </a:rPr>
461
- ${el("a:t", null, opts.title ?? "")}
462
- </a:r>
463
- </a:p>
464
- </c:rich>
465
- </c:tx>
466
- ${layout}
467
- <c:overlay val="0"/>
468
- </c:title>`;
427
+ const modes = (hasX ? voidEl("c:xMode", { val: "edge" }) : "") + (hasY ? voidEl("c:yMode", { val: "edge" }) : "");
428
+ const vals = (hasX ? voidEl("c:x", { val: edgeFraction((opts.titlePos?.x ?? 0) + (chartX ?? 0)) }) : "") + (hasY ? voidEl("c:y", { val: edgeFraction((opts.titlePos?.y ?? 0) + (chartY ?? 0)) }) : "");
429
+ layout = el("c:layout", null, raw(el("c:manualLayout", null, raw(modes + vals))));
430
+ }
431
+ const paragraph = el("a:p", null, [raw(el("a:pPr", opts.titleAlign === "left" || opts.titleAlign === "right" ? { algn: opts.titleAlign.slice(0, 1) } : null, raw("\n <a:defRPr" + runAttrs + runChildren + "</a:defRPr>"), {
432
+ openPrefix: "\n ",
433
+ closePrefix: "\n "
434
+ })), raw(el("a:r", null, [raw("\n <a:rPr" + runAttrs + runChildren + "</a:rPr>"), raw("\n " + el("a:t", null, opts.title ?? ""))], {
435
+ openPrefix: "\n ",
436
+ closePrefix: "\n "
437
+ }))], {
438
+ openPrefix: "\n ",
439
+ closePrefix: "\n "
440
+ });
441
+ const rich = el("c:rich", null, [
442
+ raw(voidEl("a:bodyPr", { rot: opts.titleRotate ? convertAngleUnits(opts.titleRotate, "titleRotate") : void 0 }, { openPrefix: "\n " })),
443
+ raw(voidEl("a:lstStyle", null, { openPrefix: "\n " })),
444
+ raw(paragraph)
445
+ ], {
446
+ openPrefix: "\n ",
447
+ closePrefix: "\n "
448
+ });
449
+ return el("c:title", null, [
450
+ raw(el("c:tx", null, raw(rich), {
451
+ openPrefix: "\n ",
452
+ closePrefix: "\n "
453
+ })),
454
+ raw("\n " + layout),
455
+ raw(voidEl("c:overlay", { val: 0 }, { openPrefix: "\n " }))
456
+ ], { closePrefix: "\n " });
469
457
  }
470
458
  /**
471
459
  * Create Grid Line Element
@@ -501,31 +489,62 @@ function createGridLineElement(glOpts) {
501
489
  * @param leaderLines - emit the trailing `<c:showLeaderLines>` (category-axis plots only)
502
490
  */
503
491
  function chartDataLabels(opts, leaderLines) {
504
- let xml = " <c:dLbls>";
505
- xml += " " + voidEl("c:numFmt", {
506
- formatCode: (opts.dataLabelFormatCode ?? "") || "General",
507
- sourceLinked: 0
492
+ const defRPr = el("a:defRPr", {
493
+ b: opts.dataLabelFontBold ? 1 : 0,
494
+ i: opts.dataLabelFontItalic ? 1 : 0,
495
+ strike: "noStrike",
496
+ sz: ptToHundredths(opts.dataLabelFontSize || 12),
497
+ u: "none"
498
+ }, [raw(genXmlColorSelection(opts.dataLabelColor || "000000")), raw(" " + createChartTextFonts(opts.dataLabelFontFace || "Arial"))], {
499
+ openPrefix: " ",
500
+ closePrefix: " "
501
+ });
502
+ const txPr = el("c:txPr", null, [
503
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
504
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
505
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(defRPr), { closePrefix: " " })), { openPrefix: " " }))
506
+ ], {
507
+ openPrefix: " ",
508
+ closePrefix: " "
509
+ });
510
+ return el("c:dLbls", null, [
511
+ raw(voidEl("c:numFmt", {
512
+ formatCode: (opts.dataLabelFormatCode ?? "") || "General",
513
+ sourceLinked: 0
514
+ }, { openPrefix: " " })),
515
+ raw(txPr),
516
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition }, { openPrefix: " " })) : null,
517
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
518
+ raw(voidEl("c:showVal", { val: opts.showValue ? 1 : 0 }, { openPrefix: " " })),
519
+ raw(voidEl("c:showCatName", { val: 0 }, { openPrefix: " " })),
520
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 }, { openPrefix: " " })),
521
+ raw(voidEl("c:showPercent", { val: 0 }, { openPrefix: " " })),
522
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " })),
523
+ leaderLines ? raw(voidEl("c:showLeaderLines", { val: opts.showLeaderLines ? 1 : 0 }, { openPrefix: " " })) : null
524
+ ], {
525
+ openPrefix: " ",
526
+ closePrefix: " "
508
527
  });
509
- xml += " <c:txPr>";
510
- xml += " <a:bodyPr/>";
511
- xml += " <a:lstStyle/>";
512
- xml += " <a:p><a:pPr>";
513
- xml += ` <a:defRPr b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" strike="noStrike" sz="${ptToHundredths(opts.dataLabelFontSize || 12)}" u="none">`;
514
- xml += genXmlColorSelection(opts.dataLabelColor || "000000");
515
- xml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
516
- xml += " </a:defRPr>";
517
- xml += " </a:pPr></a:p>";
518
- xml += " </c:txPr>";
519
- if (opts.dataLabelPosition) xml += " <c:dLblPos val=\"" + opts.dataLabelPosition + "\"/>";
520
- xml += " <c:showLegendKey val=\"0\"/>";
521
- xml += " <c:showVal val=\"" + (opts.showValue ? "1" : "0") + "\"/>";
522
- xml += " <c:showCatName val=\"0\"/>";
523
- xml += " <c:showSerName val=\"" + (opts.showSerName ? "1" : "0") + "\"/>";
524
- xml += " <c:showPercent val=\"0\"/>";
525
- xml += " <c:showBubbleSize val=\"0\"/>";
526
- if (leaderLines) xml += ` <c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
527
- xml += " </c:dLbls>";
528
- return xml;
528
+ }
529
+ /**
530
+ * An `<a:solidFill>` of the theme text colour dimmed towards the background — the grey the chart
531
+ * furniture (stock hi-low lines and up/down bars, the data-table grid) is drawn in.
532
+ * `lumMod`/`lumOff` are the two halves of one dimming: how much of the luminance survives, and
533
+ * how much white is mixed back in.
534
+ * @param lumMod - surviving luminance, in thousandths of a percent
535
+ * @param lumOff - luminance added back, in thousandths of a percent
536
+ */
537
+ function dimmedTextFill(lumMod, lumOff) {
538
+ return el("a:solidFill", null, raw(el("a:schemeClr", { val: "tx1" }, [raw(voidEl("a:lumMod", { val: lumMod })), raw(voidEl("a:lumOff", { val: lumOff }))])));
539
+ }
540
+ /** The hairline outline drawn in {@link dimmedTextFill}, at the same three dimming levels. */
541
+ function dimmedTextLine(lumMod, lumOff, fmt) {
542
+ return el("a:ln", {
543
+ w: 9525,
544
+ cap: "flat",
545
+ cmpd: "sng",
546
+ algn: "ctr"
547
+ }, [raw(dimmedTextFill(lumMod, lumOff)), raw(voidEl("a:round"))], fmt);
529
548
  }
530
549
  /**
531
550
  * Build a `<c:pt>` numeric-cache data point, or '' to leave a gap.
@@ -545,7 +564,7 @@ function numCachePt(idx, value) {
545
564
  warn("chart/non-finite-value", `chart value "${value}" at index ${idx} is not a finite number; data point omitted.`);
546
565
  return "";
547
566
  }
548
- return `<c:pt idx="${idx}"><c:v>${value}</c:v></c:pt>`;
567
+ return el("c:pt", { idx }, raw(el("c:v", null, value)));
549
568
  }
550
569
  /**
551
570
  * Build the error-bar elements (`<c:errBars>`) for a single series.
@@ -568,29 +587,19 @@ function makeChartErrorBarsXml(chartType, errorBars, obj) {
568
587
  const valueType = eb.valueType || "fixedVal";
569
588
  const barType = eb.barType || "both";
570
589
  const direction = eb.direction || "y";
571
- strXml += "<c:errBars>";
572
- strXml += `<c:errDir val="${direction}"/>`;
573
- strXml += `<c:errBarType val="${barType}"/>`;
574
- strXml += `<c:errValType val="${valueType}"/>`;
575
- strXml += `<c:noEndCap val="${eb.noEndCap ? "1" : "0"}"/>`;
590
+ let children = voidEl("c:errDir", { val: direction }) + voidEl("c:errBarType", { val: barType }) + voidEl("c:errValType", { val: valueType }) + voidEl("c:noEndCap", { val: eb.noEndCap ? 1 : 0 });
576
591
  if (valueType === "cust") {
577
592
  if (barType !== "minus") {
578
593
  if (!eb.plusValues?.length) warn("chart/error-bars-missing-values", `chart series "${obj.name}" errorBars valueType 'cust' needs \`plusValues\` for barType '${barType}'.`);
579
- strXml += makeErrBarNumLit("plus", eb.plusValues || []);
594
+ children += makeErrBarNumLit("plus", eb.plusValues || []);
580
595
  }
581
596
  if (barType !== "plus") {
582
597
  if (!eb.minusValues?.length) warn("chart/error-bars-missing-values", `chart series "${obj.name}" errorBars valueType 'cust' needs \`minusValues\` for barType '${barType}'.`);
583
- strXml += makeErrBarNumLit("minus", eb.minusValues || []);
598
+ children += makeErrBarNumLit("minus", eb.minusValues || []);
584
599
  }
585
- } else if (valueType !== "stdErr") strXml += `<c:val val="${eb.value ?? 1}"/>`;
586
- if (eb.color || eb.size != null) {
587
- strXml += "<c:spPr><a:ln";
588
- strXml += eb.size != null ? ` w="${ptsToEmuLenient(eb.size)}"` : "";
589
- strXml += ">";
590
- strXml += eb.color ? genXmlColorSelection(eb.color) : "";
591
- strXml += "</a:ln></c:spPr>";
592
- }
593
- strXml += "</c:errBars>";
600
+ } else if (valueType !== "stdErr") children += voidEl("c:val", { val: eb.value ?? 1 });
601
+ if (eb.color || eb.size != null) children += el("c:spPr", null, raw(el("a:ln", { w: eb.size != null ? ptsToEmuLenient(eb.size) : void 0 }, raw(eb.color ? genXmlColorSelection(eb.color) : ""))));
602
+ strXml += el("c:errBars", null, raw(children));
594
603
  });
595
604
  return strXml;
596
605
  }
@@ -600,12 +609,12 @@ function makeChartErrorBarsXml(chartType, errorBars, obj) {
600
609
  * @param values - per-point magnitudes (index-aligned with the series values)
601
610
  */
602
611
  function makeErrBarNumLit(tag, values) {
603
- let strXml = `<c:${tag}><c:numLit><c:formatCode>General</c:formatCode><c:ptCount val="${values.length}"/>`;
604
- values.forEach((value, idx) => {
605
- strXml += numCachePt(idx, value);
606
- });
607
- strXml += `</c:numLit></c:${tag}>`;
608
- return strXml;
612
+ const numLit = el("c:numLit", null, [
613
+ raw(el("c:formatCode", null, "General")),
614
+ raw(voidEl("c:ptCount", { val: values.length })),
615
+ raw(values.map((value, idx) => numCachePt(idx, value)).join(""))
616
+ ]);
617
+ return el(`c:${tag}`, null, raw(numLit));
609
618
  }
610
619
  /**
611
620
  * Build a `<c:serLines>` ("Series Lines") element for a bar chart.
@@ -614,7 +623,7 @@ function makeErrBarNumLit(tag, values) {
614
623
  */
615
624
  function createSerLinesElement(opt) {
616
625
  if (!opt) return "";
617
- if (opt === true) return "<c:serLines/>";
626
+ if (opt === true) return voidEl("c:serLines");
618
627
  if (opt.style === "none") return "";
619
628
  const line = el("a:ln", {
620
629
  w: ptsToEmuLenient(opt.size || DEF_CHART_GRIDLINE.size || 1),
@@ -640,7 +649,15 @@ function createLeaderLinesElement(opts) {
640
649
  if (!opts.leaderLineColor && opts.leaderLineSize == null) return "";
641
650
  const w = ptsToEmuLenient(opts.leaderLineSize ?? .75);
642
651
  const color = opts.leaderLineColor || "808080";
643
- return `<c:leaderLines><c:spPr><a:ln w="${w}" cap="flat">${genXmlColorSelection(color)}<a:prstDash val="solid"/><a:round/></a:ln><a:effectLst/></c:spPr></c:leaderLines>`;
652
+ const line = el("a:ln", {
653
+ w,
654
+ cap: "flat"
655
+ }, [
656
+ raw(genXmlColorSelection(color)),
657
+ raw(voidEl("a:prstDash", { val: "solid" })),
658
+ raw(voidEl("a:round"))
659
+ ]);
660
+ return el("c:leaderLines", null, raw(el("c:spPr", null, [raw(line), raw(voidEl("a:effectLst"))])));
644
661
  }
645
662
  /**
646
663
  * Build a single custom `<c:dLbl>` (rich-text data label) overriding one data point's label.
@@ -656,24 +673,57 @@ function createLeaderLinesElement(opts) {
656
673
  */
657
674
  function makeCustomDLblXml(idx, text, opts) {
658
675
  const sz = ptToHundredths(opts.dataLabelFontSize || 12);
659
- const bold = opts.dataLabelFontBold ? "1" : "0";
660
- const italic = opts.dataLabelFontItalic ? "1" : "0";
676
+ const bold = opts.dataLabelFontBold ? 1 : 0;
677
+ const italic = opts.dataLabelFontItalic ? 1 : 0;
661
678
  const color = createColorElement(opts.dataLabelColor || "000000");
662
679
  const face = opts.dataLabelFontFace || "Arial";
663
680
  const lang = opts.lang || "en-US";
664
- return `<c:dLbl><c:idx val="${idx}"/><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:pPr><a:defRPr sz="${sz}" b="${bold}" i="${italic}" u="none" strike="noStrike"><a:solidFill>${color}</a:solidFill>${createChartTextFonts(face)}</a:defRPr></a:pPr><a:r><a:rPr lang="${lang}" sz="${sz}" b="${bold}" i="${italic}" u="none" strike="noStrike" dirty="0"><a:solidFill>${color}</a:solidFill>${createChartTextFonts(face)}</a:rPr>` + el("a:t", null, text) + "</a:r></a:p></c:rich></c:tx><c:showLegendKey val=\"0\"/><c:showVal val=\"0\"/><c:showCatName val=\"0\"/><c:showSerName val=\"0\"/><c:showPercent val=\"0\"/><c:showBubbleSize val=\"0\"/></c:dLbl>";
681
+ const runChildren = [raw(el("a:solidFill", null, raw(color))), raw(createChartTextFonts(face))];
682
+ const fontAttrs = {
683
+ sz,
684
+ b: bold,
685
+ i: italic,
686
+ u: "none",
687
+ strike: "noStrike"
688
+ };
689
+ const paragraph = el("a:p", null, [raw(el("a:pPr", null, raw(el("a:defRPr", fontAttrs, runChildren)))), raw(el("a:r", null, [raw(el("a:rPr", {
690
+ lang,
691
+ ...fontAttrs,
692
+ dirty: 0
693
+ }, runChildren)), raw(el("a:t", null, text))]))]);
694
+ const rich = el("c:rich", null, [
695
+ raw(voidEl("a:bodyPr")),
696
+ raw(voidEl("a:lstStyle")),
697
+ raw(paragraph)
698
+ ]);
699
+ return el("c:dLbl", null, [
700
+ raw(voidEl("c:idx", { val: idx })),
701
+ raw(el("c:tx", null, raw(rich))),
702
+ raw(voidEl("c:showLegendKey", { val: 0 })),
703
+ raw(voidEl("c:showVal", { val: 0 })),
704
+ raw(voidEl("c:showCatName", { val: 0 })),
705
+ raw(voidEl("c:showSerName", { val: 0 })),
706
+ raw(voidEl("c:showPercent", { val: 0 })),
707
+ raw(voidEl("c:showBubbleSize", { val: 0 }))
708
+ ]);
665
709
  }
666
710
  /**
667
711
  * Build an `<a:ln>` border element from a per-data-point `BorderProps`.
668
712
  * @param border - point border style (`type`, `color`, `pt`)
669
713
  */
670
714
  function createChartBorderLine(border) {
671
- if (border.type === "none") return "<a:ln><a:noFill/></a:ln>";
672
- const dash = border.type === "dash" ? "dash" : "solid";
673
- return `<a:ln w="${ptsToEmuLenient(resolveBorderWidth(border, 1))}" cap="flat">${genXmlColorSelection({
674
- color: border.color || "666666",
675
- transparency: border.transparency
676
- })}<a:prstDash val="${dash}"/><a:round/></a:ln>`;
715
+ if (border.type === "none") return el("a:ln", null, raw(voidEl("a:noFill")));
716
+ return el("a:ln", {
717
+ w: ptsToEmuLenient(resolveBorderWidth(border, 1)),
718
+ cap: "flat"
719
+ }, [
720
+ raw(genXmlColorSelection({
721
+ color: border.color || "666666",
722
+ transparency: border.transparency
723
+ })),
724
+ raw(voidEl("a:prstDash", { val: border.type === "dash" ? "dash" : "solid" })),
725
+ raw(voidEl("a:round"))
726
+ ]);
677
727
  }
678
728
  /**
679
729
  * Build `<c:dPt>` entries for a series in the bar/line/area/scatter loops.
@@ -705,30 +755,89 @@ function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
705
755
  const pattern = ptStyle?.pattern;
706
756
  const border = ptStyle?.border;
707
757
  if (!fillColor && !pattern && !border) return;
708
- xml += "<c:dPt>";
709
- xml += `<c:idx val="${index}"/>`;
710
- if (isBar) xml += "<c:invertIfNegative val=\"0\"/>";
711
- xml += "<c:bubble3D val=\"0\"/>";
712
- xml += "<c:spPr>";
713
- if ((isBar || isScatter) && opts.lineSize === 0 && !border && !ptStyle?.fill && !pattern) xml += "<a:ln><a:noFill/></a:ln>";
758
+ let shape = "";
759
+ if ((isBar || isScatter) && opts.lineSize === 0 && !border && !ptStyle?.fill && !pattern) shape = el("a:ln", null, raw(voidEl("a:noFill")));
714
760
  else {
715
- if (pattern) xml += genXmlPatternFill(fillColor && !pattern.fgColor ? {
761
+ if (pattern) shape = genXmlPatternFill(fillColor && !pattern.fgColor ? {
716
762
  ...pattern,
717
763
  fgColor: fillColor
718
764
  } : pattern);
719
- else if (fillColor) {
720
- if (chartType === "bar3D") xml += `<a:ln>${genXmlColorSelection(fillColor)}</a:ln>`;
721
- else xml += genXmlColorSelection(fillColor);
722
- }
723
- if (border) xml += createChartBorderLine(border);
765
+ else if (fillColor) shape = chartType === "bar3D" ? el("a:ln", null, raw(genXmlColorSelection(fillColor))) : genXmlColorSelection(fillColor);
766
+ if (border) shape += createChartBorderLine(border);
724
767
  }
725
- xml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
726
- xml += "</c:spPr>";
727
- xml += "</c:dPt>";
768
+ xml += el("c:dPt", null, [
769
+ raw(voidEl("c:idx", { val: index })),
770
+ isBar ? raw(voidEl("c:invertIfNegative", { val: 0 })) : null,
771
+ raw(voidEl("c:bubble3D", { val: 0 })),
772
+ raw(el("c:spPr", null, [raw(shape), raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))]))
773
+ ]);
728
774
  });
729
775
  return xml;
730
776
  }
731
777
  /**
778
+ * A `<c:tx>` series-name block: the `<c:f>` formula plus the one-point `<c:strCache>` mirroring
779
+ * the header cell it points at. The string counterpart of {@link numRefBlock}.
780
+ *
781
+ * Built through `el()`/`voidEl()` rather than by concatenation, with the indentation carried on
782
+ * the `openPrefix`/`childPrefix`/`closePrefix` byte-layout hooks those helpers already have. The
783
+ * five copies this replaces were all hand-built strings, so routing them here takes fifty-odd
784
+ * hand-written delimiters out of `src/gen/chart/` rather than moving them into one file.
785
+ * @param ref - the `<c:f>` formula, from {@link sheetCellRef} or written inline
786
+ * @param name - the series name to cache; escaped on the way into `<c:v>`
787
+ * @param layout - which of the three historical whitespace spellings to emit; see {@link StrRefLayout}
788
+ */
789
+ function strRefBlock(ref, name, layout = "indented") {
790
+ const compact = layout === "compact";
791
+ const pt = el("c:pt", { idx: 0 }, raw(el("c:v", null, name)));
792
+ const cacheChildren = [raw(voidEl("c:ptCount", { val: 1 })), raw(pt)];
793
+ const strCache = layout === "expanded" ? el("c:strCache", null, cacheChildren, {
794
+ childPrefix: " ",
795
+ closePrefix: " "
796
+ }) : el("c:strCache", null, cacheChildren);
797
+ const indent = (spaces) => compact ? "" : spaces;
798
+ const strRef = el("c:strRef", null, [raw(el("c:f", null, ref)), raw(strCache)], {
799
+ openPrefix: indent(" "),
800
+ childPrefix: indent(" "),
801
+ closePrefix: indent(" ")
802
+ });
803
+ return el("c:tx", null, raw(strRef), {
804
+ openPrefix: indent(" "),
805
+ closePrefix: indent(" ")
806
+ });
807
+ }
808
+ /**
809
+ * A `<c:cat>` category reference: the `<c:f>` formula plus the `<c:numCache>`/`<c:strCache>` that
810
+ * mirrors the label cells it points at.
811
+ *
812
+ * `kind` picks the cache: `'num'` for numeric categories — dates, which need a `<c:formatCode>` so
813
+ * PowerPoint renders them as dates rather than serial numbers — and `'str'` for text ones. Stock
814
+ * and cat-axis each carried both arms of that choice, and surface carried the string one, five
815
+ * copies of the same `<c:f>` + `<c:ptCount>` + `<c:pt idx>` loop.
816
+ *
817
+ * `layout` preserves which whitespace spelling each site emitted, for the reason
818
+ * {@link StrRefLayout} gives. The `<c:multiLvlStrRef>` arm of a cat-axis is deliberately not
819
+ * folded in: multi-level categories nest a `<c:lvl>` per label group, which is a different shape
820
+ * rather than a different spelling.
821
+ * @param kind - `num` for a numeric (date) category cache, `str` for a text one
822
+ * @param ref - the `<c:f>` formula
823
+ * @param labels - the category labels, in order; escaped on the way into `<c:v>`
824
+ * @param formatCode - the cached `<c:formatCode>`; numeric caches only
825
+ * @param layout - which whitespace spelling to emit
826
+ */
827
+ function catRefBlock(kind, ref, labels, formatCode, layout = "compact") {
828
+ const indent = (spaces) => layout === "indented" ? spaces : "";
829
+ const points = labels.map((label, idx) => el("c:pt", { idx }, raw(el("c:v", null, label)))).join("");
830
+ const cache = el(`c:${kind}Cache`, null, [formatCode === void 0 ? null : raw(el("c:formatCode", null, formatCode)), raw(voidEl("c:ptCount", { val: labels.length }) + points)], {
831
+ childPrefix: indent(" "),
832
+ closePrefix: indent(" ")
833
+ });
834
+ return el(`c:${kind}Ref`, null, [raw(el("c:f", null, ref)), raw(cache)], {
835
+ openPrefix: indent(" "),
836
+ childPrefix: indent(" "),
837
+ closePrefix: indent(" ")
838
+ });
839
+ }
840
+ /**
732
841
  * A `<c:xVal>`/`<c:yVal>` numeric-reference block: the `<c:f>` formula plus the `<c:numCache>`
733
842
  * that mirrors the cells it points at.
734
843
  *
@@ -748,19 +857,19 @@ function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
748
857
  * change wearing a cleanup's clothes, which the byte-identity gate exists to refuse.
749
858
  */
750
859
  function numRefBlock(tag, ref, formatCode, values, refIndent = " ") {
751
- let xml = `<${tag}>`;
752
- xml += " <c:numRef>";
753
- xml += `${refIndent}<c:f>${ref}</c:f>`;
754
- xml += " <c:numCache>";
755
- xml += " <c:formatCode>" + formatCode + "</c:formatCode>";
756
- xml += ` <c:ptCount val="${values.length}"/>`;
757
- values.forEach((value, idx) => {
758
- xml += numCachePt(idx, value);
759
- });
760
- xml += " </c:numCache>";
761
- xml += " </c:numRef>";
762
- xml += `</${tag}>`;
763
- return xml;
860
+ const numCache = el("c:numCache", null, [
861
+ raw(el("c:formatCode", null, formatCode, { openPrefix: " " })),
862
+ raw(voidEl("c:ptCount", { val: values.length }, { openPrefix: " " })),
863
+ raw(values.map((value, idx) => numCachePt(idx, value)).join(""))
864
+ ], {
865
+ openPrefix: " ",
866
+ closePrefix: " "
867
+ });
868
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, ref, { openPrefix: refIndent })), raw(numCache)], {
869
+ openPrefix: " ",
870
+ closePrefix: " "
871
+ });
872
+ return el(tag, null, raw(numRef));
764
873
  }
765
874
  //#endregion
766
875
  //#region src/gen/define/chart.ts
@@ -1427,6 +1536,42 @@ function addConnectorDefinition(target, opts) {
1427
1536
  target._slideObjects.push(newObject);
1428
1537
  }
1429
1538
  //#endregion
1539
+ //#region src/gen/define/image-rel.ts
1540
+ /**
1541
+ * Push an image media rel onto `target`, reusing an identical source's package part.
1542
+ *
1543
+ * De-dup is per target and by source, not by bytes: file-path images are matched on `path`,
1544
+ * while base64 `data` images have no real path — they all share the `preencoded.<extn>`
1545
+ * placeholder — so they are matched on their payload instead, which is what stops the same
1546
+ * inline image being embedded once per use. A rel already marked `isDuplicate` is never
1547
+ * matched against, so every duplicate points at the one original rather than at a chain.
1548
+ *
1549
+ * This is a *slide-local* optimization. `package/assemble.ts` runs a second, deck-wide collapse
1550
+ * keyed on extension + bytes once every rel's data is loaded, which subsumes this one for reuse
1551
+ * across slides and for sources that only turn out identical after loading.
1552
+ * @param target - slide (or layout/master) the rel is registered on
1553
+ * @param source - the resolved image source: a `path`, a base64 `data` payload, or both
1554
+ * @param relId - the relationship id already allocated for this use
1555
+ */
1556
+ function registerImageMediaRel(target, source, relId) {
1557
+ const path = source.path || "";
1558
+ const data = source.data || "";
1559
+ const type = imageContentType(source.extn);
1560
+ const dupe = target._relsMedia.find((item) => {
1561
+ if (item.isDuplicate || !item.Target || item.type !== type) return false;
1562
+ return path ? item.path === path : !!data && item.data === data;
1563
+ });
1564
+ target._relsMedia.push({
1565
+ path: path || "preencoded." + source.extn,
1566
+ type,
1567
+ extn: source.extn,
1568
+ data,
1569
+ rId: relId,
1570
+ isDuplicate: !!dupe?.Target,
1571
+ Target: dupe?.Target ? dupe.Target : `../media/image-${mediaSlideKey(target)}-${target._relsMedia.length + 1}.${source.extn}`
1572
+ });
1573
+ }
1574
+ //#endregion
1430
1575
  //#region src/gen/define/image.ts
1431
1576
  /** DPI PowerPoint assumes when sizing an inserted raster image (natural pixels / 96 == inches) */
1432
1577
  const IMAGE_NATURAL_DPI = 96;
@@ -1459,21 +1604,11 @@ function registerImageFillMedia(target, fill) {
1459
1604
  return;
1460
1605
  }
1461
1606
  const imageRelId = getNewRelId(target);
1462
- const mediaKey = mediaSlideKey(target);
1463
- const imgContentType = imageContentType(strImgExtn);
1464
- const dupeItem = target._relsMedia.find((item) => {
1465
- if (item.isDuplicate || !item.Target || item.type !== imgContentType) return false;
1466
- return strImagePath ? item.path === strImagePath : !!strImageData && item.data === strImageData;
1467
- });
1468
- target._relsMedia.push({
1469
- path: strImagePath || "preencoded." + strImgExtn,
1470
- type: imgContentType,
1471
- extn: strImgExtn,
1472
- data: strImageData || "",
1473
- rId: imageRelId,
1474
- isDuplicate: !!dupeItem?.Target,
1475
- Target: dupeItem?.Target ? dupeItem.Target : `../media/image-${mediaKey}-${target._relsMedia.length + 1}.${strImgExtn}`
1476
- });
1607
+ registerImageMediaRel(target, {
1608
+ path: strImagePath,
1609
+ data: strImageData,
1610
+ extn: strImgExtn
1611
+ }, imageRelId);
1477
1612
  fill.type = "image";
1478
1613
  fill._imgRid = imageRelId;
1479
1614
  }
@@ -1599,20 +1734,11 @@ function addImageDefinition(target, opt) {
1599
1734
  });
1600
1735
  newObject.imageRid = imageRelId + 1;
1601
1736
  } else {
1602
- const imgContentType = imageContentType(strImgExtn);
1603
- const dupeItem = target._relsMedia.find((item) => {
1604
- if (item.isDuplicate || !item.Target || item.type !== imgContentType) return false;
1605
- return strImagePath ? item.path === strImagePath : !!strImageData && item.data === strImageData;
1606
- });
1607
- target._relsMedia.push({
1608
- path: strImagePath || "preencoded." + strImgExtn,
1609
- type: imgContentType,
1610
- extn: strImgExtn,
1611
- data: strImageData || "",
1612
- rId: imageRelId,
1613
- isDuplicate: !!dupeItem?.Target,
1614
- Target: dupeItem?.Target ? dupeItem.Target : `../media/image-${mediaKey}-${target._relsMedia.length + 1}.${strImgExtn}`
1615
- });
1737
+ registerImageMediaRel(target, {
1738
+ path: strImagePath,
1739
+ data: strImageData,
1740
+ extn: strImgExtn
1741
+ }, imageRelId);
1616
1742
  newObject.imageRid = imageRelId;
1617
1743
  }
1618
1744
  if (typeof objHyperlink === "object") {
@@ -1957,7 +2083,7 @@ const SHAPE_NAME_ALIASES = {
1957
2083
  * @param {ShapeProps} opts shape options
1958
2084
  */
1959
2085
  function addShapeDefinition(target, shapeName, opts) {
1960
- const options = typeof opts === "object" ? opts : {};
2086
+ const options = typeof opts === "object" ? { ...opts } : {};
1961
2087
  options.line = options.line || { type: "none" };
1962
2088
  options.shadow = correctShadowOptions(options.shadow);
1963
2089
  const resolvedShapeName = typeof shapeName === "string" && SHAPE_NAME_ALIASES[shapeName] ? SHAPE_NAME_ALIASES[shapeName] : shapeName;
@@ -1998,8 +2124,21 @@ function addShapeDefinition(target, shapeName, opts) {
1998
2124
  * @param {boolean} isPlaceholder whether this a placeholder object
1999
2125
  */
2000
2126
  function addTextDefinition(target, text, opts, isPlaceholder) {
2001
- const textObjects = !text || text.length === 0 ? [{ text: "" }] : text;
2002
- const objectOptions = opts || {};
2127
+ const owned = /* @__PURE__ */ new Map();
2128
+ /** Copy a caller-supplied options object once, returning the same copy for the same input. */
2129
+ const own = (source) => {
2130
+ if (!source) return {};
2131
+ const already = owned.get(source);
2132
+ if (already) return already;
2133
+ const copy = { ...source };
2134
+ owned.set(source, copy);
2135
+ return copy;
2136
+ };
2137
+ const textObjects = (!text || text.length === 0 ? [{ text: "" }] : text).map((item) => ({
2138
+ ...item,
2139
+ options: own(item.options)
2140
+ }));
2141
+ const objectOptions = own(opts);
2003
2142
  const newObject = {
2004
2143
  _type: isPlaceholder ? "placeholder" : "text",
2005
2144
  shape: opts.shape || "rect",
@@ -2032,7 +2171,7 @@ function addTextDefinition(target, text, opts, isPlaceholder) {
2032
2171
  itemOpts.line = itemOpts.line || {};
2033
2172
  itemOpts.lineSpacing = itemOpts.lineSpacing && !isNaN(itemOpts.lineSpacing) ? itemOpts.lineSpacing : void 0;
2034
2173
  itemOpts.lineSpacingMultiple = itemOpts.lineSpacingMultiple && !isNaN(itemOpts.lineSpacingMultiple) ? itemOpts.lineSpacingMultiple : void 0;
2035
- itemOpts._bodyProp = itemOpts._bodyProp || {};
2174
+ itemOpts._bodyProp = { ...itemOpts._bodyProp };
2036
2175
  itemOpts._bodyProp.anchor = !itemOpts.placeholder ? "ctr" : void 0;
2037
2176
  itemOpts._bodyProp.vert = itemOpts.textDirection ?? itemOpts.vert;
2038
2177
  itemOpts._bodyProp.wrap = typeof itemOpts.wrap === "boolean" ? itemOpts.wrap : true;
@@ -2248,6 +2387,20 @@ const GROUPABLE_TYPES = [
2248
2387
  "group"
2249
2388
  ];
2250
2389
  /**
2390
+ * Whether `groupObjects()` would accept this object, given only what it is. The remaining reasons a
2391
+ * call can still fail are about the *selection* rather than the object — an unresolved, duplicated
2392
+ * or ambiguous name — so a consumer cannot decide those from one object either way.
2393
+ *
2394
+ * Exported because `Slide.objects` reports it as `canGroup`, and a consumer that had to re-state the
2395
+ * rule would be pinning a copy of the list above: it would keep refusing the day grouping learns a
2396
+ * new kind, and go on offering the day one is withdrawn, with nothing failing in either direction.
2397
+ * @param {SlideObject} obj - a top-level authored object
2398
+ * @returns {boolean} true when its kind is groupable and it is not a placeholder
2399
+ */
2400
+ function isGroupableObject(obj) {
2401
+ return GROUPABLE_TYPES.includes(obj._type) && !obj.options?.placeholder;
2402
+ }
2403
+ /**
2251
2404
  * Depth-first search for an object whose *stored* name is `key`, among group children. Used only to
2252
2405
  * explain a failed lookup, so it takes the same attribute-escaped key the caller already compared
2253
2406
  * against `_slideObjects` (see `groupObjectsDefinition`) rather than escaping again per call.
@@ -2295,7 +2448,7 @@ function groupObjectsDefinition(target, objectNames, opts) {
2295
2448
  throw new InvalidOptionError("group/unresolved-object-name", `groupObjects(): cannot group "${name}" — ${hint}.`);
2296
2449
  }
2297
2450
  if (ambiguous) throw new InvalidOptionError("group/ambiguous-object-name", `groupObjects(): objectName "${name}" is ambiguous — ${matches.length} objects on this slide share it. Give them unique objectNames.`);
2298
- if (!GROUPABLE_TYPES.includes(obj._type) || obj.options?.placeholder) {
2451
+ if (!isGroupableObject(obj)) {
2299
2452
  const kind = obj.options?.placeholder ? "placeholder" : obj._type;
2300
2453
  throw new UnsupportedFeatureError("group/kind-not-groupable", `groupObjects(): cannot group "${name}" — grouping a ${String(kind)} is not supported yet.`);
2301
2454
  }
@@ -2310,6 +2463,26 @@ function groupObjectsDefinition(target, objectNames, opts) {
2310
2463
  target._slideObjects.splice(0, target._slideObjects.length, ...regrouped);
2311
2464
  }
2312
2465
  //#endregion
2466
+ //#region src/media/placeholders.ts
2467
+ /**
2468
+ * The three base64 placeholder images the library falls back to.
2469
+ *
2470
+ * Their own module because of where the bundler puts them, not because they group naturally.
2471
+ * The chunker splits by module, so a constant is pulled into every chunk that reaches the module
2472
+ * declaring it — and these lived in `constants-internal.ts` beside `XML_DECL`, `DEF_FONT_SIZE`
2473
+ * and the rest of the vocabulary all of `src/gen/` imports. The result was that `IMG_PLAYBTN`,
2474
+ * a 74 KB PNG wanted by exactly one module, landed in two chunks and cost 149 KB of the shipped
2475
+ * package, 13% of the largest chunk. A caller who only ever wrote text boxes paid for the video
2476
+ * poster.
2477
+ *
2478
+ * So the rule this module encodes: a payload measured in tens of kilobytes does not share a
2479
+ * module with vocabulary, however tidy that grouping reads.
2480
+ */
2481
+ /** Shown in place of media that failed to load, under `onMediaError: 'placeholder'`. */
2482
+ const IMG_BROKEN = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAB3CAYAAAD1oOVhAAAGAUlEQVR4Xu2dT0xcRRzHf7tAYSsc0EBSIq2xEg8mtTGebVzEqOVIolz0siRE4gGTStqKwdpWsXoyGhMuyAVJOHBgqyvLNgonDkabeCBYW/8kTUr0wsJC+Wfm0bfuvn37Znbem9mR9303mJnf/Pb7ed95M7PDI5JIJPYJV5EC7e3t1N/fT62trdqViQCIu+bVgpIHEo/Hqbe3V/sdYVKHyWSSZmZm8ilVA0oeyNjYmEnaVC2Xvr6+qg5fAOJAz4DU1dURGzFSqZRVqtMpAFIGyMjICC0vL9PExIRWKADiAYTNshYWFrRCARAOEFZcCKWtrY0GBgaUTYkBRACIE4rKZwqACALR5RQAqQCIDqcASIVAVDsFQCSAqHQKgEgCUeUUAPEBRIVTAMQnEBvK5OQkbW9vk991CoAEAMQJxc86BUACAhKUUwAkQCBBOAVAAgbi1ykAogCIH6cAiCIgsk4BEIVAZJwCIIqBVLqiBxANQFgXS0tLND4+zl08AogmIG5OSSQS1gGKwgtANAIRcQqAaAbCe6YASBWA2E6xDyeyDUl7+AKQMkDYYevm5mZHabA/Li4uUiaTsYLau8QA4gLE/hU7wajyYtv1hReDAiAOxQcHBymbzark4BkbQKom/X8dp9Npmpqasn4BIAYAYSnYp+4BBEAMUcCwNOCQsAKZnp62NtQOw8WmwT09PUo+ijaHsOMx7GppaaH6+nolH0Z10K2tLVpdXbW6UfV3mNqBdHd3U1NTk2rtlMRfW1uj2dlZAFGirkRQAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAGHqrm8caPzQ0WC1logbeiC7X3xJm0PvUmRzh45cuki1588FAmVn9BO6P3yF9utrqGH0MtW82S8UN9RA9v/4k7InjhcJFTs/TLVXLwmJV67S7vD7tHF5pKi46fYdosdOcOOGG8j1OcqefbFEJD9Q3GCwDhqT31HklS4A8VRgfYM2Op6k3bt/BQJl58J7lPvwg5JYNccepaMry0LPqFA7hCm39+NNyp2J0172b19QysGINj5CsRtpij57musOViH0QPJQXn6J9u7dlYJSFkbrMYolrwvDAJAC+WWdEpQz7FTgECeUCpzi6YxvvqXoM6eEhqnCSgDikEzUKUE7Aw7xuHctKB5OYU3dZlNR9syQdAaAcAYTC0pXF+39c09o2Ik+3EqxVKqiB7hbYAxZkk4pbBaEM+AQofv+wTrFwylBOQNABIGwavdfe4O2pg5elO+86l99nY58/VUF0byrYsjiSFluNlXYrOHcBar7+EogUADEQ0YRGHbzoKAASBkg2+9cpM1rV0tK2QOcXW7bLEFAARAXIF4w2DrDWoeUWaf4hQIgDiA8GPZ2iNfi0Q8UACkAIgrDbrJ385eDxaPLLrEsFAB5oG6lMPJQPLZZZKAACBGVhcG2Q+bmuLu2nk55e4jqPv1IeEoceiBeX7s2zCa5MAqdstl91vfXwaEGsv/rb5TtOFk6tWXOuJGh6KmnhO9sayrMninPx103JBtXblHkice58cINZP4Hyr5wpkgkdiChEmc4FWazLzenNKa/p0jncwDiqcD6BuWePk07t1asatZGoYQzSqA4nFJ7soNiP/+EUyfc25GI2GG53dHPrKo1g/1Cw4pIXLrzO+1c+/wg7tBbFDle/EbQcjFCPWQJCau5EoBoFpzXHYDwFNJcDiCaBed1ByA8hTSXA4hmwXndAQhPIc3lAKJZcF53AMJTSHM5gGgWnNcdgPAU0lwOIJoF53UHIDyFNJcfSiCdnZ0Ui8U0SxlMd7lcjubn561gh+Y1scFIU/0o/3sgeLO12E2k7UXKYumgFoAYdg8ACIAYpoBh6cAhAGKYAoalA4cAiGEKGJYOHAIghilgWDpwCIAYpoBh6cAhAGKYAoalA4cAiGEKGJYOHAIghilgWDpwCIAYpoBh6ZQ4JB6PKzviYthnNy4d9h+1M5mMlVckkUjsG5dhiBMCEMPg/wuOfrZZ/RSywQAAAABJRU5ErkJggg==";
2483
+ /** 1x1 transparent PNG, standing in for an SVG the runtime could not rasterize. */
2484
+ const IMG_SVG_PLACEHOLDER = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
2485
+ //#endregion
2313
2486
  //#region src/gen/define/media.ts
2314
2487
  /**
2315
2488
  * Adds a media object to a slide definition.
@@ -2436,21 +2609,11 @@ function registerPreviewImage(target, cover) {
2436
2609
  if (!strImagePath && !strImageData) strImageData = "image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAxSURBVFhH7c4hAQAACMAw+geFGOAJAGbi5mpRmf1Z7HEdAAAAAAAAAAAAAAAAAADAAPwOyNPFH3F8AAAAAElFTkSuQmCC";
2437
2610
  const extn = imageExtensionForSource(strImagePath, strImageData);
2438
2611
  const rId = getNewRelId(target);
2439
- const mediaKey = mediaSlideKey(target);
2440
- const type = imageContentType(extn);
2441
- const dupe = target._relsMedia.find((item) => {
2442
- if (item.isDuplicate || !item.Target || item.type !== type) return false;
2443
- return strImagePath ? item.path === strImagePath : item.data === strImageData;
2444
- });
2445
- target._relsMedia.push({
2446
- path: strImagePath || "preencoded." + extn,
2447
- type,
2448
- extn,
2449
- data: strImageData || "",
2450
- rId,
2451
- isDuplicate: !!dupe?.Target,
2452
- Target: dupe?.Target ? dupe.Target : `../media/image-${mediaKey}-${target._relsMedia.length + 1}.${extn}`
2453
- });
2612
+ registerImageMediaRel(target, {
2613
+ path: strImagePath,
2614
+ data: strImageData,
2615
+ extn
2616
+ }, rId);
2454
2617
  return rId;
2455
2618
  }
2456
2619
  //#endregion
@@ -2555,7 +2718,8 @@ function addModel3dDefinition(target, opt) {
2555
2718
  w: opt.w ?? 4,
2556
2719
  h: opt.h ?? 3,
2557
2720
  objectName,
2558
- ...opt.altText ? { altText: opt.altText } : {}
2721
+ ...opt.altText ? { altText: opt.altText } : {},
2722
+ ...opt.objectLock ? { objectLock: opt.objectLock } : {}
2559
2723
  },
2560
2724
  model3d: {
2561
2725
  modelRid,
@@ -2709,6 +2873,70 @@ function addOleObjectDefinition(target, opt) {
2709
2873
  target._slideObjects.push(slideData);
2710
2874
  }
2711
2875
  //#endregion
2876
+ //#region src/gen/table/spans.ts
2877
+ /**
2878
+ * The largest `colspan`/`rowspan` this library will honour.
2879
+ *
2880
+ * Unlike the read side's point-cache ceiling there is no format number to borrow: `a:tc@gridSpan`
2881
+ * is a bare `xsd:int`. So this is a sanity bound rather than a schema one, and it is set an order
2882
+ * of magnitude past PowerPoint's own maximum table (its Insert Table dialog stops at 75 columns
2883
+ * and 75 rows), which is what makes it something no authored table can reach. What it stops is
2884
+ * the other end, where the allocation takes the host process with it.
2885
+ */
2886
+ const MAX_TABLE_SPAN = 1e3;
2887
+ /**
2888
+ * Validate one cell's `colspan`/`rowspan`, warning and falling back to `1` when it is not a whole
2889
+ * number in `1..MAX_TABLE_SPAN`.
2890
+ *
2891
+ * `undefined` is the common case and means "no span" — it warns about nothing and returns `1`.
2892
+ * Everything else is caller error: `NaN`, a negative, a fraction and a span past the ceiling all
2893
+ * describe a table that cannot be built (a fractional span silently truncates, a negative one
2894
+ * shifts every column after it, an enormous one is the allocation above).
2895
+ * @param value - the raw option value
2896
+ * @param kind - `'colspan'` or `'rowspan'`, for the diagnostic
2897
+ * @returns the span to use, at least 1
2898
+ */
2899
+ function resolveSpan(value, kind) {
2900
+ if (value === void 0) return 1;
2901
+ if (!Number.isInteger(value) || value < 1 || value > 1e3) {
2902
+ warn("table/span-out-of-range", `table cell: ${kind} must be a whole number between 1 and ${MAX_TABLE_SPAN}; got ${String(value)}. Using 1.`);
2903
+ return 1;
2904
+ }
2905
+ return value;
2906
+ }
2907
+ /**
2908
+ * Re-clone `rows` with every out-of-range `colspan`/`rowspan` corrected to `1`.
2909
+ *
2910
+ * Runs once, up front, so the column count, the `_hmerge`/`_vmerge` dummies, the auto-pager's
2911
+ * grid and the emitted `gridSpan`/`rowSpan` attributes all read the same number — checking at
2912
+ * each of those sites instead would warn several times per bad cell and could still disagree.
2913
+ *
2914
+ * A cell whose spans are fine is passed through by identity, not copied: `_spanOrigin` links the
2915
+ * merge dummies back to the origin *object*, and the all-valid path is every real table, which
2916
+ * must stay allocation-free and byte-identical.
2917
+ * @param rows - the rows to check; row arrays are never mutated
2918
+ * @returns the same rows with only the offending cells replaced
2919
+ */
2920
+ function withCheckedSpans(rows) {
2921
+ return rows.map((cells) => cells.map((cell) => {
2922
+ const opts = cell?.options;
2923
+ if (!opts) return cell;
2924
+ const colspan = resolveSpan(opts.colspan, "colspan");
2925
+ const rowspan = resolveSpan(opts.rowspan, "rowspan");
2926
+ const colspanOk = opts.colspan === void 0 || opts.colspan === colspan;
2927
+ const rowspanOk = opts.rowspan === void 0 || opts.rowspan === rowspan;
2928
+ if (colspanOk && rowspanOk) return cell;
2929
+ return {
2930
+ ...cell,
2931
+ options: {
2932
+ ...opts,
2933
+ ...opts.colspan === void 0 ? {} : { colspan },
2934
+ ...opts.rowspan === void 0 ? {} : { rowspan }
2935
+ }
2936
+ };
2937
+ }));
2938
+ }
2939
+ //#endregion
2712
2940
  //#region src/gen/define/table.ts
2713
2941
  /**
2714
2942
  * Expand a cell/table border into the 4-side tuple the rest of the pipeline expects.
@@ -2907,7 +3135,8 @@ function registerTableImageFills(target, rows, opt) {
2907
3135
  }
2908
3136
  function addTableDefinition(target, tableRows, options, slideLayout, presLayout, addSlide, getSlide) {
2909
3137
  const slides = [target];
2910
- const opt = options && typeof options === "object" ? options : {};
3138
+ const opt = options && typeof options === "object" ? { ...options } : {};
3139
+ if (Array.isArray(opt.border)) opt.border = [...opt.border];
2911
3140
  opt.objectName = opt.objectName ? encodeXmlAttrValue(validateObjectName(opt.objectName, "table")) : `Table ${target._slideObjects.filter((obj) => obj._type === "table").length}`;
2912
3141
  if (opt.placeholder && slideLayout?._slideObjects) {
2913
3142
  const placeHold = slideLayout._slideObjects.find((item) => item._type === "placeholder" && item.options?.placeholder === opt.placeholder);
@@ -2920,7 +3149,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
2920
3149
  }
2921
3150
  if (tableRows === null || tableRows.length === 0 || !Array.isArray(tableRows)) throw new InvalidOptionError("table/rows-not-an-array", "addTable: Array expected! EX: 'slide.addTable( [rows], {options} );'");
2922
3151
  if (!tableRows[0] || !Array.isArray(tableRows[0])) throw new InvalidOptionError("table/rows-not-nested", "addTable: 'rows' should be an array of cells! EX: 'slide.addTable( [ ['A'], ['B'], {text:'C',options:{align:'center'}} ] );'");
2923
- const arrRows = normalizeTableRows(applyTableHeaderColumnSugar(tableRows, opt), opt);
3152
+ const arrRows = withCheckedSpans(normalizeTableRows(applyTableHeaderColumnSugar(tableRows, opt), opt));
2924
3153
  if (opt.x === void 0 || opt.x === null) opt.x = .5;
2925
3154
  if (opt.y === void 0 || opt.y === null) opt.y = .5;
2926
3155
  opt.fontSize = opt.fontSize || 12;
@@ -3091,7 +3320,8 @@ function pushZoomObject(target, variant, opts, zoom) {
3091
3320
  y: opts.y ?? 0,
3092
3321
  w: opts.w ?? 0,
3093
3322
  h: opts.h ?? 0,
3094
- objectName
3323
+ objectName,
3324
+ ...opts.objectLock ? { objectLock: opts.objectLock } : {}
3095
3325
  },
3096
3326
  zoom: {
3097
3327
  variant,
@@ -3212,6 +3442,30 @@ function addSummaryZoomDefinition(target, opts, sections) {
3212
3442
  }
3213
3443
  //#endregion
3214
3444
  //#region src/slide.ts
3445
+ /**
3446
+ * Project one authored render-object onto the public {@link SlideObjectInfo}, recursing into a
3447
+ * group's children.
3448
+ *
3449
+ * The name is decoded on the way out. Every `add*Definition` stores it attribute-escaped, and
3450
+ * `groupObjects()` escapes the caller's spelling before it compares, so handing the stored form
3451
+ * back would escape it twice and resolve nothing — the same mismatch that once made `groupObjects`
3452
+ * throw for an object plainly on the slide, arriving from the other direction.
3453
+ *
3454
+ * Every `add*Definition` also *generates* a name when the caller passed none, so the fallback below
3455
+ * is unreachable through the public API. It is there because the field is typed non-optional
3456
+ * internally only by convention, and an empty name would otherwise read back as `undefined` from a
3457
+ * surface that promises a string.
3458
+ */
3459
+ function toSlideObjectInfo(obj) {
3460
+ const stored = obj.options?.objectName;
3461
+ return {
3462
+ type: obj._type,
3463
+ objectName: typeof stored === "string" && stored.length > 0 ? decodeXmlAttrValue(stored) : "",
3464
+ isPlaceholder: !!obj.options?.placeholder,
3465
+ canGroup: isGroupableObject(obj),
3466
+ children: (obj._groupObjects ?? []).map(toSlideObjectInfo)
3467
+ };
3468
+ }
3215
3469
  /** Distinguish a multi-type (combo) chart array (`ChartMulti[]`) from a single chart's data (`OptsChartData[]`). */
3216
3470
  function isMultiChart(arg) {
3217
3471
  const first = arg[0];
@@ -3318,6 +3572,17 @@ var SlideBuilder = class {
3318
3572
  get newAutoPagedSlides() {
3319
3573
  return this._newAutoPagedSlides;
3320
3574
  }
3575
+ /**
3576
+ * The objects authored on this slide so far, bottom-to-top in z-order.
3577
+ *
3578
+ * Built fresh on every access rather than cached: the underlying list is mutated in place by
3579
+ * `addGroup`/`groupObjects` as well as appended to, so a cache would have to be invalidated from
3580
+ * every authoring path and would go stale the first time one was added without remembering.
3581
+ * Building it costs a walk of a list a slide-sized deck keeps in the dozens.
3582
+ */
3583
+ get objects() {
3584
+ return this._slideObjects.map(toSlideObjectInfo);
3585
+ }
3321
3586
  /** Slide width in inches (resolved from the active presentation layout). */
3322
3587
  get width() {
3323
3588
  return emuToInches(this._presLayout.width);
@@ -3567,16 +3832,17 @@ var SlideBuilder = class {
3567
3832
  * than one parameterized one. Called by {@link ./chart-xml}'s axis region.
3568
3833
  *
3569
3834
  * Sub-blocks are shared where they can be shared *exactly*: {@link axisLineSpPr} covers
3570
- * `<c:spPr>`, which differs between the three only by indent width. `<c:txPr>` is
3571
- * deliberately left duplicated — its inherited indentation is irregular *within* each copy
3572
- * as well as between them (the category axis closes `</a:defRPr>` at three spaces, the value
3573
- * axis at six), so parameterising it would take about as many indent arguments as it has
3574
- * lines and would hide the quirks rather than share the structure.
3835
+ * `<c:spPr>`, which differs between the three only by indent width. `<c:txPr>` stays
3836
+ * duplicated — its inherited indentation is irregular *within* each copy as well as between
3837
+ * them (the category axis closes `</a:defRPr>` at three spaces, the value axis at six), so
3838
+ * one parameterised builder would take about as many indent arguments as it has lines, and
3839
+ * would hide the quirks rather than share the structure. Each copy states its own layout at
3840
+ * its own call site instead.
3575
3841
  *
3576
- * The other way out -- regularising the whitespace so one builder *could* serve all three --
3577
- * is a byte change, which AGENTS.md is explicit about: a whitespace-only diff is a STOP, not
3578
- * a known divergence. Revisit this only if the emitted whitespace in these parts is ever
3579
- * regularised deliberately, as its own decision.
3842
+ * Regularising that whitespace so one builder *could* serve all three is still open, and
3843
+ * still a byte change — AGENTS.md is explicit that a whitespace-only diff is a STOP, not a
3844
+ * known divergence. It is a decision to take deliberately, with a re-baselined gate, not a
3845
+ * cleanup to fold into something else.
3580
3846
  */
3581
3847
  /**
3582
3848
  * The `<c:spPr>` axis-line block, identical in shape on all three axes.
@@ -3596,89 +3862,103 @@ var SlideBuilder = class {
3596
3862
  */
3597
3863
  function axisLineSpPr(indent, widthEmu, show, color, dash) {
3598
3864
  const at = (extra) => " ".repeat(indent + extra);
3599
- return `${at(0)}<c:spPr>${at(2)}<a:ln w="${widthEmu}" cap="flat">` + (!show ? "<a:noFill/>" : genXmlColorSelection(color || DEF_GRIDLINE_COLOR)) + `${at(4)}<a:prstDash val="${dash}"/>${at(4)}<a:round/>${at(2)}</a:ln>${at(0)}</c:spPr>`;
3865
+ const line = el("a:ln", {
3866
+ w: widthEmu,
3867
+ cap: "flat"
3868
+ }, [
3869
+ raw(!show ? voidEl("a:noFill") : genXmlColorSelection(color || DEF_GRIDLINE_COLOR)),
3870
+ raw(voidEl("a:prstDash", { val: dash }, { openPrefix: at(4) })),
3871
+ raw(voidEl("a:round", null, { openPrefix: at(4) }))
3872
+ ], {
3873
+ openPrefix: at(2),
3874
+ closePrefix: at(2)
3875
+ });
3876
+ return el("c:spPr", null, raw(line), {
3877
+ openPrefix: at(0),
3878
+ closePrefix: at(0)
3879
+ });
3600
3880
  }
3601
3881
  function makeCatAxis(opts, axisId, valAxisId) {
3602
- let strXml = "";
3603
3882
  const usesValueAxisForCategories = opts._type === "scatter" || opts._type === "bubble" || opts._type === "bubble3D";
3604
3883
  const usesCategoryAxis = !usesValueAxisForCategories && !opts.catLabelFormatCode;
3605
- if (usesValueAxisForCategories) strXml += "<c:valAx>";
3606
- else strXml += "<c:" + (opts.catLabelFormatCode ? "dateAx" : "catAx") + ">";
3607
- strXml += " <c:axId val=\"" + axisId + "\"/>";
3608
- strXml += " <c:scaling>";
3609
- strXml += "<c:orientation val=\"" + (opts.catAxisOrientation || "minMax") + "\"/>";
3610
- if (opts.catAxisMaxVal || opts.catAxisMaxVal === 0) strXml += `<c:max val="${opts.catAxisMaxVal}"/>`;
3611
- if (opts.catAxisMinVal || opts.catAxisMinVal === 0) strXml += `<c:min val="${opts.catAxisMinVal}"/>`;
3612
- strXml += "</c:scaling>";
3613
- strXml += " <c:delete val=\"" + (opts.catAxisHidden ? "1" : "0") + "\"/>";
3614
- strXml += " <c:axPos val=\"" + (opts.barDir === "col" ? "b" : "l") + "\"/>";
3615
- strXml += opts.catGridLine && opts.catGridLine.style !== "none" ? createGridLineElement(opts.catGridLine) : "";
3616
- if (opts.showCatAxisTitle) strXml += genXmlTitle({
3617
- color: opts.catAxisTitleColor,
3618
- fontFace: opts.catAxisTitleFontFace,
3619
- fontSize: opts.catAxisTitleFontSize,
3620
- titleRotate: opts.catAxisTitleRotate,
3621
- title: opts.catAxisTitle || "Axis Title"
3622
- });
3623
- if (opts._type === "scatter" || opts._type === "bubble" || opts._type === "bubble3D") {
3624
- const xAxisFmtCode = opts.catAxisLabelFormatCode ?? opts.valAxisLabelFormatCode;
3625
- strXml += " " + voidEl("c:numFmt", {
3626
- formatCode: xAxisFmtCode || "General",
3627
- sourceLinked: 1
3628
- });
3629
- } else strXml += " " + voidEl("c:numFmt", {
3884
+ const tag = usesValueAxisForCategories ? "c:valAx" : opts.catLabelFormatCode ? "c:dateAx" : "c:catAx";
3885
+ const scaling = el("c:scaling", null, [
3886
+ raw(voidEl("c:orientation", { val: opts.catAxisOrientation || "minMax" })),
3887
+ opts.catAxisMaxVal || opts.catAxisMaxVal === 0 ? raw(voidEl("c:max", { val: opts.catAxisMaxVal })) : null,
3888
+ opts.catAxisMinVal || opts.catAxisMinVal === 0 ? raw(voidEl("c:min", { val: opts.catAxisMinVal })) : null
3889
+ ], { openPrefix: " " });
3890
+ const numFmt = usesValueAxisForCategories ? voidEl("c:numFmt", {
3891
+ formatCode: (opts.catAxisLabelFormatCode ?? opts.valAxisLabelFormatCode) || "General",
3892
+ sourceLinked: 1
3893
+ }, { openPrefix: " " }) : voidEl("c:numFmt", {
3630
3894
  formatCode: (opts.catLabelFormatCode ?? "") || "General",
3631
3895
  sourceLinked: 1
3896
+ }, { openPrefix: " " });
3897
+ const ticks = opts._type === "scatter" ? voidEl("c:majorTickMark", { val: "none" }, { openPrefix: " " }) + voidEl("c:minorTickMark", { val: "none" }, { openPrefix: " " }) + voidEl("c:tickLblPos", { val: opts.catAxisLabelPos || "nextTo" }, { openPrefix: " " }) : voidEl("c:majorTickMark", { val: opts.catAxisMajorTickMark || "out" }, { openPrefix: " " }) + voidEl("c:minorTickMark", { val: opts.catAxisMinorTickMark || "none" }, { openPrefix: " " }) + voidEl("c:tickLblPos", { val: opts.catAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo") }, { openPrefix: " " });
3898
+ const defRPr = el("a:defRPr", {
3899
+ sz: ptToHundredths(opts.catAxisLabelFontSize || 12),
3900
+ b: opts.catAxisLabelFontBold ? 1 : 0,
3901
+ i: opts.catAxisLabelFontItalic ? 1 : 0,
3902
+ u: "none",
3903
+ strike: "noStrike"
3904
+ }, [raw(genXmlColorSelection(opts.catAxisLabelColor || "000000")), raw(" " + createChartTextFonts(opts.catAxisLabelFontFace || "Arial"))], {
3905
+ openPrefix: " ",
3906
+ closePrefix: " "
3907
+ });
3908
+ const txPr = el("c:txPr", null, [
3909
+ raw(voidEl("a:bodyPr", { rot: opts.catAxisLabelRotate ? convertAngleUnits(opts.catAxisLabelRotate, "catAxisLabelRotate") : void 0 })),
3910
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
3911
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
3912
+ openPrefix: " ",
3913
+ closePrefix: " "
3914
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
3915
+ openPrefix: " ",
3916
+ closePrefix: " "
3917
+ }))
3918
+ ], {
3919
+ openPrefix: " ",
3920
+ closePrefix: " "
3632
3921
  });
3633
- if (opts._type === "scatter") {
3634
- strXml += " <c:majorTickMark val=\"none\"/>";
3635
- strXml += " <c:minorTickMark val=\"none\"/>";
3636
- strXml += " <c:tickLblPos val=\"" + (opts.catAxisLabelPos || "nextTo") + "\"/>";
3637
- } else {
3638
- strXml += " <c:majorTickMark val=\"" + (opts.catAxisMajorTickMark || "out") + "\"/>";
3639
- strXml += " <c:minorTickMark val=\"" + (opts.catAxisMinorTickMark || "none") + "\"/>";
3640
- strXml += " <c:tickLblPos val=\"" + (opts.catAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo")) + "\"/>";
3641
- }
3642
- strXml += axisLineSpPr(2, opts.catAxisLineSize ? ptsToEmuLenient(opts.catAxisLineSize) : EMU_PER_POINT, opts.catAxisLineShow, opts.catAxisLineColor, opts.catAxisLineStyle || "solid");
3643
- strXml += " <c:txPr>";
3644
- if (opts.catAxisLabelRotate) strXml += `<a:bodyPr rot="${convertAngleUnits(opts.catAxisLabelRotate, "catAxisLabelRotate")}"/>`;
3645
- else strXml += "<a:bodyPr/>";
3646
- strXml += " <a:lstStyle/>";
3647
- strXml += " <a:p>";
3648
- strXml += " <a:pPr>";
3649
- strXml += ` <a:defRPr sz="${ptToHundredths(opts.catAxisLabelFontSize || 12)}" b="${opts.catAxisLabelFontBold ? 1 : 0}" i="${opts.catAxisLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
3650
- strXml += genXmlColorSelection(opts.catAxisLabelColor || "000000");
3651
- strXml += " " + createChartTextFonts(opts.catAxisLabelFontFace || "Arial");
3652
- strXml += " </a:defRPr>";
3653
- strXml += " </a:pPr>";
3654
- strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
3655
- strXml += " </a:p>";
3656
- strXml += " </c:txPr>";
3657
- strXml += " <c:crossAx val=\"" + valAxisId + "\"/>";
3658
3922
  const valAxisCrossTag = typeof opts.valAxisCrossesAt === "number" ? "crossesAt" : "crosses";
3659
3923
  const valAxisCrossValue = typeof opts.valAxisCrossesAt === "number" ? opts.valAxisCrossesAt : opts.valAxisCrossesAt || "autoZero";
3660
- strXml += ` <c:${valAxisCrossTag} val="${valAxisCrossValue}"/>`;
3661
- if (!usesValueAxisForCategories) strXml += " <c:auto val=\"1\"/>";
3662
- if (usesCategoryAxis) {
3663
- strXml += " <c:lblAlgn val=\"ctr\"/>";
3664
- if (opts.catAxisLabelFrequency) strXml += " <c:tickLblSkip val=\"" + opts.catAxisLabelFrequency + "\"/>";
3665
- strXml += ` <c:noMultiLvlLbl val="${opts.catAxisMultiLevelLabels ? 0 : 1}"/>`;
3666
- }
3924
+ let units = "";
3667
3925
  if (opts.catLabelFormatCode || usesValueAxisForCategories) {
3668
3926
  if (opts.catLabelFormatCode) {
3669
3927
  const baseTimeUnit = validTimeUnit(opts.catAxisBaseTimeUnit, "catAxisBaseTimeUnit");
3670
3928
  const majorTimeUnit = validTimeUnit(opts.catAxisMajorTimeUnit, "catAxisMajorTimeUnit");
3671
3929
  const minorTimeUnit = validTimeUnit(opts.catAxisMinorTimeUnit, "catAxisMinorTimeUnit");
3672
- if (baseTimeUnit) strXml += "<c:baseTimeUnit val=\"" + baseTimeUnit + "\"/>";
3673
- if (majorTimeUnit) strXml += voidEl("c:majorTimeUnit", { val: majorTimeUnit });
3674
- if (minorTimeUnit) strXml += voidEl("c:minorTimeUnit", { val: minorTimeUnit });
3930
+ if (baseTimeUnit) units += voidEl("c:baseTimeUnit", { val: baseTimeUnit });
3931
+ if (majorTimeUnit) units += voidEl("c:majorTimeUnit", { val: majorTimeUnit });
3932
+ if (minorTimeUnit) units += voidEl("c:minorTimeUnit", { val: minorTimeUnit });
3675
3933
  }
3676
- if (opts.catAxisMajorUnit) strXml += `<c:majorUnit val="${opts.catAxisMajorUnit}"/>`;
3677
- if (opts.catAxisMinorUnit) strXml += `<c:minorUnit val="${opts.catAxisMinorUnit}"/>`;
3678
- }
3679
- if (usesValueAxisForCategories) strXml += "</c:valAx>";
3680
- else strXml += "</c:" + (opts.catLabelFormatCode ? "dateAx" : "catAx") + ">";
3681
- return strXml;
3934
+ if (opts.catAxisMajorUnit) units += voidEl("c:majorUnit", { val: opts.catAxisMajorUnit });
3935
+ if (opts.catAxisMinorUnit) units += voidEl("c:minorUnit", { val: opts.catAxisMinorUnit });
3936
+ }
3937
+ return el(tag, null, [
3938
+ raw(voidEl("c:axId", { val: axisId }, { openPrefix: " " })),
3939
+ raw(scaling),
3940
+ raw(voidEl("c:delete", { val: opts.catAxisHidden ? 1 : 0 }, { openPrefix: " " })),
3941
+ raw(voidEl("c:axPos", { val: opts.barDir === "col" ? "b" : "l" }, { openPrefix: " " })),
3942
+ raw(opts.catGridLine && opts.catGridLine.style !== "none" ? createGridLineElement(opts.catGridLine) : ""),
3943
+ opts.showCatAxisTitle ? raw(genXmlTitle({
3944
+ color: opts.catAxisTitleColor,
3945
+ fontFace: opts.catAxisTitleFontFace,
3946
+ fontSize: opts.catAxisTitleFontSize,
3947
+ titleRotate: opts.catAxisTitleRotate,
3948
+ title: opts.catAxisTitle || "Axis Title"
3949
+ })) : null,
3950
+ raw(numFmt),
3951
+ raw(ticks),
3952
+ raw(axisLineSpPr(2, opts.catAxisLineSize ? ptsToEmuLenient(opts.catAxisLineSize) : EMU_PER_POINT, opts.catAxisLineShow, opts.catAxisLineColor, opts.catAxisLineStyle || "solid")),
3953
+ raw(txPr),
3954
+ raw(voidEl("c:crossAx", { val: valAxisId }, { openPrefix: " " })),
3955
+ raw(voidEl(`c:${valAxisCrossTag}`, { val: valAxisCrossValue }, { openPrefix: " " })),
3956
+ usesValueAxisForCategories ? null : raw(voidEl("c:auto", { val: 1 }, { openPrefix: " " })),
3957
+ usesCategoryAxis ? raw(voidEl("c:lblAlgn", { val: "ctr" }, { openPrefix: " " })) : null,
3958
+ usesCategoryAxis && opts.catAxisLabelFrequency ? raw(voidEl("c:tickLblSkip", { val: opts.catAxisLabelFrequency }, { openPrefix: " " })) : null,
3959
+ usesCategoryAxis ? raw(voidEl("c:noMultiLvlLbl", { val: opts.catAxisMultiLevelLabels ? 0 : 1 }, { openPrefix: " " })) : null,
3960
+ raw(units)
3961
+ ]);
3682
3962
  }
3683
3963
  /**
3684
3964
  * Create Value Axis (Used by `bar3D`)
@@ -3690,61 +3970,68 @@ function makeValAxis(opts, valAxisId) {
3690
3970
  let axisPos = valAxisId === "2094734552" ? opts.barDir === "col" ? "l" : "b" : opts.barDir !== "col" ? "r" : "t";
3691
3971
  if (valAxisId === "2094734553") axisPos = "r";
3692
3972
  const crossAxId = valAxisId === "2094734552" ? AXIS_ID_CATEGORY_PRIMARY : AXIS_ID_CATEGORY_SECONDARY;
3693
- let strXml = "";
3694
- strXml += "<c:valAx>";
3695
- strXml += " <c:axId val=\"" + valAxisId + "\"/>";
3696
- strXml += " <c:scaling>";
3697
- if (opts.valAxisLogScaleBase) strXml += `<c:logBase val="${opts.valAxisLogScaleBase}"/>`;
3698
- strXml += "<c:orientation val=\"" + (opts.valAxisOrientation || "minMax") + "\"/>";
3699
- if (opts.valAxisMaxVal || opts.valAxisMaxVal === 0) strXml += `<c:max val="${opts.valAxisMaxVal}"/>`;
3700
- if (opts.valAxisMinVal || opts.valAxisMinVal === 0) strXml += `<c:min val="${opts.valAxisMinVal}"/>`;
3701
- strXml += " </c:scaling>";
3702
- strXml += ` <c:delete val="${opts.valAxisHidden ? 1 : 0}"/>`;
3703
- strXml += " <c:axPos val=\"" + axisPos + "\"/>";
3704
- if (opts.valGridLine && opts.valGridLine.style !== "none") strXml += createGridLineElement(opts.valGridLine);
3705
- if (opts.showValAxisTitle) strXml += genXmlTitle({
3706
- color: opts.valAxisTitleColor,
3707
- fontFace: opts.valAxisTitleFontFace,
3708
- fontSize: opts.valAxisTitleFontSize,
3709
- titleRotate: opts.valAxisTitleRotate,
3710
- title: opts.valAxisTitle || "Axis Title"
3711
- });
3712
- strXml += voidEl("c:numFmt", {
3713
- formatCode: opts.valAxisLabelFormatCode || "General",
3714
- sourceLinked: 0
3973
+ const scaling = el("c:scaling", null, [
3974
+ opts.valAxisLogScaleBase ? raw(voidEl("c:logBase", { val: opts.valAxisLogScaleBase })) : null,
3975
+ raw(voidEl("c:orientation", { val: opts.valAxisOrientation || "minMax" })),
3976
+ opts.valAxisMaxVal || opts.valAxisMaxVal === 0 ? raw(voidEl("c:max", { val: opts.valAxisMaxVal })) : null,
3977
+ opts.valAxisMinVal || opts.valAxisMinVal === 0 ? raw(voidEl("c:min", { val: opts.valAxisMinVal })) : null
3978
+ ], {
3979
+ openPrefix: " ",
3980
+ closePrefix: " "
3715
3981
  });
3716
- if (opts._type === "scatter") {
3717
- strXml += " <c:majorTickMark val=\"none\"/>";
3718
- strXml += " <c:minorTickMark val=\"none\"/>";
3719
- strXml += " <c:tickLblPos val=\"nextTo\"/>";
3720
- } else {
3721
- strXml += " <c:majorTickMark val=\"" + (opts.valAxisMajorTickMark || "out") + "\"/>";
3722
- strXml += " <c:minorTickMark val=\"" + (opts.valAxisMinorTickMark || "none") + "\"/>";
3723
- strXml += " <c:tickLblPos val=\"" + (opts.valAxisLabelPos || (opts.barDir === "col" ? "nextTo" : "low")) + "\"/>";
3724
- }
3725
- strXml += axisLineSpPr(1, opts.valAxisLineSize ? ptsToEmuLenient(opts.valAxisLineSize) : EMU_PER_POINT, opts.valAxisLineShow, opts.valAxisLineColor, opts.valAxisLineStyle || "solid");
3726
- strXml += " <c:txPr>";
3727
- strXml += ` <a:bodyPr${opts.valAxisLabelRotate ? " rot=\"" + convertAngleUnits(opts.valAxisLabelRotate, "valAxisLabelRotate").toString() + "\"" : ""}/>`;
3728
- strXml += " <a:lstStyle/>";
3729
- strXml += " <a:p>";
3730
- strXml += " <a:pPr>";
3731
- strXml += ` <a:defRPr sz="${ptToHundredths(opts.valAxisLabelFontSize || 12)}" b="${opts.valAxisLabelFontBold ? 1 : 0}" i="${opts.valAxisLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
3732
- strXml += genXmlColorSelection(opts.valAxisLabelColor || "000000");
3733
- strXml += " " + createChartTextFonts(opts.valAxisLabelFontFace || "Arial");
3734
- strXml += " </a:defRPr>";
3735
- strXml += " </a:pPr>";
3736
- strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
3737
- strXml += " </a:p>";
3738
- strXml += " </c:txPr>";
3739
- strXml += " <c:crossAx val=\"" + crossAxId + "\"/>";
3740
- if (typeof opts.catAxisCrossesAt === "number") strXml += ` <c:crossesAt val="${opts.catAxisCrossesAt}"/>`;
3741
- else if (typeof opts.catAxisCrossesAt === "string") strXml += " <c:crosses val=\"" + opts.catAxisCrossesAt + "\"/>";
3742
- else strXml += " <c:crosses val=\"" + (axisPos === "r" || axisPos === "t" ? "max" : "autoZero") + "\"/>";
3743
- strXml += " <c:crossBetween val=\"" + (opts.valAxisCrossBetween ? opts.valAxisCrossBetween : opts._type === "scatter" || !!(Array.isArray(opts._type) && opts._type.some((type) => asChartType(type.type) === "area")) ? "midCat" : "between") + "\"/>";
3744
- if (opts.valAxisMajorUnit) strXml += ` <c:majorUnit val="${opts.valAxisMajorUnit}"/>`;
3745
- if (opts.valAxisDisplayUnit) strXml += el("c:dispUnits", null, [raw(voidEl("c:builtInUnit", { val: opts.valAxisDisplayUnit })), raw(opts.valAxisDisplayUnitLabel ? voidEl("c:dispUnitsLbl") : "")]);
3746
- strXml += "</c:valAx>";
3747
- return strXml;
3982
+ const ticks = opts._type === "scatter" ? voidEl("c:majorTickMark", { val: "none" }, { openPrefix: " " }) + voidEl("c:minorTickMark", { val: "none" }, { openPrefix: " " }) + voidEl("c:tickLblPos", { val: "nextTo" }, { openPrefix: " " }) : voidEl("c:majorTickMark", { val: opts.valAxisMajorTickMark || "out" }, { openPrefix: " " }) + voidEl("c:minorTickMark", { val: opts.valAxisMinorTickMark || "none" }, { openPrefix: " " }) + voidEl("c:tickLblPos", { val: opts.valAxisLabelPos || (opts.barDir === "col" ? "nextTo" : "low") }, { openPrefix: " " });
3983
+ const defRPr = el("a:defRPr", {
3984
+ sz: ptToHundredths(opts.valAxisLabelFontSize || 12),
3985
+ b: opts.valAxisLabelFontBold ? 1 : 0,
3986
+ i: opts.valAxisLabelFontItalic ? 1 : 0,
3987
+ u: "none",
3988
+ strike: "noStrike"
3989
+ }, [raw(genXmlColorSelection(opts.valAxisLabelColor || "000000")), raw(" " + createChartTextFonts(opts.valAxisLabelFontFace || "Arial"))], {
3990
+ openPrefix: " ",
3991
+ closePrefix: " "
3992
+ });
3993
+ const txPr = el("c:txPr", null, [
3994
+ raw(voidEl("a:bodyPr", { rot: opts.valAxisLabelRotate ? convertAngleUnits(opts.valAxisLabelRotate, "valAxisLabelRotate") : void 0 }, { openPrefix: " " })),
3995
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
3996
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
3997
+ openPrefix: " ",
3998
+ closePrefix: " "
3999
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
4000
+ openPrefix: " ",
4001
+ closePrefix: " "
4002
+ }))
4003
+ ], {
4004
+ openPrefix: " ",
4005
+ closePrefix: " "
4006
+ });
4007
+ const crosses = typeof opts.catAxisCrossesAt === "number" ? voidEl("c:crossesAt", { val: opts.catAxisCrossesAt }, { openPrefix: " " }) : typeof opts.catAxisCrossesAt === "string" ? voidEl("c:crosses", { val: opts.catAxisCrossesAt }, { openPrefix: " " }) : voidEl("c:crosses", { val: axisPos === "r" || axisPos === "t" ? "max" : "autoZero" }, { openPrefix: " " });
4008
+ const crossBetween = opts.valAxisCrossBetween || (opts._type === "scatter" || !!(Array.isArray(opts._type) && opts._type.some((type) => asChartType(type.type) === "area")) ? "midCat" : "between");
4009
+ return el("c:valAx", null, [
4010
+ raw(voidEl("c:axId", { val: valAxisId }, { openPrefix: " " })),
4011
+ raw(scaling),
4012
+ raw(voidEl("c:delete", { val: opts.valAxisHidden ? 1 : 0 }, { openPrefix: " " })),
4013
+ raw(voidEl("c:axPos", { val: axisPos }, { openPrefix: " " })),
4014
+ opts.valGridLine && opts.valGridLine.style !== "none" ? raw(createGridLineElement(opts.valGridLine)) : null,
4015
+ opts.showValAxisTitle ? raw(genXmlTitle({
4016
+ color: opts.valAxisTitleColor,
4017
+ fontFace: opts.valAxisTitleFontFace,
4018
+ fontSize: opts.valAxisTitleFontSize,
4019
+ titleRotate: opts.valAxisTitleRotate,
4020
+ title: opts.valAxisTitle || "Axis Title"
4021
+ })) : null,
4022
+ raw(voidEl("c:numFmt", {
4023
+ formatCode: opts.valAxisLabelFormatCode || "General",
4024
+ sourceLinked: 0
4025
+ })),
4026
+ raw(ticks),
4027
+ raw(axisLineSpPr(1, opts.valAxisLineSize ? ptsToEmuLenient(opts.valAxisLineSize) : EMU_PER_POINT, opts.valAxisLineShow, opts.valAxisLineColor, opts.valAxisLineStyle || "solid")),
4028
+ raw(txPr),
4029
+ raw(voidEl("c:crossAx", { val: crossAxId }, { openPrefix: " " })),
4030
+ raw(crosses),
4031
+ raw(voidEl("c:crossBetween", { val: crossBetween }, { openPrefix: " " })),
4032
+ opts.valAxisMajorUnit ? raw(voidEl("c:majorUnit", { val: opts.valAxisMajorUnit }, { openPrefix: " " })) : null,
4033
+ opts.valAxisDisplayUnit ? raw(el("c:dispUnits", null, [raw(voidEl("c:builtInUnit", { val: opts.valAxisDisplayUnit })), raw(opts.valAxisDisplayUnitLabel ? voidEl("c:dispUnitsLbl") : "")])) : null
4034
+ ]);
3748
4035
  }
3749
4036
  /**
3750
4037
  * Create Series Axis (Used by `bar3D`)
@@ -3754,651 +4041,850 @@ function makeValAxis(opts, valAxisId) {
3754
4041
  * @return {string} XML
3755
4042
  */
3756
4043
  function makeSerAxis(opts, axisId, valAxisId) {
3757
- let strXml = "";
3758
- strXml += "<c:serAx>";
3759
- strXml += " <c:axId val=\"" + axisId + "\"/>";
3760
- strXml += " <c:scaling><c:orientation val=\"" + (opts.serAxisOrientation || "minMax") + "\"/></c:scaling>";
3761
- strXml += " <c:delete val=\"" + (opts.serAxisHidden ? "1" : "0") + "\"/>";
3762
- strXml += " <c:axPos val=\"" + (opts.barDir === "col" ? "b" : "l") + "\"/>";
3763
- strXml += opts.serGridLine && opts.serGridLine.style !== "none" ? createGridLineElement(opts.serGridLine) : "";
3764
- if (opts.showSerAxisTitle) strXml += genXmlTitle({
3765
- color: opts.serAxisTitleColor,
3766
- fontFace: opts.serAxisTitleFontFace,
3767
- fontSize: opts.serAxisTitleFontSize,
3768
- titleRotate: opts.serAxisTitleRotate,
3769
- title: opts.serAxisTitle || "Axis Title"
3770
- });
3771
- strXml += " " + voidEl("c:numFmt", {
3772
- formatCode: (opts.serLabelFormatCode ?? "") || "General",
3773
- sourceLinked: 0
4044
+ const defRPr = el("a:defRPr", {
4045
+ sz: ptToHundredths(opts.serAxisLabelFontSize || 12),
4046
+ b: opts.serAxisLabelFontBold ? 1 : 0,
4047
+ i: opts.serAxisLabelFontItalic ? 1 : 0,
4048
+ u: "none",
4049
+ strike: "noStrike"
4050
+ }, [raw(" " + genXmlColorSelection(opts.serAxisLabelColor || "000000")), raw(" " + createChartTextFonts(opts.serAxisLabelFontFace || "Arial"))], {
4051
+ openPrefix: " ",
4052
+ closePrefix: " "
4053
+ });
4054
+ const txPr = el("c:txPr", null, [
4055
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
4056
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4057
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
4058
+ openPrefix: " ",
4059
+ closePrefix: " "
4060
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
4061
+ openPrefix: " ",
4062
+ closePrefix: " "
4063
+ }))
4064
+ ], {
4065
+ openPrefix: " ",
4066
+ closePrefix: " "
3774
4067
  });
3775
- strXml += " <c:majorTickMark val=\"out\"/>";
3776
- strXml += " <c:minorTickMark val=\"none\"/>";
3777
- strXml += ` <c:tickLblPos val="${opts.serAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo")}"/>`;
3778
- strXml += axisLineSpPr(2, EMU_PER_POINT, opts.serAxisLineShow, opts.serAxisLineColor, "solid");
3779
- strXml += " <c:txPr>";
3780
- strXml += " <a:bodyPr/>";
3781
- strXml += " <a:lstStyle/>";
3782
- strXml += " <a:p>";
3783
- strXml += " <a:pPr>";
3784
- strXml += ` <a:defRPr sz="${ptToHundredths(opts.serAxisLabelFontSize || 12)}" b="${opts.serAxisLabelFontBold ? "1" : "0"}" i="${opts.serAxisLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
3785
- strXml += ` ${genXmlColorSelection(opts.serAxisLabelColor || "000000")}`;
3786
- strXml += " " + createChartTextFonts(opts.serAxisLabelFontFace || "Arial");
3787
- strXml += " </a:defRPr>";
3788
- strXml += " </a:pPr>";
3789
- strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\"/>";
3790
- strXml += " </a:p>";
3791
- strXml += " </c:txPr>";
3792
- strXml += " <c:crossAx val=\"" + valAxisId + "\"/>";
3793
- strXml += " <c:crosses val=\"autoZero\"/>";
3794
- if (opts.serAxisLabelFrequency) strXml += " <c:tickLblSkip val=\"" + opts.serAxisLabelFrequency + "\"/>";
4068
+ let units = "";
3795
4069
  if (opts.serLabelFormatCode) {
3796
4070
  const baseTimeUnit = validTimeUnit(opts.serAxisBaseTimeUnit, "serAxisBaseTimeUnit");
3797
4071
  const majorTimeUnit = validTimeUnit(opts.serAxisMajorTimeUnit, "serAxisMajorTimeUnit");
3798
4072
  const minorTimeUnit = validTimeUnit(opts.serAxisMinorTimeUnit, "serAxisMinorTimeUnit");
3799
- if (baseTimeUnit) strXml += ` <c:baseTimeUnit val="${baseTimeUnit}"/>`;
3800
- if (majorTimeUnit) strXml += voidEl("c:majorTimeUnit", { val: majorTimeUnit }, { openPrefix: " " });
3801
- if (minorTimeUnit) strXml += voidEl("c:minorTimeUnit", { val: minorTimeUnit }, { openPrefix: " " });
3802
- if (opts.serAxisMajorUnit) strXml += ` <c:majorUnit val="${opts.serAxisMajorUnit}"/>`;
3803
- if (opts.serAxisMinorUnit) strXml += ` <c:minorUnit val="${opts.serAxisMinorUnit}"/>`;
3804
- }
3805
- strXml += "</c:serAx>";
3806
- return strXml;
4073
+ if (baseTimeUnit) units += ` <c:baseTimeUnit val="${baseTimeUnit}"/>`;
4074
+ if (majorTimeUnit) units += voidEl("c:majorTimeUnit", { val: majorTimeUnit }, { openPrefix: " " });
4075
+ if (minorTimeUnit) units += voidEl("c:minorTimeUnit", { val: minorTimeUnit }, { openPrefix: " " });
4076
+ if (opts.serAxisMajorUnit) units += voidEl("c:majorUnit", { val: opts.serAxisMajorUnit }, { openPrefix: " " });
4077
+ if (opts.serAxisMinorUnit) units += voidEl("c:minorUnit", { val: opts.serAxisMinorUnit }, { openPrefix: " " });
4078
+ }
4079
+ return el("c:serAx", null, [
4080
+ raw(voidEl("c:axId", { val: axisId }, { openPrefix: " " })),
4081
+ raw(el("c:scaling", null, raw(voidEl("c:orientation", { val: opts.serAxisOrientation || "minMax" })), { openPrefix: " " })),
4082
+ raw(voidEl("c:delete", { val: opts.serAxisHidden ? 1 : 0 }, { openPrefix: " " })),
4083
+ raw(voidEl("c:axPos", { val: opts.barDir === "col" ? "b" : "l" }, { openPrefix: " " })),
4084
+ raw(opts.serGridLine && opts.serGridLine.style !== "none" ? createGridLineElement(opts.serGridLine) : ""),
4085
+ opts.showSerAxisTitle ? raw(genXmlTitle({
4086
+ color: opts.serAxisTitleColor,
4087
+ fontFace: opts.serAxisTitleFontFace,
4088
+ fontSize: opts.serAxisTitleFontSize,
4089
+ titleRotate: opts.serAxisTitleRotate,
4090
+ title: opts.serAxisTitle || "Axis Title"
4091
+ })) : null,
4092
+ raw(voidEl("c:numFmt", {
4093
+ formatCode: (opts.serLabelFormatCode ?? "") || "General",
4094
+ sourceLinked: 0
4095
+ }, { openPrefix: " " })),
4096
+ raw(voidEl("c:majorTickMark", { val: "out" }, { openPrefix: " " })),
4097
+ raw(voidEl("c:minorTickMark", { val: "none" }, { openPrefix: " " })),
4098
+ raw(voidEl("c:tickLblPos", { val: opts.serAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo") }, { openPrefix: " " })),
4099
+ raw(axisLineSpPr(2, EMU_PER_POINT, opts.serAxisLineShow, opts.serAxisLineColor, "solid")),
4100
+ raw(txPr),
4101
+ raw(voidEl("c:crossAx", { val: valAxisId }, { openPrefix: " " })),
4102
+ raw(voidEl("c:crosses", { val: "autoZero" }, { openPrefix: " " })),
4103
+ opts.serAxisLabelFrequency ? raw(voidEl("c:tickLblSkip", { val: opts.serAxisLabelFrequency }, { openPrefix: " " })) : null,
4104
+ raw(units)
4105
+ ]);
3807
4106
  }
3808
- /**
3809
- * Create char title elements
3810
- * @param {ChartPropsTitle} opts - options
3811
- * @return {string} XML `<c:title>`
3812
- */
3813
4107
  //#endregion
3814
4108
  //#region src/gen/chart/plot-cat-axis.ts
3815
- /**
3816
- * Plot a category-axis chart family (area / bar / bar3d / line / radar) into a
3817
- * `<c:xxxChart>` element. These share the grouping / series / cat+val axis structure.
3818
- */
3819
- function makeCatAxisPlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
3820
- let colorIndex = -1;
3821
- let strXml = "";
3822
- strXml += `<c:${chartType}Chart>`;
4109
+ /** The chart types drawn as a line (a marker per point, an optional smooth) rather than as a body. */
4110
+ const isLineLike = (chartType) => chartType === "line" || chartType === "radar";
4111
+ /** The two bar families, which share `<c:barDir>`, `<c:invertIfNegative>` and a gap width. */
4112
+ const isBarLike = (chartType) => chartType === "bar" || chartType === "bar3D";
4113
+ /** The type-gated children that open a plot element, before `<c:varyColors>`. */
4114
+ function plotGrouping(chartType, opts) {
3823
4115
  if (chartType === "area" || chartType === "line") {
3824
4116
  const lineGrouping = opts.barGrouping === "stacked" || opts.barGrouping === "percentStacked" ? opts.barGrouping : "standard";
3825
- strXml += "<c:grouping val=\"" + lineGrouping + "\"/>";
3826
- }
3827
- if (chartType === "bar" || chartType === "bar3D") {
3828
- strXml += "<c:barDir val=\"" + opts.barDir + "\"/>";
3829
- strXml += "<c:grouping val=\"" + (opts.barGrouping || "clustered") + "\"/>";
4117
+ return voidEl("c:grouping", { val: lineGrouping });
3830
4118
  }
4119
+ if (isBarLike(chartType)) return voidEl("c:barDir", { val: opts.barDir }) + voidEl("c:grouping", { val: opts.barGrouping || "clustered" });
3831
4120
  if (chartType === "radar") {
3832
4121
  const radarStyleWire = {
3833
4122
  radar: "standard",
3834
4123
  markers: "marker",
3835
4124
  filled: "filled"
3836
4125
  }[opts.radarStyle || "radar"] ?? "standard";
3837
- strXml += "<c:radarStyle val=\"" + radarStyleWire + "\"/>";
3838
- }
3839
- strXml += "<c:varyColors val=\"0\"/>";
3840
- data.forEach((obj) => {
3841
- colorIndex++;
3842
- strXml += "<c:ser>";
3843
- strXml += ` <c:idx val="${obj._dataIndex}"/><c:order val="${obj._dataIndex}"/>`;
3844
- strXml += " <c:tx>";
3845
- strXml += " <c:strRef>";
3846
- strXml += ` <c:f>${sheetCellRef(obj._dataIndex + dataLabels(obj).length + 1, 1)}</c:f>`;
3847
- strXml += " <c:strCache><c:ptCount val=\"1\"/><c:pt idx=\"0\">" + el("c:v", null, obj.name ?? "") + "</c:pt></c:strCache>";
3848
- strXml += " </c:strRef>";
3849
- strXml += " </c:tx>";
3850
- const chartColors = resolveChartPalette(opts);
3851
- const seriesOverride = opts.seriesOptions?.[obj._dataIndex];
3852
- const seriesColor = seriesOverride?.color ?? paletteColor(chartColors, colorIndex);
3853
- strXml += " <c:spPr>";
3854
- if (seriesColor === "transparent") strXml += "<a:noFill/>";
3855
- else if (opts.chartColorsOpacity) strXml += "<a:solidFill>" + createColorElement(seriesColor, `<a:alpha val="${Math.round(opts.chartColorsOpacity * FIXED_PCT_PER_PERCENT)}"/>`) + "</a:solidFill>";
3856
- else strXml += genXmlColorSelection(seriesColor);
3857
- if (chartType === "line" || chartType === "radar") {
3858
- const effectiveLineSize = seriesOverride?.lineSize ?? opts.lineSize ?? 2;
3859
- if (effectiveLineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
3860
- else {
3861
- strXml += `<a:ln w="${ptsToEmuLenient(effectiveLineSize)}" cap="${createLineCap(opts.lineCap)}">${chartColorLineFill(seriesColor)}`;
3862
- strXml += voidEl("a:prstDash", { val: opts.lineDashValues?.[colorIndex] ?? opts.lineDash ?? "solid" }) + voidEl("a:round") + "</a:ln>";
3863
- }
3864
- } else if (opts.dataBorder) strXml += `<a:ln w="${ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75))}" cap="${createLineCap(opts.lineCap)}">${genXmlColorSelection({
4126
+ return voidEl("c:radarStyle", { val: radarStyleWire });
4127
+ }
4128
+ return "";
4129
+ }
4130
+ /** Fill, outline and shadow for one series, from its resolved colour and the line/border options. */
4131
+ function serShapeProps(chartType, opts, seriesColor, lineSize, serIndex) {
4132
+ const fill = seriesColor === "transparent" ? voidEl("a:noFill") : opts.chartColorsOpacity ? el("a:solidFill", null, raw(createColorElement(seriesColor, voidEl("a:alpha", { val: Math.round(opts.chartColorsOpacity * FIXED_PCT_PER_PERCENT) })))) : genXmlColorSelection(seriesColor);
4133
+ let line = "";
4134
+ if (isLineLike(chartType)) {
4135
+ const effectiveLineSize = lineSize ?? opts.lineSize ?? 2;
4136
+ line = effectiveLineSize === 0 ? el("a:ln", null, raw(voidEl("a:noFill"))) : el("a:ln", {
4137
+ w: ptsToEmuLenient(effectiveLineSize),
4138
+ cap: createLineCap(opts.lineCap)
4139
+ }, [
4140
+ raw(chartColorLineFill(seriesColor)),
4141
+ raw(voidEl("a:prstDash", { val: opts.lineDashValues?.[serIndex] ?? opts.lineDash ?? "solid" })),
4142
+ raw(voidEl("a:round"))
4143
+ ]);
4144
+ } else if (opts.dataBorder) line = el("a:ln", {
4145
+ w: ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75)),
4146
+ cap: createLineCap(opts.lineCap)
4147
+ }, [
4148
+ raw(genXmlColorSelection({
3865
4149
  color: opts.dataBorder.color ?? "363636",
3866
4150
  transparency: opts.dataBorder.transparency
3867
- })}<a:prstDash val="solid"/><a:round/></a:ln>`;
3868
- strXml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
3869
- strXml += " </c:spPr>";
3870
- if (chartType === "bar" || chartType === "bar3D") strXml += " <c:invertIfNegative val=\"0\"/>";
3871
- if (chartType === "line" || chartType === "radar") {
3872
- strXml += "<c:marker>";
3873
- strXml += " <c:symbol val=\"" + opts.lineDataSymbol + "\"/>";
3874
- if (opts.lineDataSymbolSize) strXml += `<c:size val="${opts.lineDataSymbolSize}"/>`;
3875
- strXml += " <c:spPr>";
3876
- {
3877
- const markerColor = paletteColor(chartColors, obj._dataIndex);
3878
- strXml += markerColor === "transparent" ? "<a:noFill/>" : genXmlColorSelection(markerColor);
3879
- }
3880
- strXml += ` <a:ln w="${opts.lineDataSymbolLineSize}" cap="flat">${chartColorLineFill(opts.lineDataSymbolLineColor || seriesColor)}<a:prstDash val="solid"/><a:round/></a:ln>`;
3881
- strXml += " <a:effectLst/>";
3882
- strXml += " </c:spPr>";
3883
- strXml += "</c:marker>";
3884
- }
3885
- {
3886
- const barVaryColors = (chartType === "bar" || chartType === "bar3D") && data.length === 1 && (opts.chartColors && opts.chartColors !== BARCHART_COLORS && opts.chartColors.length > 1 || opts.invertedColors?.length) ? opts.chartColors || BARCHART_COLORS : null;
3887
- strXml += makeSeriesDataPointsXml(chartType, obj, opts, barVaryColors);
3888
- }
3889
- if (chartType !== "radar") {
3890
- const lblColor = seriesOverride?.dataLabelColor ?? opts.dataLabelColor ?? "000000";
3891
- const lblBold = seriesOverride?.dataLabelFontBold ?? opts.dataLabelFontBold ?? false;
3892
- const lblItalic = seriesOverride?.dataLabelFontItalic ?? opts.dataLabelFontItalic ?? false;
3893
- const lblSize = seriesOverride?.dataLabelFontSize ?? opts.dataLabelFontSize ?? 12;
3894
- const lblFace = seriesOverride?.dataLabelFontFace ?? opts.dataLabelFontFace ?? "Arial";
3895
- const lblFmtCode = seriesOverride?.dataLabelFormatCode ?? opts.dataLabelFormatCode;
3896
- strXml += "<c:dLbls>";
3897
- if (obj.customLabels?.length) obj.customLabels.forEach((lbl, idx) => {
3898
- if (lbl) strXml += makeCustomDLblXml(idx, lbl, opts);
3899
- });
3900
- strXml += voidEl("c:numFmt", {
3901
- formatCode: (lblFmtCode ?? "") || "General",
3902
- sourceLinked: 0
3903
- });
3904
- if (opts.dataLabelBkgrdColors) strXml += `<c:spPr>${genXmlColorSelection(seriesColor)}</c:spPr>`;
3905
- strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
3906
- strXml += `<a:defRPr b="${lblBold ? 1 : 0}" i="${lblItalic ? 1 : 0}" strike="noStrike" sz="${ptToHundredths(lblSize)}" u="none">`;
3907
- strXml += genXmlColorSelection(lblColor);
3908
- strXml += createChartTextFonts(lblFace);
3909
- strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
3910
- if (opts.dataLabelPosition) strXml += voidEl("c:dLblPos", { val: opts.dataLabelPosition });
3911
- strXml += "<c:showLegendKey val=\"0\"/>";
3912
- strXml += `<c:showVal val="${opts.showValue ? "1" : "0"}"/>`;
3913
- strXml += `<c:showCatName val="0"/><c:showSerName val="${opts.showSerName ? "1" : "0"}"/><c:showPercent val="0"/><c:showBubbleSize val="0"/>`;
3914
- strXml += `<c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
3915
- strXml += "</c:dLbls>";
3916
- }
3917
- if (chartType !== "radar") strXml += makeChartErrorBarsXml(chartType, obj.errorBars, obj);
3918
- strXml += "<c:cat>";
3919
- if (opts.catLabelFormatCode) {
3920
- strXml += " <c:numRef>";
3921
- strXml += ` <c:f>Sheet1!$A$2:$A$${firstLabelGroup(obj).length + 1}</c:f>`;
3922
- strXml += " <c:numCache>";
3923
- strXml += " <c:formatCode>" + (opts.catLabelFormatCode || "General") + "</c:formatCode>";
3924
- strXml += ` <c:ptCount val="${firstLabelGroup(obj).length}"/>`;
3925
- firstLabelGroup(obj).forEach((label, idx) => strXml += `<c:pt idx="${idx}">${el("c:v", null, label)}</c:pt>`);
3926
- strXml += " </c:numCache>";
3927
- strXml += " </c:numRef>";
3928
- } else if (dataLabels(obj).length === 1) {
3929
- strXml += " <c:strRef>";
3930
- strXml += ` <c:f>Sheet1!$A$2:$A$${firstLabelGroup(obj).length + 1}</c:f>`;
3931
- strXml += " <c:strCache>";
3932
- strXml += ` <c:ptCount val="${firstLabelGroup(obj).length}"/>`;
3933
- firstLabelGroup(obj).forEach((label, idx) => strXml += `<c:pt idx="${idx}">${el("c:v", null, label)}</c:pt>`);
3934
- strXml += " </c:strCache>";
3935
- strXml += " </c:strRef>";
3936
- } else {
3937
- strXml += " <c:multiLvlStrRef>";
3938
- strXml += ` <c:f>${sheetRangeRef(1, 2, dataLabels(obj).length, firstLabelGroup(obj).length + 1)}</c:f>`;
3939
- strXml += " <c:multiLvlStrCache>";
3940
- strXml += ` <c:ptCount val="${firstLabelGroup(obj).length}"/>`;
3941
- dataLabels(obj).forEach((labelsGroup) => {
3942
- strXml += "<c:lvl>";
3943
- labelsGroup.forEach((label, idx) => strXml += `<c:pt idx="${idx}">${el("c:v", null, label)}</c:pt>`);
3944
- strXml += "</c:lvl>";
3945
- });
3946
- strXml += " </c:multiLvlStrCache>";
3947
- strXml += " </c:multiLvlStrRef>";
3948
- }
3949
- strXml += "</c:cat>";
3950
- strXml += "<c:val>";
3951
- strXml += " <c:numRef>";
3952
- strXml += `<c:f>${sheetRangeRef(obj._dataIndex + dataLabels(obj).length + 1, 2, obj._dataIndex + dataLabels(obj).length + 1, firstLabelGroup(obj).length + 1)}</c:f>`;
3953
- strXml += " <c:numCache>";
3954
- strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
3955
- strXml += ` <c:ptCount val="${firstLabelGroup(obj).length}"/>`;
3956
- dataValues(obj).forEach((value, idx) => {
3957
- strXml += numCachePt(idx, value);
3958
- });
3959
- strXml += " </c:numCache>";
3960
- strXml += " </c:numRef>";
3961
- strXml += "</c:val>";
3962
- if (chartType === "line") strXml += "<c:smooth val=\"" + (opts.lineSmooth ? "1" : "0") + "\"/>";
3963
- strXml += "</c:ser>";
4151
+ })),
4152
+ raw(voidEl("a:prstDash", { val: "solid" })),
4153
+ raw(voidEl("a:round"))
4154
+ ]);
4155
+ return el("c:spPr", null, [
4156
+ raw(fill),
4157
+ raw(line),
4158
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4159
+ ], {
4160
+ openPrefix: " ",
4161
+ closePrefix: " "
3964
4162
  });
3965
- strXml += chartDataLabels(opts, true);
3966
- if (chartType === "bar") {
3967
- strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
3968
- strXml += ` <c:overlap val="${opts.barOverlapPct != null ? opts.barOverlapPct : (opts.barGrouping || "").includes("tacked") ? 100 : 0}"/>`;
3969
- strXml += createSerLinesElement(opts.barSeriesLine);
3970
- } else if (chartType === "bar3D") {
3971
- strXml += ` <c:gapWidth val="${opts.barGapWidthPct}"/>`;
3972
- strXml += ` <c:gapDepth val="${opts.barGapDepthPct}"/>`;
3973
- strXml += " <c:shape val=\"" + opts.bar3DShape + "\"/>";
3974
- } else if (chartType === "line") strXml += " <c:marker val=\"1\"/>";
3975
- strXml += `<c:axId val="${catAxisId}"/><c:axId val="${valAxisId}"/>`;
3976
- if (chartType === "bar3D") strXml += `<c:axId val="${AXIS_ID_SERIES_PRIMARY}"/>`;
3977
- strXml += `</c:${chartType}Chart>`;
3978
- return strXml;
4163
+ }
4164
+ /**
4165
+ * The point marker for a line/radar series. `<c:marker>` must precede `<c:dLbls>` in CT_LineSer
4166
+ * (schema order: spPr → marker → dPt → dLbls).
4167
+ */
4168
+ function serMarker(opts, markerColor, seriesColor) {
4169
+ const spPr = el("c:spPr", null, [
4170
+ raw(markerColor === "transparent" ? voidEl("a:noFill") : genXmlColorSelection(markerColor)),
4171
+ raw(el("a:ln", {
4172
+ w: opts.lineDataSymbolLineSize,
4173
+ cap: "flat"
4174
+ }, [
4175
+ raw(chartColorLineFill(opts.lineDataSymbolLineColor || seriesColor)),
4176
+ raw(voidEl("a:prstDash", { val: "solid" })),
4177
+ raw(voidEl("a:round"))
4178
+ ], { openPrefix: " " })),
4179
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
4180
+ ], {
4181
+ openPrefix: " ",
4182
+ closePrefix: " "
4183
+ });
4184
+ return el("c:marker", null, [
4185
+ raw(voidEl("c:symbol", { val: opts.lineDataSymbol }, { openPrefix: " " })),
4186
+ opts.lineDataSymbolSize ? raw(voidEl("c:size", { val: opts.lineDataSymbolSize })) : null,
4187
+ raw(spPr)
4188
+ ]);
4189
+ }
4190
+ /** Per-series `<c:dLbls>`: number format, an optional label background, text style, show flags. */
4191
+ function serDataLabels(obj, opts, seriesColor) {
4192
+ const over = opts.seriesOptions?.[obj._dataIndex];
4193
+ const defRPr = el("a:defRPr", {
4194
+ b: over?.dataLabelFontBold ?? opts.dataLabelFontBold ?? false ? 1 : 0,
4195
+ i: over?.dataLabelFontItalic ?? opts.dataLabelFontItalic ?? false ? 1 : 0,
4196
+ strike: "noStrike",
4197
+ sz: ptToHundredths(over?.dataLabelFontSize ?? opts.dataLabelFontSize ?? 12),
4198
+ u: "none"
4199
+ }, [raw(genXmlColorSelection(over?.dataLabelColor ?? opts.dataLabelColor ?? "000000")), raw(createChartTextFonts(over?.dataLabelFontFace ?? opts.dataLabelFontFace ?? "Arial"))]);
4200
+ const txPr = el("c:txPr", null, [
4201
+ raw(voidEl("a:bodyPr")),
4202
+ raw(voidEl("a:lstStyle")),
4203
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(defRPr)))))
4204
+ ]);
4205
+ const lblFmtCode = over?.dataLabelFormatCode ?? opts.dataLabelFormatCode;
4206
+ return el("c:dLbls", null, [
4207
+ raw((obj.customLabels ?? []).map((lbl, idx) => lbl ? makeCustomDLblXml(idx, lbl, opts) : "").join("")),
4208
+ raw(voidEl("c:numFmt", {
4209
+ formatCode: (lblFmtCode ?? "") || "General",
4210
+ sourceLinked: 0
4211
+ })),
4212
+ opts.dataLabelBkgrdColors ? raw(el("c:spPr", null, raw(genXmlColorSelection(seriesColor)))) : null,
4213
+ raw(txPr),
4214
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition })) : null,
4215
+ raw(voidEl("c:showLegendKey", { val: 0 })),
4216
+ raw(voidEl("c:showVal", { val: opts.showValue ? 1 : 0 })),
4217
+ raw(voidEl("c:showCatName", { val: 0 })),
4218
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 })),
4219
+ raw(voidEl("c:showPercent", { val: 0 })),
4220
+ raw(voidEl("c:showBubbleSize", { val: 0 })),
4221
+ raw(voidEl("c:showLeaderLines", { val: opts.showLeaderLines ? 1 : 0 }))
4222
+ ]);
4223
+ }
4224
+ /**
4225
+ * The `<c:cat>` category reference. Numeric (date) categories and single-level text ones are
4226
+ * {@link catRefBlock}; multi-level labels nest a `<c:lvl>` per group, which is a different shape
4227
+ * rather than a different spelling of the same one.
4228
+ */
4229
+ function serCategories(obj, opts) {
4230
+ const groups = dataLabels(obj);
4231
+ const cats = firstLabelGroup(obj);
4232
+ const catRef = `Sheet1!$A$2:$A$${cats.length + 1}`;
4233
+ if (opts.catLabelFormatCode) return el("c:cat", null, raw(catRefBlock("num", catRef, cats, opts.catLabelFormatCode || "General", "indented")));
4234
+ if (groups.length === 1) return el("c:cat", null, raw(catRefBlock("str", catRef, cats, void 0, "indented")));
4235
+ const lvls = groups.map((labelsGroup) => el("c:lvl", null, raw(labelsGroup.map((label, idx) => el("c:pt", { idx }, raw(el("c:v", null, label)))).join("")))).join("");
4236
+ const cache = el("c:multiLvlStrCache", null, [raw(voidEl("c:ptCount", { val: cats.length }, { openPrefix: " " })), raw(lvls)], {
4237
+ openPrefix: " ",
4238
+ closePrefix: " "
4239
+ });
4240
+ const ref = el("c:multiLvlStrRef", null, [raw(el("c:f", null, sheetRangeRef(1, 2, groups.length, cats.length + 1), { openPrefix: " " })), raw(cache)], {
4241
+ openPrefix: " ",
4242
+ closePrefix: " "
4243
+ });
4244
+ return el("c:cat", null, raw(ref));
4245
+ }
4246
+ /** The `<c:val>` numeric cache: the series' own sheet column, one point per category. */
4247
+ function serValues(obj, valFmtCode) {
4248
+ const valCol = obj._dataIndex + dataLabels(obj).length + 1;
4249
+ const catCount = firstLabelGroup(obj).length;
4250
+ const numCache = el("c:numCache", null, [
4251
+ raw(el("c:formatCode", null, valFmtCode, { openPrefix: " " })),
4252
+ raw(voidEl("c:ptCount", { val: catCount }, { openPrefix: " " })),
4253
+ raw(dataValues(obj).map((value, idx) => numCachePt(idx, value)).join(""))
4254
+ ], {
4255
+ openPrefix: " ",
4256
+ closePrefix: " "
4257
+ });
4258
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, sheetRangeRef(valCol, 2, valCol, catCount + 1))), raw(numCache)], {
4259
+ openPrefix: " ",
4260
+ closePrefix: " "
4261
+ });
4262
+ return el("c:val", null, raw(numRef));
4263
+ }
4264
+ /**
4265
+ * Plot a category-axis chart family (area / bar / bar3d / line / radar) into a
4266
+ * `<c:xxxChart>` element. These share the grouping / series / cat+val axis structure.
4267
+ */
4268
+ function makeCatAxisPlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
4269
+ const chartColors = resolveChartPalette(opts);
4270
+ const barVaryColors = isBarLike(chartType) && data.length === 1 && (opts.chartColors && opts.chartColors !== BARCHART_COLORS && opts.chartColors.length > 1 || opts.invertedColors?.length) ? opts.chartColors || BARCHART_COLORS : null;
4271
+ const sers = data.map((obj, serIndex) => {
4272
+ const seriesOverride = opts.seriesOptions?.[obj._dataIndex];
4273
+ const seriesColor = seriesOverride?.color ?? paletteColor(chartColors, serIndex);
4274
+ return el("c:ser", null, [
4275
+ raw(voidEl("c:idx", { val: obj._dataIndex }, { openPrefix: " " })),
4276
+ raw(voidEl("c:order", { val: obj._dataIndex })),
4277
+ raw(strRefBlock(sheetCellRef(obj._dataIndex + dataLabels(obj).length + 1, 1), obj.name ?? "")),
4278
+ raw(serShapeProps(chartType, opts, seriesColor, seriesOverride?.lineSize, serIndex)),
4279
+ isBarLike(chartType) ? raw(voidEl("c:invertIfNegative", { val: 0 }, { openPrefix: " " })) : null,
4280
+ isLineLike(chartType) ? raw(serMarker(opts, paletteColor(chartColors, obj._dataIndex), seriesColor)) : null,
4281
+ raw(makeSeriesDataPointsXml(chartType, obj, opts, barVaryColors)),
4282
+ chartType === "radar" ? null : raw(serDataLabels(obj, opts, seriesColor)),
4283
+ chartType === "radar" ? null : raw(makeChartErrorBarsXml(chartType, obj.errorBars, obj)),
4284
+ raw(serCategories(obj, opts)),
4285
+ raw(serValues(obj, valFmtCode)),
4286
+ chartType === "line" ? raw(voidEl("c:smooth", { val: opts.lineSmooth ? 1 : 0 })) : null
4287
+ ]);
4288
+ }).join("");
4289
+ let plotOptions = "";
4290
+ if (chartType === "bar") plotOptions = voidEl("c:gapWidth", { val: opts.barGapWidthPct }, { openPrefix: " " }) + voidEl("c:overlap", { val: opts.barOverlapPct ?? ((opts.barGrouping || "").includes("tacked") ? 100 : 0) }, { openPrefix: " " }) + createSerLinesElement(opts.barSeriesLine);
4291
+ else if (chartType === "bar3D") plotOptions = voidEl("c:gapWidth", { val: opts.barGapWidthPct }, { openPrefix: " " }) + voidEl("c:gapDepth", { val: opts.barGapDepthPct }, { openPrefix: " " }) + voidEl("c:shape", { val: opts.bar3DShape }, { openPrefix: " " });
4292
+ else if (chartType === "line") plotOptions = voidEl("c:marker", { val: 1 }, { openPrefix: " " });
4293
+ return el(`c:${chartType}Chart`, null, [
4294
+ raw(plotGrouping(chartType, opts)),
4295
+ raw(voidEl("c:varyColors", { val: 0 })),
4296
+ raw(sers),
4297
+ raw(chartDataLabels(opts, true)),
4298
+ raw(plotOptions),
4299
+ raw(voidEl("c:axId", { val: catAxisId })),
4300
+ raw(voidEl("c:axId", { val: valAxisId })),
4301
+ chartType === "bar3D" ? raw(voidEl("c:axId", { val: AXIS_ID_SERIES_PRIMARY })) : null
4302
+ ]);
3979
4303
  }
3980
4304
  //#endregion
3981
4305
  //#region src/gen/chart/plot-scatter.ts
4306
+ /** The font attributes a scatter label's `<a:defRPr>` and `<a:rPr>` both carry, in schema order. */
4307
+ function labelFontAttrs(opts) {
4308
+ return {
4309
+ sz: ptToHundredths(opts.dataLabelFontSize || 12),
4310
+ b: opts.dataLabelFontBold ? 1 : 0,
4311
+ i: opts.dataLabelFontItalic ? 1 : 0,
4312
+ u: "none",
4313
+ strike: "noStrike"
4314
+ };
4315
+ }
4316
+ /** The colour and typeface children of those run properties; `indent` is theirs, not the parent's. */
4317
+ function labelFontChildren(opts, indent) {
4318
+ return [raw(indent + el("a:solidFill", null, raw(createColorElement(opts.dataLabelColor || "000000")))), raw(indent + createChartTextFonts(opts.dataLabelFontFace || "Arial"))];
4319
+ }
4320
+ /**
4321
+ * The `(x, y)` runs appended to a `customXY` label: a literal `" ("`, an `XVALUE` field, a
4322
+ * `", "`, a `YVALUE` field and a `")"`.
4323
+ *
4324
+ * Each `<a:fld>` id is minted per build. A field id has to be unique, which is a property a
4325
+ * derived id would have to reproduce without an oracle for how far that uniqueness has to
4326
+ * reach — so the ids stay random and the *comparison* gives: `NORMALIZERS` in
4327
+ * `scripts/pptx-parts.mjs` erases exactly these two field types before the byte-identity gate
4328
+ * diffs a part. Do not "fix" the nondeterminism here.
4329
+ */
4330
+ function customXYRuns(obj, opts) {
4331
+ const lang = opts.lang || "en-US";
4332
+ const literal = (text) => el("a:r", null, [raw(voidEl("a:rPr", {
4333
+ lang,
4334
+ baseline: 0,
4335
+ dirty: 0
4336
+ }, { openPrefix: " " })), raw(el("a:t", null, text, { openPrefix: " " }))], {
4337
+ openPrefix: " ",
4338
+ closePrefix: " "
4339
+ });
4340
+ const field = (type, text) => el("a:fld", {
4341
+ id: `{${getUuid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")}}`,
4342
+ type
4343
+ }, [
4344
+ raw(voidEl("a:rPr", {
4345
+ lang,
4346
+ baseline: 0
4347
+ }, { openPrefix: " " })),
4348
+ raw(el("a:pPr", null, raw(voidEl("a:defRPr", null, { openPrefix: " " })), {
4349
+ openPrefix: " ",
4350
+ closePrefix: " "
4351
+ })),
4352
+ raw(" " + el("a:t", null, text))
4353
+ ], {
4354
+ openPrefix: " ",
4355
+ closePrefix: " "
4356
+ });
4357
+ return literal(" (") + field("XVALUE", "[" + (obj.name ?? "")) + literal(", ") + field("YVALUE", "[" + (obj.name ?? "") + "]") + literal(")") + voidEl("a:endParaRPr", {
4358
+ lang,
4359
+ dirty: 0
4360
+ }, { openPrefix: " " });
4361
+ }
4362
+ /**
4363
+ * One per-point `<c:dLbl>` for the `custom` / `customXY` label formats: rich text carrying the
4364
+ * caller's label, optionally followed by the X/Y value fields.
4365
+ */
4366
+ function scatterCustomLabel(obj, opts, label, idx, chartUuid) {
4367
+ const rich = el("c:rich", null, [
4368
+ raw(el("a:bodyPr", null, raw(voidEl("a:spAutoFit", null, { openPrefix: " " })), {
4369
+ openPrefix: " ",
4370
+ closePrefix: " "
4371
+ })),
4372
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4373
+ raw(el("a:p", null, [
4374
+ raw(el("a:pPr", null, raw(el("a:defRPr", labelFontAttrs(opts), labelFontChildren(opts, " "), {
4375
+ openPrefix: " ",
4376
+ closePrefix: " "
4377
+ })), {
4378
+ openPrefix: " ",
4379
+ closePrefix: " "
4380
+ })),
4381
+ raw(el("a:r", null, [raw(el("a:rPr", {
4382
+ lang: opts.lang || "en-US",
4383
+ ...labelFontAttrs(opts),
4384
+ dirty: 0
4385
+ }, labelFontChildren(opts, " "), {
4386
+ openPrefix: " ",
4387
+ closePrefix: " "
4388
+ })), raw(" " + el("a:t", null, label))], {
4389
+ openPrefix: " ",
4390
+ closePrefix: " "
4391
+ })),
4392
+ opts.dataLabelFormatScatter === "customXY" && !/^ *$/.test(label) ? raw(customXYRuns(obj, opts)) : null
4393
+ ], {
4394
+ openPrefix: " ",
4395
+ closePrefix: " "
4396
+ }))
4397
+ ], {
4398
+ openPrefix: " ",
4399
+ closePrefix: " "
4400
+ });
4401
+ const spPr = el("c:spPr", null, [
4402
+ raw(voidEl("a:noFill", null, { openPrefix: " " })),
4403
+ raw(el("a:ln", null, raw(voidEl("a:noFill", null, { openPrefix: " " })), {
4404
+ openPrefix: " ",
4405
+ closePrefix: " "
4406
+ })),
4407
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
4408
+ ], {
4409
+ openPrefix: " ",
4410
+ closePrefix: " "
4411
+ });
4412
+ const extLst = el("c:extLst", null, [raw(voidEl("c:ext", {
4413
+ uri: "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}",
4414
+ "xmlns:c15": "http://schemas.microsoft.com/office/drawing/2012/chart"
4415
+ }, { openPrefix: " " })), raw(el("c:ext", {
4416
+ uri: "{C3380CC4-5D6E-409C-BE32-E72D297353CC}",
4417
+ "xmlns:c16": "http://schemas.microsoft.com/office/drawing/2014/chart"
4418
+ }, raw(voidEl("c16:uniqueId", { val: `{${String(idx + 1).padStart(8, "0")}${chartUuid}}` }, { openPrefix: " " })), {
4419
+ openPrefix: " ",
4420
+ closePrefix: " "
4421
+ }))], {
4422
+ openPrefix: " ",
4423
+ closePrefix: " "
4424
+ });
4425
+ return el("c:dLbl", null, [
4426
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4427
+ raw(el("c:tx", null, raw(rich), {
4428
+ openPrefix: " ",
4429
+ closePrefix: " "
4430
+ })),
4431
+ raw(spPr),
4432
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition }, { openPrefix: " " })) : null,
4433
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4434
+ raw(voidEl("c:showVal", { val: 0 }, { openPrefix: " " })),
4435
+ raw(voidEl("c:showCatName", { val: 0 }, { openPrefix: " " })),
4436
+ raw(voidEl("c:showSerName", { val: 0 }, { openPrefix: " " })),
4437
+ raw(voidEl("c:showPercent", { val: 0 }, { openPrefix: " " })),
4438
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " })),
4439
+ raw(voidEl("c:showLeaderLines", { val: 1 }, { openPrefix: " " })),
4440
+ raw(extLst)
4441
+ ], { openPrefix: " " });
4442
+ }
4443
+ /** The single chart-level `<c:dLbls>` of the `XY` label format: PowerPoint composes the text. */
4444
+ function scatterXYLabels(opts) {
4445
+ const spPr = el("c:spPr", null, [
4446
+ raw(voidEl("a:noFill", null, { openPrefix: " " })),
4447
+ raw(el("a:ln", null, raw(voidEl("a:noFill", null, { openPrefix: " " })), {
4448
+ openPrefix: " ",
4449
+ closePrefix: " "
4450
+ })),
4451
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
4452
+ ], {
4453
+ openPrefix: " ",
4454
+ closePrefix: " "
4455
+ });
4456
+ const txPr = el("c:txPr", null, [
4457
+ raw(el("a:bodyPr", null, raw(voidEl("a:spAutoFit", null, { openPrefix: " " })), {
4458
+ openPrefix: " ",
4459
+ closePrefix: " "
4460
+ })),
4461
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4462
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(el("a:defRPr", labelFontAttrs(opts), labelFontChildren(opts, " "), {
4463
+ openPrefix: " ",
4464
+ closePrefix: " "
4465
+ })), {
4466
+ openPrefix: " ",
4467
+ closePrefix: " "
4468
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
4469
+ openPrefix: " ",
4470
+ closePrefix: " "
4471
+ }))
4472
+ ], {
4473
+ openPrefix: " ",
4474
+ closePrefix: " "
4475
+ });
4476
+ const extLst = el("c:extLst", null, raw(el("c:ext", {
4477
+ uri: "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}",
4478
+ "xmlns:c15": "http://schemas.microsoft.com/office/drawing/2012/chart"
4479
+ }, raw(voidEl("c15:showLeaderLines", { val: 1 }, { openPrefix: " " })), {
4480
+ openPrefix: " ",
4481
+ closePrefix: " "
4482
+ })), {
4483
+ openPrefix: " ",
4484
+ closePrefix: " "
4485
+ });
4486
+ return el("c:dLbls", null, [
4487
+ raw(spPr),
4488
+ raw(txPr),
4489
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition }, { openPrefix: " " })) : null,
4490
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4491
+ raw(voidEl("c:showVal", { val: opts.showLabel ? 1 : 0 }, { openPrefix: " " })),
4492
+ raw(voidEl("c:showCatName", { val: opts.showLabel ? 1 : 0 }, { openPrefix: " " })),
4493
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 }, { openPrefix: " " })),
4494
+ raw(voidEl("c:showPercent", { val: 0 }, { openPrefix: " " })),
4495
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " })),
4496
+ raw(extLst)
4497
+ ]);
4498
+ }
4499
+ /** Fill, outline and shadow for one scatter series. */
4500
+ function scatterSerShapeProps(opts, serColor, serIndex) {
4501
+ const fill = serColor === "transparent" ? voidEl("a:noFill") : opts.chartColorsOpacity ? el("a:solidFill", null, raw(createColorElement(serColor, voidEl("a:alpha", { val: Math.round(opts.chartColorsOpacity * FIXED_PCT_PER_PERCENT) })))) : genXmlColorSelection(serColor);
4502
+ const line = opts.lineSize === 0 ? el("a:ln", null, raw(voidEl("a:noFill"))) : el("a:ln", {
4503
+ w: ptsToEmuLenient(opts.lineSize ?? 2),
4504
+ cap: createLineCap(opts.lineCap)
4505
+ }, [
4506
+ raw(chartColorLineFill(serColor)),
4507
+ raw(voidEl("a:prstDash", { val: opts.lineDashValues?.[serIndex] ?? opts.lineDash ?? "solid" })),
4508
+ raw(voidEl("a:round"))
4509
+ ]);
4510
+ return el("c:spPr", null, [
4511
+ raw(fill),
4512
+ raw(line),
4513
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4514
+ ], {
4515
+ openPrefix: " ",
4516
+ closePrefix: " "
4517
+ });
4518
+ }
4519
+ /** The point marker: symbol, optional size, and its own fill and outline. */
4520
+ function scatterMarker(opts, markerColor) {
4521
+ const spPr = el("c:spPr", null, [
4522
+ raw(markerColor === "transparent" ? voidEl("a:noFill") : genXmlColorSelection(markerColor)),
4523
+ raw(el("a:ln", {
4524
+ w: opts.lineDataSymbolLineSize,
4525
+ cap: "flat"
4526
+ }, [
4527
+ raw(chartColorLineFill(opts.lineDataSymbolLineColor || markerColor)),
4528
+ raw(voidEl("a:prstDash", { val: "solid" })),
4529
+ raw(voidEl("a:round"))
4530
+ ])),
4531
+ raw(voidEl("a:effectLst"))
4532
+ ]);
4533
+ return el("c:marker", null, [
4534
+ raw(voidEl("c:symbol", { val: opts.lineDataSymbol }, { openPrefix: " " })),
4535
+ opts.lineDataSymbolSize ? raw(voidEl("c:size", { val: opts.lineDataSymbolSize })) : null,
4536
+ raw(spPr)
4537
+ ]);
4538
+ }
3982
4539
  /**
3983
4540
  * Plot an XY scatter chart into `<c:scatterChart>` (paired X/Y numeric series).
3984
4541
  */
3985
4542
  function makeScatterPlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
3986
- let colorIndex = -1;
3987
- let strXml = "";
3988
- strXml += "<c:" + chartType + "Chart>";
3989
- strXml += "<c:scatterStyle val=\"lineMarker\"/>";
3990
- strXml += "<c:varyColors val=\"0\"/>";
3991
- colorIndex = -1;
3992
- data.filter((_obj, idx) => idx > 0).forEach((obj, idx) => {
3993
- colorIndex++;
3994
- const chartColors = resolveChartPalette(opts);
3995
- strXml += "<c:ser>";
3996
- strXml += ` <c:idx val="${idx}"/>`;
3997
- strXml += ` <c:order val="${idx}"/>`;
3998
- strXml += " <c:tx>";
3999
- strXml += " <c:strRef>";
4000
- strXml += ` <c:f>${sheetCellRef(idx + 2, 1)}</c:f>`;
4001
- strXml += " <c:strCache><c:ptCount val=\"1\"/><c:pt idx=\"0\">" + el("c:v", null, obj.name ?? "") + "</c:pt></c:strCache>";
4002
- strXml += " </c:strRef>";
4003
- strXml += " </c:tx>";
4004
- strXml += " <c:spPr>";
4005
- {
4006
- const tmpSerColor = paletteColor(chartColors, colorIndex);
4007
- if (tmpSerColor === "transparent") strXml += "<a:noFill/>";
4008
- else if (opts.chartColorsOpacity) strXml += "<a:solidFill>" + createColorElement(tmpSerColor, "<a:alpha val=\"" + Math.round(opts.chartColorsOpacity * FIXED_PCT_PER_PERCENT).toString() + "\"/>") + "</a:solidFill>";
4009
- else strXml += genXmlColorSelection(tmpSerColor);
4010
- if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
4011
- else {
4012
- strXml += `<a:ln w="${ptsToEmuLenient(opts.lineSize ?? 2)}" cap="${createLineCap(opts.lineCap)}">${chartColorLineFill(tmpSerColor)}`;
4013
- strXml += voidEl("a:prstDash", { val: opts.lineDashValues?.[colorIndex] ?? opts.lineDash ?? "solid" }) + voidEl("a:round") + "</a:ln>";
4014
- }
4015
- strXml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
4016
- }
4017
- strXml += " </c:spPr>";
4018
- strXml += "<c:marker>";
4019
- strXml += " <c:symbol val=\"" + opts.lineDataSymbol + "\"/>";
4020
- if (opts.lineDataSymbolSize) strXml += `<c:size val="${opts.lineDataSymbolSize}"/>`;
4021
- strXml += "<c:spPr>";
4022
- {
4023
- const markerColor = paletteColor(chartColors, idx);
4024
- strXml += markerColor === "transparent" ? "<a:noFill/>" : genXmlColorSelection(markerColor);
4025
- }
4026
- strXml += `<a:ln w="${opts.lineDataSymbolLineSize}" cap="flat">${chartColorLineFill(opts.lineDataSymbolLineColor || paletteColor(chartColors, colorIndex))}<a:prstDash val="solid"/><a:round/></a:ln>`;
4027
- strXml += "<a:effectLst/>";
4028
- strXml += "</c:spPr>";
4029
- strXml += "</c:marker>";
4030
- {
4031
- const scatterVaryColors = data.length === 1 && opts.chartColors !== BARCHART_COLORS ? opts.chartColors || BARCHART_COLORS : null;
4032
- strXml += makeSeriesDataPointsXml(chartType, obj, opts, scatterVaryColors);
4033
- }
4543
+ const chartColors = resolveChartPalette(opts);
4544
+ const xValues = dataValues(data[0]);
4545
+ const scatterVaryColors = data.length === 1 && opts.chartColors !== BARCHART_COLORS ? opts.chartColors || BARCHART_COLORS : null;
4546
+ const sers = data.slice(1).map((obj, idx) => {
4547
+ const serColor = paletteColor(chartColors, idx);
4548
+ const yValues = dataValues(obj);
4549
+ const values = numRefBlock("c:xVal", `Sheet1!$A$2:$A$${xValues.length + 1}`, valFmtCode, xValues) + numRefBlock("c:yVal", sheetRangeRef(idx + 2, 2, idx + 2, xValues.length + 1), valFmtCode, xValues.map((_value, i) => yValues[i]));
4550
+ let labels = "";
4034
4551
  if (opts.showLabel) {
4035
4552
  const chartUuid = getUuid("-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
4036
- if (dataLabels(obj)[0] && (opts.dataLabelFormatScatter === "custom" || opts.dataLabelFormatScatter === "customXY")) {
4037
- strXml += "<c:dLbls>";
4038
- firstLabelGroup(obj).forEach((label, idx) => {
4039
- if (opts.dataLabelFormatScatter === "custom" || opts.dataLabelFormatScatter === "customXY") {
4040
- strXml += " <c:dLbl>";
4041
- strXml += ` <c:idx val="${idx}"/>`;
4042
- strXml += " <c:tx>";
4043
- strXml += " <c:rich>";
4044
- strXml += " <a:bodyPr>";
4045
- strXml += " <a:spAutoFit/>";
4046
- strXml += " </a:bodyPr>";
4047
- strXml += " <a:lstStyle/>";
4048
- strXml += " <a:p>";
4049
- strXml += " <a:pPr>";
4050
- strXml += ` <a:defRPr sz="${ptToHundredths(opts.dataLabelFontSize || 12)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
4051
- strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
4052
- strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
4053
- strXml += " </a:defRPr>";
4054
- strXml += " </a:pPr>";
4055
- strXml += " <a:r>";
4056
- strXml += ` <a:rPr lang="${encodeXmlEntities(opts.lang || "en-US")}" sz="${ptToHundredths(opts.dataLabelFontSize || 12)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike" dirty="0">`;
4057
- strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
4058
- strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
4059
- strXml += " </a:rPr>";
4060
- strXml += " " + el("a:t", null, label);
4061
- strXml += " </a:r>";
4062
- if (opts.dataLabelFormatScatter === "customXY" && !/^ *$/.test(label)) {
4063
- strXml += " <a:r>";
4064
- strXml += " <a:rPr lang=\"" + (opts.lang || "en-US") + "\" baseline=\"0\" dirty=\"0\"/>";
4065
- strXml += " <a:t> (</a:t>";
4066
- strXml += " </a:r>";
4067
- strXml += " <a:fld id=\"{" + getUuid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") + "}\" type=\"XVALUE\">";
4068
- strXml += " <a:rPr lang=\"" + (opts.lang || "en-US") + "\" baseline=\"0\"/>";
4069
- strXml += " <a:pPr>";
4070
- strXml += " <a:defRPr/>";
4071
- strXml += " </a:pPr>";
4072
- strXml += " " + el("a:t", null, "[" + (obj.name ?? ""));
4073
- strXml += " </a:fld>";
4074
- strXml += " <a:r>";
4075
- strXml += " <a:rPr lang=\"" + (opts.lang || "en-US") + "\" baseline=\"0\" dirty=\"0\"/>";
4076
- strXml += " <a:t>, </a:t>";
4077
- strXml += " </a:r>";
4078
- strXml += " <a:fld id=\"{" + getUuid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") + "}\" type=\"YVALUE\">";
4079
- strXml += " <a:rPr lang=\"" + (opts.lang || "en-US") + "\" baseline=\"0\"/>";
4080
- strXml += " <a:pPr>";
4081
- strXml += " <a:defRPr/>";
4082
- strXml += " </a:pPr>";
4083
- strXml += " " + el("a:t", null, "[" + (obj.name ?? "") + "]");
4084
- strXml += " </a:fld>";
4085
- strXml += " <a:r>";
4086
- strXml += " <a:rPr lang=\"" + (opts.lang || "en-US") + "\" baseline=\"0\" dirty=\"0\"/>";
4087
- strXml += " <a:t>)</a:t>";
4088
- strXml += " </a:r>";
4089
- strXml += " <a:endParaRPr lang=\"" + (opts.lang || "en-US") + "\" dirty=\"0\"/>";
4090
- }
4091
- strXml += " </a:p>";
4092
- strXml += " </c:rich>";
4093
- strXml += " </c:tx>";
4094
- strXml += " <c:spPr>";
4095
- strXml += " <a:noFill/>";
4096
- strXml += " <a:ln>";
4097
- strXml += " <a:noFill/>";
4098
- strXml += " </a:ln>";
4099
- strXml += " <a:effectLst/>";
4100
- strXml += " </c:spPr>";
4101
- if (opts.dataLabelPosition) strXml += " <c:dLblPos val=\"" + opts.dataLabelPosition + "\"/>";
4102
- strXml += " <c:showLegendKey val=\"0\"/>";
4103
- strXml += " <c:showVal val=\"0\"/>";
4104
- strXml += " <c:showCatName val=\"0\"/>";
4105
- strXml += " <c:showSerName val=\"0\"/>";
4106
- strXml += " <c:showPercent val=\"0\"/>";
4107
- strXml += " <c:showBubbleSize val=\"0\"/>";
4108
- strXml += " <c:showLeaderLines val=\"1\"/>";
4109
- strXml += " <c:extLst>";
4110
- strXml += " <c:ext uri=\"{CE6537A1-D6FC-4f65-9D91-7224C49458BB}\" xmlns:c15=\"http://schemas.microsoft.com/office/drawing/2012/chart\"/>";
4111
- strXml += " <c:ext uri=\"{C3380CC4-5D6E-409C-BE32-E72D297353CC}\" xmlns:c16=\"http://schemas.microsoft.com/office/drawing/2014/chart\">";
4112
- strXml += ` <c16:uniqueId val="{${String(idx + 1).padStart(8, "0")}${chartUuid}}"/>`;
4113
- strXml += " </c:ext>";
4114
- strXml += " </c:extLst>";
4115
- strXml += "</c:dLbl>";
4116
- }
4117
- });
4118
- strXml += "</c:dLbls>";
4119
- }
4120
- if (opts.dataLabelFormatScatter === "XY") {
4121
- strXml += "<c:dLbls>";
4122
- strXml += " <c:spPr>";
4123
- strXml += " <a:noFill/>";
4124
- strXml += " <a:ln>";
4125
- strXml += " <a:noFill/>";
4126
- strXml += " </a:ln>";
4127
- strXml += " <a:effectLst/>";
4128
- strXml += " </c:spPr>";
4129
- strXml += " <c:txPr>";
4130
- strXml += " <a:bodyPr>";
4131
- strXml += " <a:spAutoFit/>";
4132
- strXml += " </a:bodyPr>";
4133
- strXml += " <a:lstStyle/>";
4134
- strXml += " <a:p>";
4135
- strXml += " <a:pPr>";
4136
- strXml += ` <a:defRPr sz="${ptToHundredths(opts.dataLabelFontSize || 12)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
4137
- strXml += " <a:solidFill>" + createColorElement(opts.dataLabelColor || "000000") + "</a:solidFill>";
4138
- strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
4139
- strXml += " </a:defRPr>";
4140
- strXml += " </a:pPr>";
4141
- strXml += ` <a:endParaRPr lang="${encodeXmlEntities(opts.lang || "en-US")}"/>`;
4142
- strXml += " </a:p>";
4143
- strXml += " </c:txPr>";
4144
- if (opts.dataLabelPosition) strXml += " <c:dLblPos val=\"" + opts.dataLabelPosition + "\"/>";
4145
- strXml += " <c:showLegendKey val=\"0\"/>";
4146
- strXml += ` <c:showVal val="${opts.showLabel ? "1" : "0"}"/>`;
4147
- strXml += ` <c:showCatName val="${opts.showLabel ? "1" : "0"}"/>`;
4148
- strXml += ` <c:showSerName val="${opts.showSerName ? "1" : "0"}"/>`;
4149
- strXml += " <c:showPercent val=\"0\"/>";
4150
- strXml += " <c:showBubbleSize val=\"0\"/>";
4151
- strXml += " <c:extLst>";
4152
- strXml += " <c:ext uri=\"{CE6537A1-D6FC-4f65-9D91-7224C49458BB}\" xmlns:c15=\"http://schemas.microsoft.com/office/drawing/2012/chart\">";
4153
- strXml += " <c15:showLeaderLines val=\"1\"/>";
4154
- strXml += " </c:ext>";
4155
- strXml += " </c:extLst>";
4156
- strXml += "</c:dLbls>";
4157
- }
4553
+ const isCustom = opts.dataLabelFormatScatter === "custom" || opts.dataLabelFormatScatter === "customXY";
4554
+ if (dataLabels(obj)[0] && isCustom) labels += el("c:dLbls", null, raw(firstLabelGroup(obj).map((label, pointIdx) => scatterCustomLabel(obj, opts, label, pointIdx, chartUuid)).join("")));
4555
+ if (opts.dataLabelFormatScatter === "XY") labels += scatterXYLabels(opts);
4158
4556
  }
4159
- strXml += makeChartErrorBarsXml(chartType, obj.errorBars, obj);
4160
- {
4161
- const xValues = dataValues(data[0]);
4162
- const yValues = dataValues(obj);
4163
- strXml += numRefBlock("c:xVal", `Sheet1!$A$2:$A$${xValues.length + 1}`, valFmtCode, xValues);
4164
- strXml += numRefBlock("c:yVal", sheetRangeRef(idx + 2, 2, idx + 2, xValues.length + 1), valFmtCode, xValues.map((_value, i) => yValues[i]));
4165
- }
4166
- strXml += "<c:smooth val=\"" + (opts.lineSmooth ? "1" : "0") + "\"/>";
4167
- strXml += "</c:ser>";
4557
+ return el("c:ser", null, [
4558
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4559
+ raw(voidEl("c:order", { val: idx }, { openPrefix: " " })),
4560
+ raw(strRefBlock(sheetCellRef(idx + 2, 1), obj.name ?? "")),
4561
+ raw(scatterSerShapeProps(opts, serColor, idx)),
4562
+ raw(scatterMarker(opts, serColor)),
4563
+ raw(makeSeriesDataPointsXml(chartType, obj, opts, scatterVaryColors)),
4564
+ raw(labels),
4565
+ raw(makeChartErrorBarsXml(chartType, obj.errorBars, obj)),
4566
+ raw(values),
4567
+ raw(voidEl("c:smooth", { val: opts.lineSmooth ? 1 : 0 }))
4568
+ ]);
4569
+ }).join("");
4570
+ return el(`c:${chartType}Chart`, null, [
4571
+ raw(voidEl("c:scatterStyle", { val: "lineMarker" })),
4572
+ raw(voidEl("c:varyColors", { val: 0 })),
4573
+ raw(sers),
4574
+ raw(chartDataLabels(opts, false)),
4575
+ raw(voidEl("c:axId", { val: catAxisId })),
4576
+ raw(voidEl("c:axId", { val: valAxisId }))
4577
+ ]);
4578
+ }
4579
+ //#endregion
4580
+ //#region src/gen/chart/plot-bubble.ts
4581
+ /** Fill + outline + shadow for one bubble series, from the palette colour and the line options. */
4582
+ function bubbleSerShapeProps(opts, serColor, serIndex) {
4583
+ const fill = serColor === "transparent" ? voidEl("a:noFill") : opts.chartColorsOpacity ? el("a:solidFill", null, raw(createColorElement(serColor, voidEl("a:alpha", { val: Math.round(opts.chartColorsOpacity * FIXED_PCT_PER_PERCENT) })))) : genXmlColorSelection(serColor);
4584
+ const line = opts.lineSize === 0 ? el("a:ln", null, raw(voidEl("a:noFill"))) : opts.dataBorder ? el("a:ln", {
4585
+ w: ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75)),
4586
+ cap: "flat"
4587
+ }, [
4588
+ raw(genXmlColorSelection({
4589
+ color: opts.dataBorder.color ?? "363636",
4590
+ transparency: opts.dataBorder.transparency
4591
+ })),
4592
+ raw(voidEl("a:prstDash", { val: "solid" })),
4593
+ raw(voidEl("a:round"))
4594
+ ]) : el("a:ln", {
4595
+ w: ptsToEmuLenient(opts.lineSize ?? 2),
4596
+ cap: "flat"
4597
+ }, [
4598
+ raw(genXmlColorSelection(serColor)),
4599
+ raw(voidEl("a:prstDash", { val: opts.lineDashValues?.[serIndex] ?? opts.lineDash ?? "solid" })),
4600
+ raw(voidEl("a:round"))
4601
+ ]);
4602
+ return el("c:spPr", null, [
4603
+ raw(fill),
4604
+ raw(line),
4605
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4606
+ ]);
4607
+ }
4608
+ /**
4609
+ * The `<c:bubbleSize>` cache: the per-point sizes that scale each bubble. Its own block rather
4610
+ * than {@link numRefBlock} because the wrapping tag differs and so does the indentation — the
4611
+ * `<c:ptCount>` here sits three spaces further in than anything around it.
4612
+ */
4613
+ function bubbleSizeBlock(obj, ref) {
4614
+ const sizes = dataSizes(obj);
4615
+ const numCache = el("c:numCache", null, [
4616
+ raw(el("c:formatCode", null, "General", { openPrefix: " " })),
4617
+ raw(voidEl("c:ptCount", { val: sizes.length }, { openPrefix: " " })),
4618
+ raw(sizes.map((value, idx) => numCachePt(idx, value)).join(""))
4619
+ ], {
4620
+ openPrefix: " ",
4621
+ closePrefix: " "
4168
4622
  });
4169
- strXml += chartDataLabels(opts, false);
4170
- strXml += `<c:axId val="${catAxisId}"/><c:axId val="${valAxisId}"/>`;
4171
- strXml += "</c:" + chartType + "Chart>";
4172
- return strXml;
4623
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, ref)), raw(numCache)], {
4624
+ openPrefix: " ",
4625
+ closePrefix: " "
4626
+ });
4627
+ return el("c:bubbleSize", null, raw(numRef), {
4628
+ openPrefix: " ",
4629
+ closePrefix: " "
4630
+ });
4631
+ }
4632
+ /** The shared `<c:dLbls>` block: number format, label text style, and which parts are shown. */
4633
+ function bubbleDataLabels(opts) {
4634
+ const defRPr = el("a:defRPr", {
4635
+ b: opts.dataLabelFontBold ? 1 : 0,
4636
+ i: opts.dataLabelFontItalic ? 1 : 0,
4637
+ strike: "noStrike",
4638
+ sz: ptToHundredths(opts.dataLabelFontSize || 12),
4639
+ u: "none"
4640
+ }, [raw(genXmlColorSelection(opts.dataLabelColor || "000000")), raw(createChartTextFonts(opts.dataLabelFontFace || "Arial"))]);
4641
+ const txPr = el("c:txPr", null, [
4642
+ raw(voidEl("a:bodyPr")),
4643
+ raw(voidEl("a:lstStyle")),
4644
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(defRPr)))))
4645
+ ]);
4646
+ const extLst = el("c:extLst", null, raw(el("c:ext", {
4647
+ uri: "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}",
4648
+ "xmlns:c15": "http://schemas.microsoft.com/office/drawing/2012/chart"
4649
+ }, raw(voidEl("c15:showLeaderLines", { val: opts.showLeaderLines ? 1 : 0 }, { openPrefix: " " })), {
4650
+ openPrefix: " ",
4651
+ closePrefix: " "
4652
+ })));
4653
+ return el("c:dLbls", null, [
4654
+ raw(voidEl("c:numFmt", {
4655
+ formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4656
+ sourceLinked: 0
4657
+ })),
4658
+ raw(txPr),
4659
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition })) : null,
4660
+ raw(voidEl("c:showLegendKey", { val: 0 })),
4661
+ raw(voidEl("c:showVal", { val: opts.showValue ? 1 : 0 })),
4662
+ raw(voidEl("c:showCatName", { val: 0 })),
4663
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 })),
4664
+ raw(voidEl("c:showPercent", { val: 0 })),
4665
+ raw(voidEl("c:showBubbleSize", { val: opts.showBubbleSize ? 1 : 0 })),
4666
+ raw(extLst)
4667
+ ]);
4173
4668
  }
4174
- //#endregion
4175
- //#region src/gen/chart/plot-bubble.ts
4176
4669
  /**
4177
4670
  * Plot a bubble / bubble3d chart into `<c:bubbleChart>` (X/Y plus per-point size).
4178
4671
  */
4179
4672
  function makeBubblePlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
4180
- let colorIndex = -1;
4673
+ const chartColors = resolveChartPalette(opts);
4674
+ const xValues = dataValues(data[0]);
4181
4675
  let idxColLtr = 1;
4182
- let strXml = "";
4183
- strXml += "<c:bubbleChart>";
4184
- strXml += "<c:varyColors val=\"0\"/>";
4185
- colorIndex = -1;
4186
- data.filter((_obj, idx) => idx > 0).forEach((obj, idx) => {
4187
- colorIndex++;
4188
- strXml += "<c:ser>";
4189
- strXml += ` <c:idx val="${idx}"/>`;
4190
- strXml += ` <c:order val="${idx}"/>`;
4191
- strXml += " <c:tx>";
4192
- strXml += " <c:strRef>";
4193
- strXml += ` <c:f>${sheetCellRef(idxColLtr + 1, 1)}</c:f>`;
4194
- strXml += " <c:strCache><c:ptCount val=\"1\"/><c:pt idx=\"0\">" + el("c:v", null, obj.name ?? "") + "</c:pt></c:strCache>";
4195
- strXml += " </c:strRef>";
4196
- strXml += " </c:tx>";
4197
- {
4198
- strXml += "<c:spPr>";
4199
- const tmpSerColor = paletteColor(resolveChartPalette(opts), colorIndex);
4200
- if (tmpSerColor === "transparent") strXml += "<a:noFill/>";
4201
- else if (opts.chartColorsOpacity) strXml += `<a:solidFill>${createColorElement(tmpSerColor, "<a:alpha val=\"" + Math.round(opts.chartColorsOpacity * FIXED_PCT_PER_PERCENT).toString() + "\"/>")}</a:solidFill>`;
4202
- else strXml += genXmlColorSelection(tmpSerColor);
4203
- if (opts.lineSize === 0) strXml += "<a:ln><a:noFill/></a:ln>";
4204
- else if (opts.dataBorder) strXml += `<a:ln w="${ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75))}" cap="flat">${genXmlColorSelection({
4205
- color: opts.dataBorder.color ?? "363636",
4206
- transparency: opts.dataBorder.transparency
4207
- })}<a:prstDash val="solid"/><a:round/></a:ln>`;
4208
- else {
4209
- strXml += `<a:ln w="${ptsToEmuLenient(opts.lineSize ?? 2)}" cap="flat">${genXmlColorSelection(tmpSerColor)}`;
4210
- strXml += voidEl("a:prstDash", { val: opts.lineDashValues?.[colorIndex] ?? opts.lineDash ?? "solid" }) + voidEl("a:round") + "</a:ln>";
4211
- }
4212
- strXml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
4213
- strXml += "</c:spPr>";
4214
- }
4215
- {
4216
- const xValues = dataValues(data[0]);
4217
- const yValues = dataValues(obj);
4218
- strXml += numRefBlock("c:xVal", `Sheet1!$A$2:$A$${xValues.length + 1}`, valFmtCode, xValues);
4219
- strXml += numRefBlock("c:yVal", sheetRangeRef(idxColLtr + 1, 2, idxColLtr + 1, xValues.length + 1), valFmtCode, xValues.map((_value, idx) => yValues[idx]), "");
4220
- idxColLtr++;
4221
- }
4222
- strXml += " <c:bubbleSize>";
4223
- strXml += " <c:numRef>";
4224
- strXml += `<c:f>${sheetRangeRef(idxColLtr + 1, 2, idxColLtr + 1, dataSizes(obj).length + 1)}</c:f>`;
4676
+ const sers = data.slice(1).map((obj, idx) => {
4677
+ const name = strRefBlock(sheetCellRef(idxColLtr + 1, 1), obj.name ?? "");
4678
+ const spPr = bubbleSerShapeProps(opts, paletteColor(chartColors, idx), idx);
4679
+ const yValues = dataValues(obj);
4680
+ const xVal = numRefBlock("c:xVal", `Sheet1!$A$2:$A$${xValues.length + 1}`, valFmtCode, xValues);
4681
+ const yVal = numRefBlock("c:yVal", sheetRangeRef(idxColLtr + 1, 2, idxColLtr + 1, xValues.length + 1), valFmtCode, xValues.map((_value, i) => yValues[i]), "");
4225
4682
  idxColLtr++;
4226
- strXml += " <c:numCache>";
4227
- strXml += " <c:formatCode>General</c:formatCode>";
4228
- strXml += ` <c:ptCount val="${dataSizes(obj).length}"/>`;
4229
- dataSizes(obj).forEach((value, idx) => {
4230
- strXml += numCachePt(idx, value);
4231
- });
4232
- strXml += " </c:numCache>";
4233
- strXml += " </c:numRef>";
4234
- strXml += " </c:bubbleSize>";
4235
- strXml += " <c:bubble3D val=\"" + (chartType === "bubble3D" ? "1" : "0") + "\"/>";
4236
- strXml += "</c:ser>";
4237
- });
4238
- strXml += "<c:dLbls>";
4239
- strXml += voidEl("c:numFmt", {
4240
- formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4241
- sourceLinked: 0
4242
- });
4243
- strXml += "<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr>";
4244
- strXml += `<a:defRPr b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" strike="noStrike" sz="${ptToHundredths(opts.dataLabelFontSize || 12)}" u="none">`;
4245
- strXml += genXmlColorSelection(opts.dataLabelColor || "000000");
4246
- strXml += createChartTextFonts(opts.dataLabelFontFace || "Arial");
4247
- strXml += "</a:defRPr></a:pPr></a:p></c:txPr>";
4248
- if (opts.dataLabelPosition) strXml += voidEl("c:dLblPos", { val: opts.dataLabelPosition });
4249
- strXml += "<c:showLegendKey val=\"0\"/>";
4250
- strXml += `<c:showVal val="${opts.showValue ? "1" : "0"}"/>`;
4251
- strXml += `<c:showCatName val="0"/><c:showSerName val="${opts.showSerName ? "1" : "0"}"/><c:showPercent val="0"/><c:showBubbleSize val="${opts.showBubbleSize ? "1" : "0"}"/>`;
4252
- strXml += "<c:extLst>";
4253
- strXml += " <c:ext uri=\"{CE6537A1-D6FC-4f65-9D91-7224C49458BB}\" xmlns:c15=\"http://schemas.microsoft.com/office/drawing/2012/chart\">";
4254
- strXml += " <c15:showLeaderLines val=\"" + (opts.showLeaderLines ? "1" : "0") + "\"/>";
4255
- strXml += " </c:ext>";
4256
- strXml += "</c:extLst>";
4257
- strXml += "</c:dLbls>";
4258
- strXml += `<c:axId val="${catAxisId}"/><c:axId val="${valAxisId}"/>`;
4259
- strXml += "</c:bubbleChart>";
4260
- return strXml;
4683
+ const sizeRef = sheetRangeRef(idxColLtr + 1, 2, idxColLtr + 1, dataSizes(obj).length + 1);
4684
+ idxColLtr++;
4685
+ return el("c:ser", null, [
4686
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4687
+ raw(voidEl("c:order", { val: idx }, { openPrefix: " " })),
4688
+ raw(name),
4689
+ raw(spPr),
4690
+ raw(xVal),
4691
+ raw(yVal),
4692
+ raw(bubbleSizeBlock(obj, sizeRef)),
4693
+ raw(voidEl("c:bubble3D", { val: chartType === "bubble3D" ? 1 : 0 }, { openPrefix: " " }))
4694
+ ]);
4695
+ }).join("");
4696
+ return el("c:bubbleChart", null, [
4697
+ raw(voidEl("c:varyColors", { val: 0 })),
4698
+ raw(sers),
4699
+ raw(bubbleDataLabels(opts)),
4700
+ raw(voidEl("c:axId", { val: catAxisId })),
4701
+ raw(voidEl("c:axId", { val: valAxisId }))
4702
+ ]);
4261
4703
  }
4262
4704
  //#endregion
4263
4705
  //#region src/gen/chart/plot-pie.ts
4264
- /**
4265
- * Plot a single-series pie / doughnut chart into `<c:pieChart>` / `<c:doughnutChart>`.
4266
- */
4267
- function makePiePlot(chartType, data, opts, valFmtCode) {
4268
- let optsChartData;
4269
- let strXml = "";
4270
- {
4271
- const first = data[0];
4272
- if (!first) return strXml;
4273
- optsChartData = first;
4274
- }
4275
- strXml += "<c:" + chartType + "Chart>";
4276
- strXml += " <c:varyColors val=\"1\"/>";
4277
- strXml += "<c:ser>";
4278
- strXml += " <c:idx val=\"0\"/>";
4279
- strXml += " <c:order val=\"0\"/>";
4280
- strXml += " <c:tx>";
4281
- strXml += " <c:strRef>";
4282
- strXml += " <c:f>Sheet1!$B$1</c:f>";
4283
- strXml += " <c:strCache>";
4284
- strXml += " <c:ptCount val=\"1\"/>";
4285
- strXml += " <c:pt idx=\"0\">" + el("c:v", null, optsChartData.name ?? "") + "</c:pt>";
4286
- strXml += " </c:strCache>";
4287
- strXml += " </c:strRef>";
4288
- strXml += " </c:tx>";
4289
- strXml += " <c:spPr>";
4290
- strXml += " <a:solidFill><a:schemeClr val=\"accent1\"/></a:solidFill>";
4291
- strXml += " <a:ln w=\"9525\" cap=\"flat\"><a:solidFill><a:srgbClr val=\"F9F9F9\"/></a:solidFill><a:prstDash val=\"solid\"/><a:round/></a:ln>";
4292
- if (opts.dataNoEffects) strXml += "<a:effectLst/>";
4293
- else strXml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
4294
- strXml += " </c:spPr>";
4295
- firstLabelGroup(optsChartData).forEach((_label, idx) => {
4296
- const chartColors = resolveChartPalette(opts);
4297
- const ptStyle = optsChartData.pointStyles?.[idx];
4298
- strXml += "<c:dPt>";
4299
- strXml += ` <c:idx val="${idx}"/>`;
4300
- strXml += " <c:bubble3D val=\"0\"/>";
4301
- strXml += " <c:spPr>";
4302
- strXml += `<a:solidFill>${createColorElement(ptStyle?.fill || paletteColor(chartColors, idx))}</a:solidFill>`;
4303
- if (ptStyle?.border) strXml += createChartBorderLine(ptStyle.border);
4304
- else if (opts.dataBorder) strXml += `<a:ln w="${ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75))}" cap="flat">${genXmlColorSelection({
4706
+ /** The label run properties both `<c:txPr>` spellings share, differing only in their indentation. */
4707
+ function labelDefRPr(opts, fontsIndent, fmt) {
4708
+ return el("a:defRPr", {
4709
+ sz: ptToHundredths(opts.dataLabelFontSize || 12),
4710
+ b: opts.dataLabelFontBold ? 1 : 0,
4711
+ i: opts.dataLabelFontItalic ? 1 : 0,
4712
+ u: "none",
4713
+ strike: "noStrike"
4714
+ }, [raw(genXmlColorSelection(opts.dataLabelColor || "000000")), raw(fontsIndent + createChartTextFonts(opts.dataLabelFontFace || "Arial"))], fmt);
4715
+ }
4716
+ /** One `<c:dPt>`: the slice's own fill, its border override, and the shared shadow. */
4717
+ function pieDataPoint(idx, ptStyle, opts, fallbackColor) {
4718
+ const border = ptStyle?.border ? createChartBorderLine(ptStyle.border) : opts.dataBorder ? el("a:ln", {
4719
+ w: ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75)),
4720
+ cap: "flat"
4721
+ }, [
4722
+ raw(genXmlColorSelection({
4305
4723
  color: opts.dataBorder.color ?? "363636",
4306
4724
  transparency: opts.dataBorder.transparency
4307
- })}<a:prstDash val="solid"/><a:round/></a:ln>`;
4308
- strXml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
4309
- strXml += " </c:spPr>";
4310
- strXml += "</c:dPt>";
4311
- });
4312
- strXml += "<c:dLbls>";
4313
- firstLabelGroup(optsChartData).forEach((_label, idx) => {
4314
- const customLbl = optsChartData.customLabels?.[idx];
4315
- strXml += "<c:dLbl>";
4316
- strXml += ` <c:idx val="${idx}"/>`;
4317
- if (customLbl) strXml += `<c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="${encodeXmlEntities(opts.lang || "en-US")}" dirty="0"/>` + el("a:t", null, customLbl) + "</a:r></a:p></c:rich></c:tx>";
4318
- strXml += " " + voidEl("c:numFmt", {
4319
- formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4320
- sourceLinked: 0
4321
- });
4322
- strXml += " <c:spPr/><c:txPr>";
4323
- strXml += " <a:bodyPr/><a:lstStyle/>";
4324
- strXml += " <a:p><a:pPr>";
4325
- strXml += ` <a:defRPr sz="${ptToHundredths(opts.dataLabelFontSize || 12)}" b="${opts.dataLabelFontBold ? 1 : 0}" i="${opts.dataLabelFontItalic ? 1 : 0}" u="none" strike="noStrike">`;
4326
- strXml += genXmlColorSelection(opts.dataLabelColor || "000000");
4327
- strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
4328
- strXml += " </a:defRPr>";
4329
- strXml += " </a:pPr></a:p>";
4330
- strXml += " </c:txPr>";
4331
- if (chartType === "pie" && opts.dataLabelPosition) strXml += voidEl("c:dLblPos", { val: opts.dataLabelPosition });
4332
- strXml += " <c:showLegendKey val=\"0\"/>";
4333
- strXml += " <c:showVal val=\"" + (customLbl ? "0" : opts.showValue ? "1" : "0") + "\"/>";
4334
- strXml += " <c:showCatName val=\"" + (opts.showLabel ? "1" : "0") + "\"/>";
4335
- strXml += " <c:showSerName val=\"" + (opts.showSerName ? "1" : "0") + "\"/>";
4336
- strXml += " <c:showPercent val=\"" + (opts.showPercent ? "1" : "0") + "\"/>";
4337
- strXml += " <c:showBubbleSize val=\"0\"/>";
4338
- strXml += " </c:dLbl>";
4339
- });
4340
- strXml += " " + voidEl("c:numFmt", {
4725
+ })),
4726
+ raw(voidEl("a:prstDash", { val: "solid" })),
4727
+ raw(voidEl("a:round"))
4728
+ ]) : "";
4729
+ const spPr = el("c:spPr", null, [
4730
+ raw(el("a:solidFill", null, raw(createColorElement(ptStyle?.fill || fallbackColor)))),
4731
+ raw(border),
4732
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4733
+ ], {
4734
+ openPrefix: " ",
4735
+ closePrefix: " "
4736
+ });
4737
+ return el("c:dPt", null, [
4738
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4739
+ raw(voidEl("c:bubble3D", { val: 0 }, { openPrefix: " " })),
4740
+ raw(spPr)
4741
+ ]);
4742
+ }
4743
+ /**
4744
+ * One `<c:dLbl>`: a pie labels its points, not its series, so every slice carries its own copy of
4745
+ * the number format, text style and show flags. A `customLabels` entry replaces the value with
4746
+ * literal rich text, which is why it also forces `<c:showVal>` off.
4747
+ */
4748
+ function pieDataLabel(idx, customLbl, opts, chartType) {
4749
+ const numFmt = voidEl("c:numFmt", {
4341
4750
  formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4342
4751
  sourceLinked: 0
4752
+ }, { openPrefix: " " });
4753
+ const txPr = el("c:txPr", null, [
4754
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
4755
+ raw(voidEl("a:lstStyle")),
4756
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(labelDefRPr(opts, " ", {
4757
+ openPrefix: " ",
4758
+ closePrefix: " "
4759
+ })), { closePrefix: " " })), { openPrefix: " " }))
4760
+ ], { closePrefix: " " });
4761
+ return el("c:dLbl", null, [
4762
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4763
+ customLbl ? raw(el("c:tx", null, raw(el("c:rich", null, [
4764
+ raw(voidEl("a:bodyPr")),
4765
+ raw(voidEl("a:lstStyle")),
4766
+ raw(el("a:p", null, raw(el("a:r", null, [raw(voidEl("a:rPr", {
4767
+ lang: opts.lang || "en-US",
4768
+ dirty: 0
4769
+ })), raw(el("a:t", null, customLbl))]))))
4770
+ ])))) : null,
4771
+ raw(numFmt),
4772
+ raw(voidEl("c:spPr", null, { openPrefix: " " })),
4773
+ raw(txPr),
4774
+ chartType === "pie" && opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition })) : null,
4775
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4776
+ raw(voidEl("c:showVal", { val: customLbl ? 0 : opts.showValue ? 1 : 0 }, { openPrefix: " " })),
4777
+ raw(voidEl("c:showCatName", { val: opts.showLabel ? 1 : 0 }, { openPrefix: " " })),
4778
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 }, { openPrefix: " " })),
4779
+ raw(voidEl("c:showPercent", { val: opts.showPercent ? 1 : 0 }, { openPrefix: " " })),
4780
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " }))
4781
+ ], { closePrefix: " " });
4782
+ }
4783
+ /** The `<c:cat>` slice-name reference and the `<c:val>` cache, both keyed on the label count. */
4784
+ function pieCategories(labels) {
4785
+ const strCache = el("c:strCache", null, [raw(voidEl("c:ptCount", { val: labels.length }, { openPrefix: " " })), raw(labels.map((label, idx) => el("c:pt", { idx }, raw(el("c:v", null, label)))).join(""))], {
4786
+ openPrefix: " ",
4787
+ closePrefix: " "
4788
+ });
4789
+ const strRef = el("c:strRef", null, [raw(el("c:f", null, `Sheet1!$A$2:$A$${labels.length + 1}`, { openPrefix: " " })), raw(strCache)], {
4790
+ openPrefix: " ",
4791
+ closePrefix: " "
4792
+ });
4793
+ return el("c:cat", null, raw(strRef));
4794
+ }
4795
+ /** The `<c:val>` numeric cache. A missing value keeps its `<c:pt>` with an empty `<c:v>`. */
4796
+ function pieValues(obj, count, valFmtCode) {
4797
+ const points = dataValues(obj).map((value, idx) => el("c:pt", { idx }, raw(el("c:v", null, value || value === 0 ? value : "")))).join("");
4798
+ const numCache = el("c:numCache", null, [
4799
+ raw(el("c:formatCode", null, valFmtCode, { openPrefix: " " })),
4800
+ raw(voidEl("c:ptCount", { val: count }, { openPrefix: " " })),
4801
+ raw(points)
4802
+ ], {
4803
+ openPrefix: " ",
4804
+ closePrefix: " "
4805
+ });
4806
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, `Sheet1!$B$2:$B$${count + 1}`, { openPrefix: " " })), raw(numCache)], {
4807
+ openPrefix: " ",
4808
+ closePrefix: " "
4809
+ });
4810
+ return el("c:val", null, raw(numRef), {
4811
+ openPrefix: " ",
4812
+ closePrefix: " "
4343
4813
  });
4344
- strXml += " <c:txPr>";
4345
- strXml += " <a:bodyPr/>";
4346
- strXml += " <a:lstStyle/>";
4347
- strXml += " <a:p>";
4348
- strXml += " <a:pPr>";
4349
- strXml += ` <a:defRPr sz="${ptToHundredths(opts.dataLabelFontSize || 12)}" b="${opts.dataLabelFontBold ? "1" : "0"}" i="${opts.dataLabelFontItalic ? "1" : "0"}" u="none" strike="noStrike">`;
4350
- strXml += genXmlColorSelection(opts.dataLabelColor || "000000");
4351
- strXml += " " + createChartTextFonts(opts.dataLabelFontFace || "Arial");
4352
- strXml += " </a:defRPr>";
4353
- strXml += " </a:pPr>";
4354
- strXml += " </a:p>";
4355
- strXml += " </c:txPr>";
4356
- strXml += chartType === "pie" ? voidEl("c:dLblPos", { val: opts.dataLabelPosition || "ctr" }) : "";
4357
- strXml += " <c:showLegendKey val=\"0\"/>";
4358
- strXml += " <c:showVal val=\"0\"/>";
4359
- strXml += " <c:showCatName val=\"1\"/>";
4360
- strXml += " <c:showSerName val=\"0\"/>";
4361
- strXml += " <c:showPercent val=\"1\"/>";
4362
- strXml += " <c:showBubbleSize val=\"0\"/>";
4363
- strXml += ` <c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
4364
- strXml += createLeaderLinesElement(opts);
4365
- strXml += "</c:dLbls>";
4366
- strXml += "<c:cat>";
4367
- strXml += " <c:strRef>";
4368
- strXml += ` <c:f>Sheet1!$A$2:$A$${firstLabelGroup(optsChartData).length + 1}</c:f>`;
4369
- strXml += " <c:strCache>";
4370
- strXml += ` <c:ptCount val="${firstLabelGroup(optsChartData).length}"/>`;
4371
- firstLabelGroup(optsChartData).forEach((label, idx) => {
4372
- strXml += `<c:pt idx="${idx}">${el("c:v", null, label)}</c:pt>`;
4373
- });
4374
- strXml += " </c:strCache>";
4375
- strXml += " </c:strRef>";
4376
- strXml += "</c:cat>";
4377
- strXml += " <c:val>";
4378
- strXml += " <c:numRef>";
4379
- strXml += ` <c:f>Sheet1!$B$2:$B$${firstLabelGroup(optsChartData).length + 1}</c:f>`;
4380
- strXml += " <c:numCache>";
4381
- strXml += " <c:formatCode>" + valFmtCode + "</c:formatCode>";
4382
- strXml += ` <c:ptCount val="${firstLabelGroup(optsChartData).length}"/>`;
4383
- dataValues(optsChartData).forEach((value, idx) => {
4384
- strXml += `<c:pt idx="${idx}"><c:v>${value || value === 0 ? value : ""}</c:v></c:pt>`;
4385
- });
4386
- strXml += " </c:numCache>";
4387
- strXml += " </c:numRef>";
4388
- strXml += " </c:val>";
4389
- strXml += " </c:ser>";
4390
- strXml += ` <c:firstSliceAng val="${opts.firstSliceAng ? Math.round(opts.firstSliceAng) : 0}"/>`;
4391
- if (chartType === "doughnut") strXml += `<c:holeSize val="${typeof opts.holeSize === "number" ? opts.holeSize : "50"}"/>`;
4392
- strXml += "</c:" + chartType + "Chart>";
4393
- return strXml;
4394
4814
  }
4395
4815
  /**
4396
- * Create Category axis
4397
- * @param {ChartOptsInternal} opts - chart options
4398
- * @param {string} axisId - value
4399
- * @param {string} valAxisId - value
4400
- * @return {string} XML
4816
+ * Plot a single-series pie / doughnut chart into `<c:pieChart>` / `<c:doughnutChart>`.
4401
4817
  */
4818
+ function makePiePlot(chartType, data, opts, valFmtCode) {
4819
+ const optsChartData = data[0];
4820
+ if (!optsChartData) return "";
4821
+ const chartColors = resolveChartPalette(opts);
4822
+ const labels = firstLabelGroup(optsChartData);
4823
+ const spPr = el("c:spPr", null, [
4824
+ raw(el("a:solidFill", null, raw(voidEl("a:schemeClr", { val: "accent1" })), { openPrefix: " " })),
4825
+ raw(el("a:ln", {
4826
+ w: 9525,
4827
+ cap: "flat"
4828
+ }, [
4829
+ raw(el("a:solidFill", null, raw(voidEl("a:srgbClr", { val: "F9F9F9" })))),
4830
+ raw(voidEl("a:prstDash", { val: "solid" })),
4831
+ raw(voidEl("a:round"))
4832
+ ], { openPrefix: " " })),
4833
+ raw(opts.dataNoEffects ? voidEl("a:effectLst") : createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4834
+ ], {
4835
+ openPrefix: " ",
4836
+ closePrefix: " "
4837
+ });
4838
+ const dLbls = el("c:dLbls", null, [
4839
+ raw(labels.map((_label, idx) => pieDataLabel(idx, optsChartData.customLabels?.[idx], opts, chartType)).join("")),
4840
+ raw(voidEl("c:numFmt", {
4841
+ formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4842
+ sourceLinked: 0
4843
+ }, { openPrefix: " " })),
4844
+ raw(el("c:txPr", null, [
4845
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
4846
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4847
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(labelDefRPr(opts, " ", {
4848
+ openPrefix: " ",
4849
+ closePrefix: " "
4850
+ })), {
4851
+ openPrefix: " ",
4852
+ closePrefix: " "
4853
+ })), {
4854
+ openPrefix: " ",
4855
+ closePrefix: " "
4856
+ }))
4857
+ ], {
4858
+ openPrefix: " ",
4859
+ closePrefix: " "
4860
+ })),
4861
+ chartType === "pie" ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition || "ctr" })) : null,
4862
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4863
+ raw(voidEl("c:showVal", { val: 0 }, { openPrefix: " " })),
4864
+ raw(voidEl("c:showCatName", { val: 1 }, { openPrefix: " " })),
4865
+ raw(voidEl("c:showSerName", { val: 0 }, { openPrefix: " " })),
4866
+ raw(voidEl("c:showPercent", { val: 1 }, { openPrefix: " " })),
4867
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " })),
4868
+ raw(voidEl("c:showLeaderLines", { val: opts.showLeaderLines ? 1 : 0 }, { openPrefix: " " })),
4869
+ raw(createLeaderLinesElement(opts))
4870
+ ]);
4871
+ const ser = el("c:ser", null, [
4872
+ raw(voidEl("c:idx", { val: 0 }, { openPrefix: " " })),
4873
+ raw(voidEl("c:order", { val: 0 }, { openPrefix: " " })),
4874
+ raw(strRefBlock("Sheet1!$B$1", optsChartData.name ?? "", "expanded")),
4875
+ raw(spPr),
4876
+ raw(labels.map((_label, idx) => pieDataPoint(idx, optsChartData.pointStyles?.[idx], opts, paletteColor(chartColors, idx))).join("")),
4877
+ raw(dLbls),
4878
+ raw(pieCategories(labels)),
4879
+ raw(pieValues(optsChartData, labels.length, valFmtCode))
4880
+ ], { closePrefix: " " });
4881
+ return el(`c:${chartType}Chart`, null, [
4882
+ raw(voidEl("c:varyColors", { val: 1 }, { openPrefix: " " })),
4883
+ raw(ser),
4884
+ raw(voidEl("c:firstSliceAng", { val: opts.firstSliceAng ? Math.round(opts.firstSliceAng) : 0 }, { openPrefix: " " })),
4885
+ chartType === "doughnut" ? raw(voidEl("c:holeSize", { val: typeof opts.holeSize === "number" ? opts.holeSize : 50 })) : null
4886
+ ]);
4887
+ }
4402
4888
  //#endregion
4403
4889
  //#region src/gen/chart/plot-stock.ts
4404
4890
  /**
@@ -4432,54 +4918,56 @@ const STOCK_STYLE_SPEC = {
4432
4918
  /** True when the given (already-normalized) stock style leads with a Volume bar series. */
4433
4919
  const isVolumeStockStyle = (style) => !!style && STOCK_STYLE_SPEC[style].volume;
4434
4920
  /** Minimal `<c:dLbls>` block (all labels off) shared by the stock and volume-bar subcharts. */
4435
- const STOCK_DLBLS = "<c:dLbls><c:showLegendKey val=\"0\"/><c:showVal val=\"0\"/><c:showCatName val=\"0\"/><c:showSerName val=\"0\"/><c:showPercent val=\"0\"/><c:showBubbleSize val=\"0\"/></c:dLbls>";
4921
+ const STOCK_DLBLS = el("c:dLbls", null, [
4922
+ raw(voidEl("c:showLegendKey", { val: 0 })),
4923
+ raw(voidEl("c:showVal", { val: 0 })),
4924
+ raw(voidEl("c:showCatName", { val: 0 })),
4925
+ raw(voidEl("c:showSerName", { val: 0 })),
4926
+ raw(voidEl("c:showPercent", { val: 0 })),
4927
+ raw(voidEl("c:showBubbleSize", { val: 0 }))
4928
+ ]);
4436
4929
  /** Emit the shared `<c:cat>` + `<c:val>` refs for a stock/volume series (single-level categories). */
4437
4930
  function stockCatVal(obj, opts, valFmtCode) {
4438
4931
  const cats = firstLabelGroup(obj);
4439
4932
  const valColRow = obj._dataIndex + dataLabels(obj).length + 1;
4440
- let strXml = "<c:cat>";
4441
- if (opts.catLabelFormatCode) {
4442
- strXml += "<c:numRef>";
4443
- strXml += `<c:f>Sheet1!$A$2:$A$${cats.length + 1}</c:f>`;
4444
- strXml += "<c:numCache>";
4445
- strXml += `<c:formatCode>${opts.catLabelFormatCode || "General"}</c:formatCode>`;
4446
- strXml += `<c:ptCount val="${cats.length}"/>`;
4447
- cats.forEach((label, idx) => strXml += `<c:pt idx="${idx}">${el("c:v", null, label)}</c:pt>`);
4448
- strXml += "</c:numCache></c:numRef>";
4449
- } else {
4450
- strXml += "<c:strRef>";
4451
- strXml += `<c:f>Sheet1!$A$2:$A$${cats.length + 1}</c:f>`;
4452
- strXml += "<c:strCache>";
4453
- strXml += `<c:ptCount val="${cats.length}"/>`;
4454
- cats.forEach((label, idx) => strXml += `<c:pt idx="${idx}">${el("c:v", null, label)}</c:pt>`);
4455
- strXml += "</c:strCache></c:strRef>";
4456
- }
4457
- strXml += "</c:cat>";
4458
- strXml += "<c:val><c:numRef>";
4459
- strXml += `<c:f>${sheetRangeRef(valColRow, 2, valColRow, cats.length + 1)}</c:f>`;
4460
- strXml += "<c:numCache>";
4461
- strXml += `<c:formatCode>${valFmtCode}</c:formatCode>`;
4462
- strXml += `<c:ptCount val="${cats.length}"/>`;
4463
- dataValues(obj).forEach((value, idx) => strXml += numCachePt(idx, value));
4464
- strXml += "</c:numCache></c:numRef></c:val>";
4465
- return strXml;
4933
+ const catRef = `Sheet1!$A$2:$A$${cats.length + 1}`;
4934
+ const cat = el("c:cat", null, raw(opts.catLabelFormatCode ? catRefBlock("num", catRef, cats, opts.catLabelFormatCode || "General") : catRefBlock("str", catRef, cats)));
4935
+ const numCache = el("c:numCache", null, [
4936
+ raw(el("c:formatCode", null, valFmtCode)),
4937
+ raw(voidEl("c:ptCount", { val: cats.length })),
4938
+ raw(dataValues(obj).map((value, idx) => numCachePt(idx, value)).join(""))
4939
+ ]);
4940
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, sheetRangeRef(valColRow, 2, valColRow, cats.length + 1))), raw(numCache)]);
4941
+ return cat + el("c:val", null, raw(numRef));
4466
4942
  }
4467
4943
  /** Emit the `<c:tx>` series-name reference for a stock/volume series. */
4468
4944
  function stockSeriesName(obj) {
4469
- return `<c:tx><c:strRef><c:f>${sheetCellRef(obj._dataIndex + dataLabels(obj).length + 1, 1)}</c:f><c:strCache><c:ptCount val="1"/><c:pt idx="0">` + el("c:v", null, obj.name ?? "") + "</c:pt></c:strCache></c:strRef></c:tx>";
4945
+ return strRefBlock(sheetCellRef(obj._dataIndex + dataLabels(obj).length + 1, 1), obj.name ?? "", "compact");
4470
4946
  }
4471
4947
  /** Emit a single stock (line) series: invisible line, optional close-marker, cat/val refs. */
4472
4948
  function makeStockLineSer(obj, opts, valFmtCode, markCloseColor) {
4473
- let strXml = "<c:ser>";
4474
- strXml += `<c:idx val="${obj._dataIndex}"/><c:order val="${obj._dataIndex}"/>`;
4475
- strXml += stockSeriesName(obj);
4476
- strXml += "<c:spPr><a:ln w=\"19050\" cap=\"rnd\"><a:noFill/><a:round/></a:ln><a:effectLst/></c:spPr>";
4477
- if (markCloseColor) strXml += "<c:marker><c:symbol val=\"dot\"/><c:size val=\"5\"/><c:spPr>" + genXmlColorSelection(markCloseColor) + `<a:ln w="9525">${genXmlColorSelection(markCloseColor)}</a:ln><a:effectLst/></c:spPr></c:marker>`;
4478
- else strXml += "<c:marker><c:symbol val=\"none\"/></c:marker>";
4479
- strXml += stockCatVal(obj, opts, valFmtCode);
4480
- strXml += "<c:smooth val=\"0\"/>";
4481
- strXml += "</c:ser>";
4482
- return strXml;
4949
+ const spPr = el("c:spPr", null, [raw(el("a:ln", {
4950
+ w: 19050,
4951
+ cap: "rnd"
4952
+ }, [raw(voidEl("a:noFill")), raw(voidEl("a:round"))])), raw(voidEl("a:effectLst"))]);
4953
+ const marker = markCloseColor ? el("c:marker", null, [
4954
+ raw(voidEl("c:symbol", { val: "dot" })),
4955
+ raw(voidEl("c:size", { val: 5 })),
4956
+ raw(el("c:spPr", null, [
4957
+ raw(genXmlColorSelection(markCloseColor)),
4958
+ raw(el("a:ln", { w: 9525 }, raw(genXmlColorSelection(markCloseColor)))),
4959
+ raw(voidEl("a:effectLst"))
4960
+ ]))
4961
+ ]) : el("c:marker", null, raw(voidEl("c:symbol", { val: "none" })));
4962
+ return el("c:ser", null, [
4963
+ raw(voidEl("c:idx", { val: obj._dataIndex })),
4964
+ raw(voidEl("c:order", { val: obj._dataIndex })),
4965
+ raw(stockSeriesName(obj)),
4966
+ raw(spPr),
4967
+ raw(marker),
4968
+ raw(stockCatVal(obj, opts, valFmtCode)),
4969
+ raw(voidEl("c:smooth", { val: 0 }))
4970
+ ]);
4483
4971
  }
4484
4972
  /**
4485
4973
  * Plot a stock chart. For `vhlc`/`vohlc` the first data series is a Volume column drawn as a
@@ -4494,32 +4982,52 @@ function makeStockPlot(_chartType, data, opts, valAxisId, catAxisId, valFmtCode)
4494
4982
  const stockSeries = spec.volume ? data.slice(1) : data;
4495
4983
  let strXml = "";
4496
4984
  if (volumeSeries) {
4497
- strXml += "<c:barChart>";
4498
- strXml += "<c:barDir val=\"col\"/><c:grouping val=\"clustered\"/><c:varyColors val=\"0\"/>";
4499
- strXml += "<c:ser>";
4500
- strXml += `<c:idx val="${volumeSeries._dataIndex}"/><c:order val="${volumeSeries._dataIndex}"/>`;
4501
- strXml += stockSeriesName(volumeSeries);
4502
- strXml += `<c:spPr>${genXmlColorSelection(chartColors[0] ?? "4472C4")}</c:spPr>`;
4503
- strXml += "<c:invertIfNegative val=\"0\"/>";
4504
- strXml += stockCatVal(volumeSeries, opts, valFmtCode);
4505
- strXml += "</c:ser>";
4506
- strXml += STOCK_DLBLS;
4507
- strXml += "<c:gapWidth val=\"150\"/>";
4508
- strXml += `<c:axId val="${AXIS_ID_CATEGORY_PRIMARY}"/><c:axId val="${AXIS_ID_VALUE_PRIMARY}"/>`;
4509
- strXml += "</c:barChart>";
4510
- }
4511
- strXml += "<c:stockChart>";
4512
- stockSeries.forEach((obj, idx) => {
4513
- const markColor = !spec.upDownBars && idx === stockSeries.length - 1 ? paletteColor(chartColors, obj._dataIndex, "ED7D31") : null;
4514
- strXml += makeStockLineSer(obj, opts, valFmtCode, markColor);
4515
- });
4516
- strXml += STOCK_DLBLS;
4517
- strXml += "<c:hiLowLines><c:spPr><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"75000\"/><a:lumOff val=\"25000\"/></a:schemeClr></a:solidFill><a:round/></a:ln><a:effectLst/></c:spPr></c:hiLowLines>";
4518
- if (spec.upDownBars) strXml += "<c:upDownBars><c:gapWidth val=\"150\"/><c:upBars><c:spPr><a:solidFill><a:schemeClr val=\"bg1\"/></a:solidFill><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></a:solidFill><a:round/></a:ln><a:effectLst/></c:spPr></c:upBars><c:downBars><c:spPr><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></a:solidFill><a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></a:solidFill><a:round/></a:ln><a:effectLst/></c:spPr></c:downBars></c:upDownBars>";
4985
+ const volumeSer = el("c:ser", null, [
4986
+ raw(voidEl("c:idx", { val: volumeSeries._dataIndex })),
4987
+ raw(voidEl("c:order", { val: volumeSeries._dataIndex })),
4988
+ raw(stockSeriesName(volumeSeries)),
4989
+ raw(el("c:spPr", null, raw(genXmlColorSelection(chartColors[0] ?? "4472C4")))),
4990
+ raw(voidEl("c:invertIfNegative", { val: 0 })),
4991
+ raw(stockCatVal(volumeSeries, opts, valFmtCode))
4992
+ ]);
4993
+ strXml += el("c:barChart", null, [
4994
+ raw(voidEl("c:barDir", { val: "col" })),
4995
+ raw(voidEl("c:grouping", { val: "clustered" })),
4996
+ raw(voidEl("c:varyColors", { val: 0 })),
4997
+ raw(volumeSer),
4998
+ raw(STOCK_DLBLS),
4999
+ raw(voidEl("c:gapWidth", { val: 150 })),
5000
+ raw(voidEl("c:axId", { val: AXIS_ID_CATEGORY_PRIMARY })),
5001
+ raw(voidEl("c:axId", { val: AXIS_ID_VALUE_PRIMARY }))
5002
+ ]);
5003
+ }
5004
+ const sers = stockSeries.map((obj, idx) => {
5005
+ return makeStockLineSer(obj, opts, valFmtCode, !spec.upDownBars && idx === stockSeries.length - 1 ? paletteColor(chartColors, obj._dataIndex, "ED7D31") : null);
5006
+ }).join("");
5007
+ const hiLowLines = el("c:hiLowLines", null, raw(el("c:spPr", null, [raw(dimmedTextLine(75e3, 25e3)), raw(voidEl("a:effectLst"))])));
5008
+ const upDownBars = spec.upDownBars ? el("c:upDownBars", null, [
5009
+ raw(voidEl("c:gapWidth", { val: 150 })),
5010
+ raw(el("c:upBars", null, raw(el("c:spPr", null, [
5011
+ raw(el("a:solidFill", null, raw(voidEl("a:schemeClr", { val: "bg1" })))),
5012
+ raw(dimmedTextLine(65e3, 35e3)),
5013
+ raw(voidEl("a:effectLst"))
5014
+ ])))),
5015
+ raw(el("c:downBars", null, raw(el("c:spPr", null, [
5016
+ raw(dimmedTextFill(65e3, 35e3)),
5017
+ raw(dimmedTextLine(65e3, 35e3)),
5018
+ raw(voidEl("a:effectLst"))
5019
+ ]))))
5020
+ ]) : "";
4519
5021
  const stockCatId = spec.volume ? AXIS_ID_CATEGORY_SECONDARY : catAxisId;
4520
5022
  const stockValId = spec.volume ? AXIS_ID_VALUE_SECONDARY : valAxisId;
4521
- strXml += `<c:axId val="${stockCatId}"/><c:axId val="${stockValId}"/>`;
4522
- strXml += "</c:stockChart>";
5023
+ strXml += el("c:stockChart", null, [
5024
+ raw(sers),
5025
+ raw(STOCK_DLBLS),
5026
+ raw(hiLowLines),
5027
+ raw(upDownBars),
5028
+ raw(voidEl("c:axId", { val: stockCatId })),
5029
+ raw(voidEl("c:axId", { val: stockValId }))
5030
+ ]);
4523
5031
  return strXml;
4524
5032
  }
4525
5033
  //#endregion
@@ -4530,28 +5038,30 @@ const isSurface3D = (opts) => opts.surface3D !== false;
4530
5038
  function surfaceCatVal(obj, valFmtCode) {
4531
5039
  const cats = firstLabelGroup(obj);
4532
5040
  const valCol = obj._dataIndex + dataLabels(obj).length + 1;
4533
- let strXml = "<c:cat><c:strRef>";
4534
- strXml += `<c:f>Sheet1!$A$2:$A$${cats.length + 1}</c:f>`;
4535
- strXml += `<c:strCache><c:ptCount val="${cats.length}"/>`;
4536
- cats.forEach((label, idx) => strXml += `<c:pt idx="${idx}">${el("c:v", null, label)}</c:pt>`);
4537
- strXml += "</c:strCache></c:strRef></c:cat>";
4538
- strXml += "<c:val><c:numRef>";
4539
- strXml += `<c:f>${sheetRangeRef(valCol, 2, valCol, cats.length + 1)}</c:f>`;
4540
- strXml += `<c:numCache><c:formatCode>${valFmtCode}</c:formatCode><c:ptCount val="${cats.length}"/>`;
4541
- dataValues(obj).forEach((value, idx) => strXml += numCachePt(idx, value));
4542
- strXml += "</c:numCache></c:numRef></c:val>";
4543
- return strXml;
5041
+ const numCache = el("c:numCache", null, [
5042
+ raw(el("c:formatCode", null, valFmtCode)),
5043
+ raw(voidEl("c:ptCount", { val: cats.length })),
5044
+ raw(dataValues(obj).map((value, idx) => numCachePt(idx, value)).join(""))
5045
+ ]);
5046
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, sheetRangeRef(valCol, 2, valCol, cats.length + 1))), raw(numCache)]);
5047
+ return el("c:cat", null, raw(catRefBlock("str", `Sheet1!$A$2:$A$${cats.length + 1}`, cats))) + el("c:val", null, raw(numRef));
4544
5048
  }
4545
5049
  /** Emit a single surface series: name ref, 3-D shape props, and cat/val refs. */
4546
5050
  function makeSurfaceSer(obj, valFmtCode, seriesColor) {
4547
5051
  const nameCol = obj._dataIndex + dataLabels(obj).length + 1;
4548
- let strXml = "<c:ser>";
4549
- strXml += `<c:idx val="${obj._dataIndex}"/><c:order val="${obj._dataIndex}"/>`;
4550
- strXml += `<c:tx><c:strRef><c:f>${sheetCellRef(nameCol, 1)}</c:f><c:strCache><c:ptCount val="1"/><c:pt idx="0">` + el("c:v", null, obj.name ?? "") + "</c:pt></c:strCache></c:strRef></c:tx>";
4551
- strXml += `<c:spPr>${genXmlColorSelection(seriesColor)}<a:ln/><a:effectLst/><a:sp3d/></c:spPr>`;
4552
- strXml += surfaceCatVal(obj, valFmtCode);
4553
- strXml += "</c:ser>";
4554
- return strXml;
5052
+ const spPr = el("c:spPr", null, [
5053
+ raw(genXmlColorSelection(seriesColor)),
5054
+ raw(voidEl("a:ln")),
5055
+ raw(voidEl("a:effectLst")),
5056
+ raw(voidEl("a:sp3d"))
5057
+ ]);
5058
+ return el("c:ser", null, [
5059
+ raw(voidEl("c:idx", { val: obj._dataIndex })),
5060
+ raw(voidEl("c:order", { val: obj._dataIndex })),
5061
+ raw(strRefBlock(sheetCellRef(nameCol, 1), obj.name ?? "", "compact")),
5062
+ raw(spPr),
5063
+ raw(surfaceCatVal(obj, valFmtCode))
5064
+ ]);
4555
5065
  }
4556
5066
  /**
4557
5067
  * Plot a surface chart into a `<c:surface3DChart>` (3-D) or `<c:surfaceChart>` (2-D contour). Every
@@ -4561,14 +5071,14 @@ function makeSurfaceSer(obj, valFmtCode, seriesColor) {
4561
5071
  function makeSurfacePlot(_chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
4562
5072
  const tag = isSurface3D(opts) ? "surface3DChart" : "surfaceChart";
4563
5073
  const chartColors = resolveChartPalette(opts);
4564
- let strXml = `<c:${tag}>`;
4565
- strXml += `<c:wireframe val="${opts.surfaceWireframe ? 1 : 0}"/>`;
4566
- data.forEach((obj, idx) => {
4567
- strXml += makeSurfaceSer(obj, valFmtCode, paletteColor(chartColors, idx, "4472C4"));
4568
- });
4569
- strXml += `<c:axId val="${catAxisId}"/><c:axId val="${valAxisId}"/><c:axId val="${AXIS_ID_SERIES_PRIMARY}"/>`;
4570
- strXml += `</c:${tag}>`;
4571
- return strXml;
5074
+ const sers = data.map((obj, idx) => makeSurfaceSer(obj, valFmtCode, paletteColor(chartColors, idx, "4472C4"))).join("");
5075
+ return el(`c:${tag}`, null, [
5076
+ raw(voidEl("c:wireframe", { val: opts.surfaceWireframe ? 1 : 0 })),
5077
+ raw(sers),
5078
+ raw(voidEl("c:axId", { val: catAxisId })),
5079
+ raw(voidEl("c:axId", { val: valAxisId })),
5080
+ raw(voidEl("c:axId", { val: AXIS_ID_SERIES_PRIMARY }))
5081
+ ]);
4572
5082
  }
4573
5083
  /**
4574
5084
  * Build the `<c:view3D>` + floor/side/back walls for a surface chart. A 3-D surface tilts the scene
@@ -4577,13 +5087,23 @@ function makeSurfacePlot(_chartType, data, opts, valAxisId, catAxisId, valFmtCod
4577
5087
  * `<c:plotArea>` in CT_Chart document order (view3D → floor → sideWall → backWall → plotArea).
4578
5088
  */
4579
5089
  function makeSurfaceScene(opts) {
4580
- const wall = "<c:thickness val=\"0\"/><c:spPr><a:noFill/><a:ln><a:noFill/></a:ln><a:effectLst/><a:sp3d/></c:spPr>";
4581
- let strXml = "<c:view3D>";
4582
- if (isSurface3D(opts)) strXml += `<c:rotX val="${opts.v3DRotX}"/><c:rotY val="${opts.v3DRotY}"/><c:rAngAx val="0"/>`;
4583
- else strXml += "<c:rotX val=\"90\"/><c:rotY val=\"0\"/><c:rAngAx val=\"0\"/><c:perspective val=\"0\"/>";
4584
- strXml += "</c:view3D>";
4585
- strXml += `<c:floor>${wall}</c:floor><c:sideWall>${wall}</c:sideWall><c:backWall>${wall}</c:backWall>`;
4586
- return strXml;
5090
+ const wall = voidEl("c:thickness", { val: 0 }) + el("c:spPr", null, [
5091
+ raw(voidEl("a:noFill")),
5092
+ raw(el("a:ln", null, raw(voidEl("a:noFill")))),
5093
+ raw(voidEl("a:effectLst")),
5094
+ raw(voidEl("a:sp3d"))
5095
+ ]);
5096
+ const scene = isSurface3D(opts) ? [
5097
+ raw(voidEl("c:rotX", { val: opts.v3DRotX })),
5098
+ raw(voidEl("c:rotY", { val: opts.v3DRotY })),
5099
+ raw(voidEl("c:rAngAx", { val: 0 }))
5100
+ ] : [
5101
+ raw(voidEl("c:rotX", { val: 90 })),
5102
+ raw(voidEl("c:rotY", { val: 0 })),
5103
+ raw(voidEl("c:rAngAx", { val: 0 })),
5104
+ raw(voidEl("c:perspective", { val: 0 }))
5105
+ ];
5106
+ return el("c:view3D", null, scene) + el("c:floor", null, raw(wall)) + el("c:sideWall", null, raw(wall)) + el("c:backWall", null, raw(wall));
4587
5107
  }
4588
5108
  //#endregion
4589
5109
  //#region src/gen/chart/chart-xml.ts
@@ -4602,6 +5122,12 @@ function makeSurfaceScene(opts) {
4602
5122
  * written by the embedded workbook ({@link ./embed-xlsx}); that mapping lives in
4603
5123
  * {@link ./data-refs}.
4604
5124
  */
5125
+ /** The three namespaces every `<c:chartSpace>` declares, in the order PowerPoint writes them. */
5126
+ const CHART_SPACE_NS = {
5127
+ "xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
5128
+ "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
5129
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
5130
+ };
4605
5131
  /**
4606
5132
  * Whether a chart-area/plot-area `fill` says anything the fill dispatch can act on.
4607
5133
  *
@@ -4624,58 +5150,52 @@ function isStatedFill(fill) {
4624
5150
  return Boolean(fill?.color || fill?.type);
4625
5151
  }
4626
5152
  /**
4627
- * Build the chartSpace/chart header: chartSpace open, title (or autoTitleDeleted),
4628
- * optional 3D view, and the plotArea open with optional manual layout.
5153
+ * The chart title (or the `autoTitleDeleted` flag that suppresses PowerPoint's default one)
5154
+ * followed by the 3-D scene, for the two families that plot into one.
4629
5155
  */
4630
5156
  function makeChartHeaderXml(rel) {
4631
- const chartArea = rel.opts.chartArea ?? {};
4632
- let strXml = "";
4633
- strXml += "<c:chartSpace xmlns:c=\"http://schemas.openxmlformats.org/drawingml/2006/chart\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">";
4634
- strXml += voidEl("c:date1904", { val: 0 });
4635
- strXml += voidEl("c:roundedCorners", { val: chartArea.roundedCorners ? 1 : 0 });
4636
- strXml += "<c:chart>";
4637
- if (rel.opts.showTitle) {
4638
- strXml += genXmlTitle({
4639
- title: rel.opts.title || "Chart Title",
4640
- color: rel.opts.titleColor,
4641
- fontFace: rel.opts.titleFontFace,
4642
- fontSize: rel.opts.titleFontSize || 18,
4643
- titleAlign: rel.opts.titleAlign,
4644
- titleBold: rel.opts.titleBold,
4645
- titleItalic: rel.opts.titleItalic,
4646
- titleUnderline: rel.opts.titleUnderline,
4647
- titlePos: rel.opts.titlePos,
4648
- titleRotate: rel.opts.titleRotate
4649
- }, rel.opts.x, rel.opts.y);
4650
- strXml += voidEl("c:autoTitleDeleted", { val: 0 });
4651
- } else strXml += voidEl("c:autoTitleDeleted", { val: 1 });
4652
- /** Add 3D view tag
4653
- * @see: https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_perspective_topic_ID0E6BUQB.html
4654
- */
4655
- if (rel.opts._type === "bar3D") strXml += el("c:view3D", null, [
5157
+ const title = rel.opts.showTitle ? genXmlTitle({
5158
+ title: rel.opts.title || "Chart Title",
5159
+ color: rel.opts.titleColor,
5160
+ fontFace: rel.opts.titleFontFace,
5161
+ fontSize: rel.opts.titleFontSize || 18,
5162
+ titleAlign: rel.opts.titleAlign,
5163
+ titleBold: rel.opts.titleBold,
5164
+ titleItalic: rel.opts.titleItalic,
5165
+ titleUnderline: rel.opts.titleUnderline,
5166
+ titlePos: rel.opts.titlePos,
5167
+ titleRotate: rel.opts.titleRotate
5168
+ }, rel.opts.x, rel.opts.y) + voidEl("c:autoTitleDeleted", { val: 0 }) : voidEl("c:autoTitleDeleted", { val: 1 });
5169
+ if (rel.opts._type === "bar3D") return title + el("c:view3D", null, [
4656
5170
  raw(voidEl("c:rotX", { val: rel.opts.v3DRotX })),
4657
5171
  raw(voidEl("c:rotY", { val: rel.opts.v3DRotY })),
4658
5172
  raw(voidEl("c:rAngAx", { val: !rel.opts.v3DRAngAx ? 0 : 1 })),
4659
5173
  raw(voidEl("c:perspective", { val: rel.opts.v3DPerspective }))
4660
5174
  ]);
4661
- else if (rel.opts._type === "surface") strXml += makeSurfaceScene(rel.opts);
4662
- strXml += "<c:plotArea>";
4663
- if (rel.opts.layout) {
4664
- const manualLayout = el("c:manualLayout", null, [
4665
- raw(voidEl("c:layoutTarget", { val: "inner" }, { closePrefix: " " })),
4666
- raw(voidEl("c:xMode", { val: "edge" }, { closePrefix: " " })),
4667
- raw(voidEl("c:yMode", { val: "edge" }, { closePrefix: " " })),
4668
- raw(voidEl("c:x", { val: rel.opts.layout.x || 0 }, { closePrefix: " " })),
4669
- raw(voidEl("c:y", { val: rel.opts.layout.y || 0 }, { closePrefix: " " })),
4670
- raw(voidEl("c:w", { val: rel.opts.layout.w || 1 }, { closePrefix: " " })),
4671
- raw(voidEl("c:h", { val: rel.opts.layout.h || 1 }, { closePrefix: " " }))
4672
- ], {
4673
- childPrefix: " ",
4674
- closePrefix: " "
4675
- });
4676
- strXml += el("c:layout", null, raw(manualLayout), { childPrefix: " " });
4677
- } else strXml += voidEl("c:layout");
4678
- return strXml;
5175
+ if (rel.opts._type === "surface") return title + makeSurfaceScene(rel.opts);
5176
+ return title;
5177
+ }
5178
+ /**
5179
+ * The plot area's `<c:layout>`. Manual layout is emitted only when the caller asked for one:
5180
+ * an empty `<c:layout/>` lets PowerPoint auto-fit, which it does well across all four TRBL
5181
+ * legend positions.
5182
+ */
5183
+ function makePlotAreaLayoutXml(rel) {
5184
+ const layout = rel.opts.layout;
5185
+ if (!layout) return voidEl("c:layout");
5186
+ const manualLayout = el("c:manualLayout", null, [
5187
+ raw(voidEl("c:layoutTarget", { val: "inner" }, { closePrefix: " " })),
5188
+ raw(voidEl("c:xMode", { val: "edge" }, { closePrefix: " " })),
5189
+ raw(voidEl("c:yMode", { val: "edge" }, { closePrefix: " " })),
5190
+ raw(voidEl("c:x", { val: layout.x || 0 }, { closePrefix: " " })),
5191
+ raw(voidEl("c:y", { val: layout.y || 0 }, { closePrefix: " " })),
5192
+ raw(voidEl("c:w", { val: layout.w || 1 }, { closePrefix: " " })),
5193
+ raw(voidEl("c:h", { val: layout.h || 1 }, { closePrefix: " " }))
5194
+ ], {
5195
+ childPrefix: " ",
5196
+ closePrefix: " "
5197
+ });
5198
+ return el("c:layout", null, raw(manualLayout), { childPrefix: " " });
4679
5199
  }
4680
5200
  /**
4681
5201
  * Build the category/value/series axis XML (empty for pie/doughnut). Resolves combo-chart
@@ -4745,97 +5265,132 @@ function makeChartAxesXml(rel, usesSecondaryValAxis, usesSecondaryCatAxis, prima
4745
5265
  return strXml;
4746
5266
  }
4747
5267
  /**
4748
- * Build the plotArea properties (data table, fill, border), close plotArea, then the legend.
5268
+ * The `<c:spPr>` shared by the plot area and the chart space: a stated fill or none, a border
5269
+ * or an explicit no-line, and an empty effect list. The two differ only in how far in they are
5270
+ * indented, which is what `fmt` and `effectIndent` carry.
4749
5271
  */
4750
- function makeChartPlotAreaPropsXml(rel) {
4751
- const plotArea = rel.opts.plotArea ?? {};
4752
- let strXml = "";
4753
- if (rel.opts.showDataTable) {
4754
- strXml += "<c:dTable>";
4755
- strXml += ` <c:showHorzBorder val="${!rel.opts.showDataTableHorzBorder ? 0 : 1}"/>`;
4756
- strXml += ` <c:showVertBorder val="${!rel.opts.showDataTableVertBorder ? 0 : 1}"/>`;
4757
- strXml += ` <c:showOutline val="${!rel.opts.showDataTableOutline ? 0 : 1}"/>`;
4758
- strXml += ` <c:showKeys val="${!rel.opts.showDataTableKeys ? 0 : 1}"/>`;
4759
- strXml += " <c:spPr>";
4760
- strXml += " <a:noFill/>";
4761
- strXml += " <a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"15000\"/><a:lumOff val=\"85000\"/></a:schemeClr></a:solidFill><a:round/></a:ln>";
4762
- strXml += " <a:effectLst/>";
4763
- strXml += " </c:spPr>";
4764
- strXml += " <c:txPr>";
4765
- strXml += " <a:bodyPr rot=\"0\" spcFirstLastPara=\"1\" vertOverflow=\"ellipsis\" vert=\"horz\" wrap=\"square\" anchor=\"ctr\" anchorCtr=\"1\"/>";
4766
- strXml += " <a:lstStyle/>";
4767
- strXml += " <a:p>";
4768
- strXml += " <a:pPr rtl=\"0\">";
4769
- strXml += ` <a:defRPr sz="${ptToHundredths(rel.opts.dataTableFontSize || 12)}" b="0" i="0" u="none" strike="noStrike" kern="1200" baseline="0">`;
4770
- strXml += " <a:solidFill><a:schemeClr val=\"tx1\"><a:lumMod val=\"65000\"/><a:lumOff val=\"35000\"/></a:schemeClr></a:solidFill>";
4771
- strXml += " <a:latin typeface=\"+mn-lt\"/>";
4772
- strXml += " <a:ea typeface=\"+mn-ea\"/>";
4773
- strXml += " <a:cs typeface=\"+mn-cs\"/>";
4774
- strXml += " </a:defRPr>";
4775
- strXml += " </a:pPr>";
4776
- strXml += " <a:endParaRPr lang=\"en-US\"/>";
4777
- strXml += " </a:p>";
4778
- strXml += " </c:txPr>";
4779
- strXml += "</c:dTable>";
4780
- }
4781
- strXml += " <c:spPr>";
4782
- strXml += isStatedFill(plotArea.fill) ? genXmlColorSelection(plotArea.fill) : "<a:noFill/>";
4783
- strXml += plotArea.border ? `<a:ln w="${ptsToEmuLenient(resolveBorderWidth(plotArea.border, 1))}" cap="flat">${genXmlColorSelection({
4784
- color: plotArea.border.color ?? "363636",
4785
- transparency: plotArea.border.transparency
4786
- })}</a:ln>` : "<a:ln><a:noFill/></a:ln>";
4787
- strXml += " <a:effectLst/>";
4788
- strXml += " </c:spPr>";
4789
- strXml += "</c:plotArea>";
4790
- if (rel.opts.showLegend) {
4791
- strXml += "<c:legend>";
4792
- strXml += "<c:legendPos val=\"" + rel.opts.legendPos + "\"/>";
4793
- if (Array.isArray(rel.opts._type)) {
4794
- let seriesIdx = 0;
4795
- rel.opts._type.forEach((type) => {
4796
- if (type.options?.showLegend === false) for (let i = 0; i < type.data.length; i++) strXml += `<c:legendEntry><c:idx val="${seriesIdx + i}"/><c:delete val="1"/></c:legendEntry>`;
4797
- seriesIdx += type.data.length;
4798
- });
4799
- }
4800
- const legendLayout = rel.opts.legendLayout;
4801
- const hasLegendX = legendLayout && typeof legendLayout.x === "number";
4802
- const hasLegendY = legendLayout && typeof legendLayout.y === "number";
4803
- const hasLegendW = legendLayout && typeof legendLayout.w === "number";
4804
- const hasLegendH = legendLayout && typeof legendLayout.h === "number";
4805
- if (hasLegendX || hasLegendY || hasLegendW || hasLegendH) {
4806
- let modes = "";
4807
- let vals = "";
4808
- if (hasLegendX) {
4809
- modes += "<c:xMode val=\"edge\"/>";
4810
- vals += `<c:x val="${legendLayout.x}"/>`;
4811
- }
4812
- if (hasLegendY) {
4813
- modes += "<c:yMode val=\"edge\"/>";
4814
- vals += `<c:y val="${legendLayout.y}"/>`;
4815
- }
4816
- if (hasLegendW) vals += `<c:w val="${legendLayout.w}"/>`;
4817
- if (hasLegendH) vals += `<c:h val="${legendLayout.h}"/>`;
4818
- strXml += `<c:layout><c:manualLayout>${modes}${vals}</c:manualLayout></c:layout>`;
4819
- }
4820
- strXml += "<c:overlay val=\"0\"/>";
4821
- if (rel.opts.legendFontFace || rel.opts.legendFontSize || rel.opts.legendColor) {
4822
- strXml += "<c:txPr>";
4823
- strXml += " <a:bodyPr/>";
4824
- strXml += " <a:lstStyle/>";
4825
- strXml += " <a:p>";
4826
- strXml += " <a:pPr>";
4827
- strXml += rel.opts.legendFontSize ? `<a:defRPr sz="${ptToHundredths(rel.opts.legendFontSize)}">` : "<a:defRPr>";
4828
- if (rel.opts.legendColor) strXml += genXmlColorSelection(rel.opts.legendColor);
4829
- if (rel.opts.legendFontFace) strXml += createChartTextFonts(rel.opts.legendFontFace);
4830
- strXml += " </a:defRPr>";
4831
- strXml += " </a:pPr>";
4832
- strXml += " <a:endParaRPr lang=\"en-US\"/>";
4833
- strXml += " </a:p>";
4834
- strXml += "</c:txPr>";
5272
+ function chartShapeProps(fill, border, effectIndent, fmt) {
5273
+ return el("c:spPr", null, [
5274
+ raw(isStatedFill(fill) ? genXmlColorSelection(fill) : voidEl("a:noFill")),
5275
+ raw(border ? el("a:ln", {
5276
+ w: ptsToEmuLenient(resolveBorderWidth(border, 1)),
5277
+ cap: "flat"
5278
+ }, raw(genXmlColorSelection({
5279
+ color: border.color ?? "363636",
5280
+ transparency: border.transparency
5281
+ }))) : el("a:ln", null, raw(voidEl("a:noFill")))),
5282
+ raw(voidEl("a:effectLst", null, { openPrefix: effectIndent }))
5283
+ ], fmt);
5284
+ }
5285
+ /**
5286
+ * The `<c:dTable>` grid drawn under the plot. Sits between the last `</c:valAx>` and the plot
5287
+ * area's own `<c:spPr>`.
5288
+ */
5289
+ function makeDataTableXml(rel) {
5290
+ const spPr = el("c:spPr", null, [
5291
+ raw(voidEl("a:noFill", null, { openPrefix: " " })),
5292
+ raw(dimmedTextLine(15e3, 85e3, { openPrefix: " " })),
5293
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
5294
+ ], {
5295
+ openPrefix: " ",
5296
+ closePrefix: " "
5297
+ });
5298
+ const defRPr = el("a:defRPr", {
5299
+ sz: ptToHundredths(rel.opts.dataTableFontSize || 12),
5300
+ b: 0,
5301
+ i: 0,
5302
+ u: "none",
5303
+ strike: "noStrike",
5304
+ kern: 1200,
5305
+ baseline: 0
5306
+ }, [
5307
+ raw(" " + dimmedTextFill(65e3, 35e3)),
5308
+ raw(voidEl("a:latin", { typeface: "+mn-lt" }, { openPrefix: " " })),
5309
+ raw(voidEl("a:ea", { typeface: "+mn-ea" }, { openPrefix: " " })),
5310
+ raw(voidEl("a:cs", { typeface: "+mn-cs" }, { openPrefix: " " }))
5311
+ ], {
5312
+ openPrefix: " ",
5313
+ closePrefix: " "
5314
+ });
5315
+ const txPr = el("c:txPr", null, [
5316
+ raw(voidEl("a:bodyPr", {
5317
+ rot: 0,
5318
+ spcFirstLastPara: 1,
5319
+ vertOverflow: "ellipsis",
5320
+ vert: "horz",
5321
+ wrap: "square",
5322
+ anchor: "ctr",
5323
+ anchorCtr: 1
5324
+ }, { openPrefix: " " })),
5325
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
5326
+ raw(el("a:p", null, [raw(el("a:pPr", { rtl: 0 }, raw(defRPr), {
5327
+ openPrefix: " ",
5328
+ closePrefix: " "
5329
+ })), raw(voidEl("a:endParaRPr", { lang: "en-US" }, { openPrefix: " " }))], {
5330
+ openPrefix: " ",
5331
+ closePrefix: " "
5332
+ }))
5333
+ ], {
5334
+ openPrefix: " ",
5335
+ closePrefix: " "
5336
+ });
5337
+ return el("c:dTable", null, [
5338
+ raw(voidEl("c:showHorzBorder", { val: !rel.opts.showDataTableHorzBorder ? 0 : 1 }, { openPrefix: " " })),
5339
+ raw(voidEl("c:showVertBorder", { val: !rel.opts.showDataTableVertBorder ? 0 : 1 }, { openPrefix: " " })),
5340
+ raw(` <c:showOutline val="${!rel.opts.showDataTableOutline ? 0 : 1}"/>`),
5341
+ raw(` <c:showKeys val="${!rel.opts.showDataTableKeys ? 0 : 1}"/>`),
5342
+ raw(spPr),
5343
+ raw(txPr)
5344
+ ]);
5345
+ }
5346
+ /**
5347
+ * The `<c:legend>`: position, the entries a combo chart deletes, an optional manual placement,
5348
+ * and the text properties when any legend font option is set.
5349
+ */
5350
+ function makeLegendXml(rel) {
5351
+ let entries = "";
5352
+ if (Array.isArray(rel.opts._type)) {
5353
+ let seriesIdx = 0;
5354
+ for (const type of rel.opts._type) {
5355
+ if (type.options?.showLegend === false) for (let i = 0; i < type.data.length; i++) entries += el("c:legendEntry", null, [raw(voidEl("c:idx", { val: seriesIdx + i })), raw(voidEl("c:delete", { val: 1 }))]);
5356
+ seriesIdx += type.data.length;
4835
5357
  }
4836
- strXml += "</c:legend>";
4837
5358
  }
4838
- return strXml;
5359
+ const legendLayout = rel.opts.legendLayout;
5360
+ const placed = (key) => typeof legendLayout?.[key] === "number";
5361
+ let layout = "";
5362
+ if (placed("x") || placed("y") || placed("w") || placed("h")) {
5363
+ const modes = (placed("x") ? voidEl("c:xMode", { val: "edge" }) : "") + (placed("y") ? voidEl("c:yMode", { val: "edge" }) : "");
5364
+ const vals = [
5365
+ "x",
5366
+ "y",
5367
+ "w",
5368
+ "h"
5369
+ ].filter(placed).map((key) => voidEl(`c:${key}`, { val: legendLayout?.[key] })).join("");
5370
+ layout = el("c:layout", null, raw(el("c:manualLayout", null, raw(modes + vals))));
5371
+ }
5372
+ let txPr = "";
5373
+ if (rel.opts.legendFontFace || rel.opts.legendFontSize || rel.opts.legendColor) {
5374
+ const defRPr = el("a:defRPr", { sz: rel.opts.legendFontSize ? ptToHundredths(rel.opts.legendFontSize) : void 0 }, [raw(rel.opts.legendColor ? genXmlColorSelection(rel.opts.legendColor) : ""), raw(rel.opts.legendFontFace ? createChartTextFonts(rel.opts.legendFontFace) : "")], { closePrefix: " " });
5375
+ txPr = el("c:txPr", null, [
5376
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
5377
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
5378
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
5379
+ openPrefix: " ",
5380
+ closePrefix: " "
5381
+ })), raw(voidEl("a:endParaRPr", { lang: "en-US" }, { openPrefix: " " }))], {
5382
+ openPrefix: " ",
5383
+ closePrefix: " "
5384
+ }))
5385
+ ]);
5386
+ }
5387
+ return el("c:legend", null, [
5388
+ raw(voidEl("c:legendPos", { val: rel.opts.legendPos })),
5389
+ raw(entries),
5390
+ raw(layout),
5391
+ raw(voidEl("c:overlay", { val: 0 })),
5392
+ raw(txPr)
5393
+ ]);
4839
5394
  }
4840
5395
  /**
4841
5396
  * Main entry point method for create charts
@@ -4844,16 +5399,16 @@ function makeChartPlotAreaPropsXml(rel) {
4844
5399
  * @return {string} XML
4845
5400
  */
4846
5401
  function makeXmlCharts(rel) {
4847
- let strXml = XML_DECL;
4848
5402
  const chartArea = rel.opts.chartArea ?? {};
5403
+ const plotAreaOpts = rel.opts.plotArea ?? {};
4849
5404
  let usesSecondaryValAxis = false;
4850
5405
  let usesSecondaryCatAxis = false;
4851
5406
  let primaryCatAxisValType = null;
4852
5407
  let secondaryCatAxisValType = null;
4853
5408
  let primaryCatAxisHasCategoryChart = false;
4854
5409
  let secondaryCatAxisHasCategoryChart = false;
4855
- strXml += makeChartHeaderXml(rel);
4856
- if (Array.isArray(rel.opts._type)) rel.opts._type.forEach((type) => {
5410
+ let plots = "";
5411
+ if (Array.isArray(rel.opts._type)) for (const type of rel.opts._type) {
4857
5412
  const options = {
4858
5413
  ...rel.opts,
4859
5414
  ...type.options
@@ -4869,27 +5424,36 @@ function makeXmlCharts(rel) {
4869
5424
  else secondaryCatAxisHasCategoryChart = true;
4870
5425
  } else if (usesValueXAxis) primaryCatAxisValType = subType;
4871
5426
  else primaryCatAxisHasCategoryChart = true;
4872
- strXml += makeChartType(subType, type.data, options, valAxisId, catAxisId);
4873
- });
4874
- else if (rel.opts._type) strXml += makeChartType(rel.opts._type, rel.data, rel.opts, AXIS_ID_VALUE_PRIMARY, AXIS_ID_CATEGORY_PRIMARY);
4875
- strXml += makeChartAxesXml(rel, usesSecondaryValAxis, usesSecondaryCatAxis, primaryCatAxisValType, secondaryCatAxisValType, primaryCatAxisHasCategoryChart, secondaryCatAxisHasCategoryChart);
4876
- strXml += makeChartPlotAreaPropsXml(rel);
4877
- strXml += " <c:plotVisOnly val=\"1\"/>";
4878
- strXml += " <c:dispBlanksAs val=\"" + rel.opts.displayBlanksAs + "\"/>";
4879
- if (rel.opts._type === "scatter") strXml += "<c:showDLblsOverMax val=\"1\"/>";
4880
- strXml += "</c:chart>";
4881
- strXml += "<c:spPr>";
4882
- strXml += isStatedFill(chartArea.fill) ? genXmlColorSelection(chartArea.fill) : "<a:noFill/>";
4883
- strXml += chartArea.border ? `<a:ln w="${ptsToEmuLenient(resolveBorderWidth(chartArea.border, 1))}" cap="flat">${genXmlColorSelection({
4884
- color: chartArea.border.color ?? "363636",
4885
- transparency: chartArea.border.transparency
4886
- })}</a:ln>` : "<a:ln><a:noFill/></a:ln>";
4887
- strXml += " <a:effectLst/>";
4888
- strXml += "</c:spPr>";
4889
- strXml += "<c:externalData r:id=\"rId1\"><c:autoUpdate val=\"0\"/></c:externalData>";
4890
- strXml += genXmlChartMetadata(rel.opts.metadata);
4891
- strXml += "</c:chartSpace>";
4892
- return strXml;
5427
+ plots += makeChartType(subType, type.data, options, valAxisId, catAxisId);
5428
+ }
5429
+ else if (rel.opts._type) plots = makeChartType(rel.opts._type, rel.data, rel.opts, AXIS_ID_VALUE_PRIMARY, AXIS_ID_CATEGORY_PRIMARY);
5430
+ const axes = makeChartAxesXml(rel, usesSecondaryValAxis, usesSecondaryCatAxis, primaryCatAxisValType, secondaryCatAxisValType, primaryCatAxisHasCategoryChart, secondaryCatAxisHasCategoryChart);
5431
+ const plotArea = el("c:plotArea", null, [
5432
+ raw(makePlotAreaLayoutXml(rel)),
5433
+ raw(plots),
5434
+ raw(axes),
5435
+ rel.opts.showDataTable ? raw(makeDataTableXml(rel)) : null,
5436
+ raw(chartShapeProps(plotAreaOpts.fill, plotAreaOpts.border, " ", {
5437
+ openPrefix: " ",
5438
+ closePrefix: " "
5439
+ }))
5440
+ ]);
5441
+ const chart = el("c:chart", null, [
5442
+ raw(makeChartHeaderXml(rel)),
5443
+ raw(plotArea),
5444
+ rel.opts.showLegend ? raw(makeLegendXml(rel)) : null,
5445
+ raw(voidEl("c:plotVisOnly", { val: 1 }, { openPrefix: " " })),
5446
+ raw(voidEl("c:dispBlanksAs", { val: rel.opts.displayBlanksAs }, { openPrefix: " " })),
5447
+ rel.opts._type === "scatter" ? raw(voidEl("c:showDLblsOverMax", { val: 1 })) : null
5448
+ ]);
5449
+ return XML_DECL + el("c:chartSpace", CHART_SPACE_NS, [
5450
+ raw(voidEl("c:date1904", { val: 0 })),
5451
+ raw(voidEl("c:roundedCorners", { val: chartArea.roundedCorners ? 1 : 0 })),
5452
+ raw(chart),
5453
+ raw(chartShapeProps(chartArea.fill, chartArea.border, " ", {})),
5454
+ raw(el("c:externalData", { "r:id": "rId1" }, raw(voidEl("c:autoUpdate", { val: 0 })))),
5455
+ raw(genXmlChartMetadata(rel.opts.metadata))
5456
+ ]);
4893
5457
  }
4894
5458
  /**
4895
5459
  * Stable ts-pptx vendor GUID identifying the chart-metadata extension on `c:chartSpace/c:extLst`.
@@ -4944,7 +5508,7 @@ function genXmlChartMetadata(metadata) {
4944
5508
  * @return {string} XML chart
4945
5509
  */
4946
5510
  function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
4947
- const valFmtCode = encodeXmlEntities(opts.valLabelFormatCode || opts.dataTableFormatCode || opts.dataLabelFormatCode || "General");
5511
+ const valFmtCode = opts.valLabelFormatCode || opts.dataTableFormatCode || opts.dataLabelFormatCode || "General";
4948
5512
  switch (chartType) {
4949
5513
  case "area":
4950
5514
  case "bar":
@@ -5328,6 +5892,13 @@ function makeChartExStyleXml() {
5328
5892
  //#region src/gen/chart/embed-xlsx.ts
5329
5893
  /** MS chart-extension relationship types (chartEx style + color-style sidecar parts). */
5330
5894
  const MS_CHART_REL = "http://schemas.microsoft.com/office/2011/relationships/";
5895
+ /** The SpreadsheetML namespace every part of the embedded workbook is written in. */
5896
+ const SML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
5897
+ /** OPC content types, and the prefix the four SpreadsheetML part types share. */
5898
+ const CT_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
5899
+ const SML_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.";
5900
+ /** One `<si>` shared string carrying literal text. */
5901
+ const sharedString = (text) => el("si", null, raw(el("t", null, text)));
5331
5902
  function relationship$2(id, type, target) {
5332
5903
  return voidEl("Relationship", {
5333
5904
  Id: id,
@@ -5340,6 +5911,17 @@ function relationships(rels) {
5340
5911
  return el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
5341
5912
  }
5342
5913
  /**
5914
+ * The embedded workbook's style sheet, captured verbatim from an Excel-authored chart workbook.
5915
+ * @raw-xml-asset
5916
+ */
5917
+ const XLSX_STYLES_XML = "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"1\"><numFmt numFmtId=\"0\" formatCode=\"General\"/></numFmts><fonts count=\"4\"><font><sz val=\"9\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"9\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"10\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"18\"/><color indexed=\"8\"/><name val=\"Arial\"/></font></fonts><fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill></fills><borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders><dxfs count=\"0\"/><tableStyles count=\"0\"/><colors><indexedColors><rgbColor rgb=\"ff000000\"/><rgbColor rgb=\"ffffffff\"/><rgbColor rgb=\"ffff0000\"/><rgbColor rgb=\"ff00ff00\"/><rgbColor rgb=\"ff0000ff\"/><rgbColor rgb=\"ffffff00\"/><rgbColor rgb=\"ffff00ff\"/><rgbColor rgb=\"ff00ffff\"/><rgbColor rgb=\"ff000000\"/><rgbColor rgb=\"ffffffff\"/><rgbColor rgb=\"ff878787\"/><rgbColor rgb=\"fff9f9f9\"/></indexedColors></colors></styleSheet>\n";
5918
+ /**
5919
+ * The Office theme the embedded workbook ships, captured verbatim. PowerPoint reads it when a user
5920
+ * opens the chart's data, so it has to be exactly the bytes Office writes.
5921
+ * @raw-xml-asset
5922
+ */
5923
+ const XLSX_THEME_XML = "<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"Office Theme\"><a:themeElements><a:clrScheme name=\"Office\"><a:dk1><a:sysClr val=\"windowText\" lastClr=\"000000\"/></a:dk1><a:lt1><a:sysClr val=\"window\" lastClr=\"FFFFFF\"/></a:lt1><a:dk2><a:srgbClr val=\"44546A\"/></a:dk2><a:lt2><a:srgbClr val=\"E7E6E6\"/></a:lt2><a:accent1><a:srgbClr val=\"4472C4\"/></a:accent1><a:accent2><a:srgbClr val=\"ED7D31\"/></a:accent2><a:accent3><a:srgbClr val=\"A5A5A5\"/></a:accent3><a:accent4><a:srgbClr val=\"FFC000\"/></a:accent4><a:accent5><a:srgbClr val=\"5B9BD5\"/></a:accent5><a:accent6><a:srgbClr val=\"70AD47\"/></a:accent6><a:hlink><a:srgbClr val=\"0563C1\"/></a:hlink><a:folHlink><a:srgbClr val=\"954F72\"/></a:folHlink></a:clrScheme><a:fontScheme name=\"Office\"><a:majorFont><a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"Yu Gothic Light\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"DengXian Light\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Times New Roman\"/><a:font script=\"Hebr\" typeface=\"Times New Roman\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"MoolBoran\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Times New Roman\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:majorFont><a:minorFont><a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"Yu Gothic\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"DengXian\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Arial\"/><a:font script=\"Hebr\" typeface=\"Arial\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"DaunPenh\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Arial\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:minorFont></a:fontScheme><a:fmtScheme name=\"Office\"><a:fillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"110000\"/><a:satMod val=\"105000\"/><a:tint val=\"67000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"103000\"/><a:tint val=\"73000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"109000\"/><a:tint val=\"81000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:satMod val=\"103000\"/><a:lumMod val=\"102000\"/><a:tint val=\"94000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:satMod val=\"110000\"/><a:lumMod val=\"100000\"/><a:shade val=\"100000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"99000\"/><a:satMod val=\"120000\"/><a:shade val=\"78000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad=\"57150\" dist=\"19050\" dir=\"5400000\" algn=\"ctr\" rotWithShape=\"0\"><a:srgbClr val=\"000000\"><a:alpha val=\"63000\"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:solidFill><a:schemeClr val=\"phClr\"><a:tint val=\"95000\"/><a:satMod val=\"170000\"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"93000\"/><a:satMod val=\"150000\"/><a:shade val=\"98000\"/><a:lumMod val=\"102000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:tint val=\"98000\"/><a:satMod val=\"130000\"/><a:shade val=\"90000\"/><a:lumMod val=\"103000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"63000\"/><a:satMod val=\"120000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/><a:extLst><a:ext uri=\"{05A4C25C-085E-4340-85A3-A5531E510DB2}\"><thm15:themeFamily xmlns:thm15=\"http://schemas.microsoft.com/office/thememl/2012/main\" name=\"Office Theme\" id=\"{62F939B6-93AF-4DB8-9C6B-D6C7DFDC589F}\" vid=\"{4A3C46E8-61CC-4603-A589-7422A47A8E4A}\"/></a:ext></a:extLst></a:theme>";
5924
+ /**
5343
5925
  * Build the chart's embedded Excel workbook as a standalone OPC package and
5344
5926
  * return its bytes — the data source PowerPoint opens when a user edits the
5345
5927
  * chart's data. Pure (no zip side effects), so both the package write path
@@ -5354,24 +5936,108 @@ function buildEmbeddedWorksheet(chartObject) {
5354
5936
  const zipExcel = new ZipWriter();
5355
5937
  const intBubbleCols = (data.length - 1) * 2 + 1;
5356
5938
  const IS_MULTI_CAT_AXES = (data[0]?.labels?.length ?? 0) > 1;
5357
- zipExcel.add("[Content_Types].xml", XML_DECL + "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"> <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/> <Default Extension=\"xml\" ContentType=\"application/xml\"/> <Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/> <Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/> <Override PartName=\"/xl/theme/theme1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/> <Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/> <Override PartName=\"/xl/sharedStrings.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\"/> <Override PartName=\"/xl/tables/table1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml\"/> <Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/> <Override PartName=\"/docProps/app.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/></Types>\n");
5358
- zipExcel.add("_rels/.rels", XML_DECL + relationships([
5359
- relationship$2("rId1", PACKAGE_REL_NS + "/metadata/core-properties", "docProps/core.xml"),
5360
- relationship$2("rId2", OFFICE_REL + "extended-properties", "docProps/app.xml"),
5361
- relationship$2("rId3", OFFICE_REL + "officeDocument", "xl/workbook.xml")
5362
- ]) + "\n");
5363
- zipExcel.add("docProps/app.xml", XML_DECL + "<Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\" xmlns:vt=\"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes\"><Application>Microsoft Macintosh Excel</Application><DocSecurity>0</DocSecurity><ScaleCrop>false</ScaleCrop><HeadingPairs><vt:vector size=\"2\" baseType=\"variant\"><vt:variant><vt:lpstr>Worksheets</vt:lpstr></vt:variant><vt:variant><vt:i4>1</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size=\"1\" baseType=\"lpstr\"><vt:lpstr>Sheet1</vt:lpstr></vt:vector></TitlesOfParts><Company></Company><LinksUpToDate>false</LinksUpToDate><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>16.0300</AppVersion></Properties>\n");
5364
- zipExcel.add("docProps/core.xml", XML_DECL + "<cp:coreProperties xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><dc:creator>TsPptx</dc:creator><cp:lastModifiedBy>TsPptx</cp:lastModifiedBy><dcterms:created xsi:type=\"dcterms:W3CDTF\">" + (/* @__PURE__ */ new Date()).toISOString() + "</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">" + (/* @__PURE__ */ new Date()).toISOString() + "</dcterms:modified></cp:coreProperties>");
5365
- zipExcel.add("xl/_rels/workbook.xml.rels", XML_DECL + relationships([
5366
- relationship$2("rId3", OFFICE_REL + "styles", "styles.xml"),
5367
- relationship$2("rId2", OFFICE_REL + "theme", "theme/theme1.xml"),
5368
- relationship$2("rId1", OFFICE_REL + "worksheet", "worksheets/sheet1.xml"),
5369
- relationship$2("rId4", OFFICE_REL + "sharedStrings", "sharedStrings.xml")
5370
- ]));
5371
- zipExcel.add("xl/styles.xml", XML_DECL + "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"1\"><numFmt numFmtId=\"0\" formatCode=\"General\"/></numFmts><fonts count=\"4\"><font><sz val=\"9\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"9\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"10\"/><color indexed=\"8\"/><name val=\"Geneva\"/></font><font><sz val=\"18\"/><color indexed=\"8\"/><name val=\"Arial\"/></font></fonts><fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill></fills><borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders><dxfs count=\"0\"/><tableStyles count=\"0\"/><colors><indexedColors><rgbColor rgb=\"ff000000\"/><rgbColor rgb=\"ffffffff\"/><rgbColor rgb=\"ffff0000\"/><rgbColor rgb=\"ff00ff00\"/><rgbColor rgb=\"ff0000ff\"/><rgbColor rgb=\"ffffff00\"/><rgbColor rgb=\"ffff00ff\"/><rgbColor rgb=\"ff00ffff\"/><rgbColor rgb=\"ff000000\"/><rgbColor rgb=\"ffffffff\"/><rgbColor rgb=\"ff878787\"/><rgbColor rgb=\"fff9f9f9\"/></indexedColors></colors></styleSheet>\n");
5372
- zipExcel.add("xl/theme/theme1.xml", XML_DECL + "<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"Office Theme\"><a:themeElements><a:clrScheme name=\"Office\"><a:dk1><a:sysClr val=\"windowText\" lastClr=\"000000\"/></a:dk1><a:lt1><a:sysClr val=\"window\" lastClr=\"FFFFFF\"/></a:lt1><a:dk2><a:srgbClr val=\"44546A\"/></a:dk2><a:lt2><a:srgbClr val=\"E7E6E6\"/></a:lt2><a:accent1><a:srgbClr val=\"4472C4\"/></a:accent1><a:accent2><a:srgbClr val=\"ED7D31\"/></a:accent2><a:accent3><a:srgbClr val=\"A5A5A5\"/></a:accent3><a:accent4><a:srgbClr val=\"FFC000\"/></a:accent4><a:accent5><a:srgbClr val=\"5B9BD5\"/></a:accent5><a:accent6><a:srgbClr val=\"70AD47\"/></a:accent6><a:hlink><a:srgbClr val=\"0563C1\"/></a:hlink><a:folHlink><a:srgbClr val=\"954F72\"/></a:folHlink></a:clrScheme><a:fontScheme name=\"Office\"><a:majorFont><a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"Yu Gothic Light\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"DengXian Light\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Times New Roman\"/><a:font script=\"Hebr\" typeface=\"Times New Roman\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"MoolBoran\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Times New Roman\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:majorFont><a:minorFont><a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/><a:font script=\"Jpan\" typeface=\"Yu Gothic\"/><a:font script=\"Hang\" typeface=\"맑은 고딕\"/><a:font script=\"Hans\" typeface=\"DengXian\"/><a:font script=\"Hant\" typeface=\"新細明體\"/><a:font script=\"Arab\" typeface=\"Arial\"/><a:font script=\"Hebr\" typeface=\"Arial\"/><a:font script=\"Thai\" typeface=\"Tahoma\"/><a:font script=\"Ethi\" typeface=\"Nyala\"/><a:font script=\"Beng\" typeface=\"Vrinda\"/><a:font script=\"Gujr\" typeface=\"Shruti\"/><a:font script=\"Khmr\" typeface=\"DaunPenh\"/><a:font script=\"Knda\" typeface=\"Tunga\"/><a:font script=\"Guru\" typeface=\"Raavi\"/><a:font script=\"Cans\" typeface=\"Euphemia\"/><a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/><a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/><a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/><a:font script=\"Thaa\" typeface=\"MV Boli\"/><a:font script=\"Deva\" typeface=\"Mangal\"/><a:font script=\"Telu\" typeface=\"Gautami\"/><a:font script=\"Taml\" typeface=\"Latha\"/><a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/><a:font script=\"Orya\" typeface=\"Kalinga\"/><a:font script=\"Mlym\" typeface=\"Kartika\"/><a:font script=\"Laoo\" typeface=\"DokChampa\"/><a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/><a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/><a:font script=\"Viet\" typeface=\"Arial\"/><a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/><a:font script=\"Geor\" typeface=\"Sylfaen\"/></a:minorFont></a:fontScheme><a:fmtScheme name=\"Office\"><a:fillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"110000\"/><a:satMod val=\"105000\"/><a:tint val=\"67000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"103000\"/><a:tint val=\"73000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"109000\"/><a:tint val=\"81000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:satMod val=\"103000\"/><a:lumMod val=\"102000\"/><a:tint val=\"94000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:satMod val=\"110000\"/><a:lumMod val=\"100000\"/><a:shade val=\"100000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"99000\"/><a:satMod val=\"120000\"/><a:shade val=\"78000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad=\"57150\" dist=\"19050\" dir=\"5400000\" algn=\"ctr\" rotWithShape=\"0\"><a:srgbClr val=\"000000\"><a:alpha val=\"63000\"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:solidFill><a:schemeClr val=\"phClr\"><a:tint val=\"95000\"/><a:satMod val=\"170000\"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"93000\"/><a:satMod val=\"150000\"/><a:shade val=\"98000\"/><a:lumMod val=\"102000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:tint val=\"98000\"/><a:satMod val=\"130000\"/><a:shade val=\"90000\"/><a:lumMod val=\"103000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"63000\"/><a:satMod val=\"120000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/><a:extLst><a:ext uri=\"{05A4C25C-085E-4340-85A3-A5531E510DB2}\"><thm15:themeFamily xmlns:thm15=\"http://schemas.microsoft.com/office/thememl/2012/main\" name=\"Office Theme\" id=\"{62F939B6-93AF-4DB8-9C6B-D6C7DFDC589F}\" vid=\"{4A3C46E8-61CC-4603-A589-7422A47A8E4A}\"/></a:ext></a:extLst></a:theme>");
5373
- zipExcel.add("xl/workbook.xml", XML_DECL + "<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x15\" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\"><fileVersion appName=\"xl\" lastEdited=\"7\" lowestEdited=\"6\" rupBuild=\"10507\"/><workbookPr/><bookViews><workbookView xWindow=\"0\" yWindow=\"500\" windowWidth=\"20960\" windowHeight=\"15960\"/></bookViews><sheets><sheet name=\"Sheet1\" sheetId=\"1\" r:id=\"rId1\"/></sheets><calcPr calcId=\"0\" concurrentCalc=\"0\"/></workbook>\n");
5374
- zipExcel.add("xl/worksheets/_rels/sheet1.xml.rels", XML_DECL + relationships([relationship$2("rId1", OFFICE_REL + "table", "../tables/table1.xml")]) + "\n");
5939
+ {
5940
+ const override = (partName, contentType) => voidEl("Override", {
5941
+ PartName: partName,
5942
+ ContentType: contentType
5943
+ }, { openPrefix: " " });
5944
+ zipExcel.add("[Content_Types].xml", XML_DECL + el("Types", { xmlns: CT_NS }, [
5945
+ raw(voidEl("Default", {
5946
+ Extension: "rels",
5947
+ ContentType: "application/vnd.openxmlformats-package.relationships+xml"
5948
+ }, { openPrefix: " " })),
5949
+ raw(voidEl("Default", {
5950
+ Extension: "xml",
5951
+ ContentType: "application/xml"
5952
+ }, { openPrefix: " " })),
5953
+ raw(override("/xl/workbook.xml", SML_CT + "sheet.main+xml")),
5954
+ raw(override("/xl/worksheets/sheet1.xml", SML_CT + "worksheet+xml")),
5955
+ raw(override("/xl/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml")),
5956
+ raw(override("/xl/styles.xml", SML_CT + "styles+xml")),
5957
+ raw(override("/xl/sharedStrings.xml", SML_CT + "sharedStrings+xml")),
5958
+ raw(override("/xl/tables/table1.xml", SML_CT + "table+xml")),
5959
+ raw(override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml")),
5960
+ raw(override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml"))
5961
+ ]) + "\n");
5962
+ zipExcel.add("_rels/.rels", XML_DECL + relationships([
5963
+ relationship$2("rId1", PACKAGE_REL_NS + "/metadata/core-properties", "docProps/core.xml"),
5964
+ relationship$2("rId2", OFFICE_REL + "extended-properties", "docProps/app.xml"),
5965
+ relationship$2("rId3", OFFICE_REL + "officeDocument", "xl/workbook.xml")
5966
+ ]) + "\n");
5967
+ const headingPairs = el("vt:vector", {
5968
+ size: 2,
5969
+ baseType: "variant"
5970
+ }, [raw(el("vt:variant", null, raw(el("vt:lpstr", null, "Worksheets")))), raw(el("vt:variant", null, raw(el("vt:i4", null, 1))))]);
5971
+ zipExcel.add("docProps/app.xml", XML_DECL + el("Properties", {
5972
+ xmlns: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
5973
+ "xmlns:vt": "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
5974
+ }, [
5975
+ raw(el("Application", null, "Microsoft Macintosh Excel")),
5976
+ raw(el("DocSecurity", null, 0)),
5977
+ raw(el("ScaleCrop", null, "false")),
5978
+ raw(el("HeadingPairs", null, raw(headingPairs))),
5979
+ raw(el("TitlesOfParts", null, raw(el("vt:vector", {
5980
+ size: 1,
5981
+ baseType: "lpstr"
5982
+ }, raw(el("vt:lpstr", null, "Sheet1")))))),
5983
+ raw(el("Company", null)),
5984
+ raw(el("LinksUpToDate", null, "false")),
5985
+ raw(el("SharedDoc", null, "false")),
5986
+ raw(el("HyperlinksChanged", null, "false")),
5987
+ raw(el("AppVersion", null, "16.0300"))
5988
+ ]) + "\n");
5989
+ zipExcel.add("docProps/core.xml", XML_DECL + el("cp:coreProperties", {
5990
+ "xmlns:cp": "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
5991
+ "xmlns:dc": "http://purl.org/dc/elements/1.1/",
5992
+ "xmlns:dcterms": "http://purl.org/dc/terms/",
5993
+ "xmlns:dcmitype": "http://purl.org/dc/dcmitype/",
5994
+ "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance"
5995
+ }, [
5996
+ raw(el("dc:creator", null, "TsPptx")),
5997
+ raw(el("cp:lastModifiedBy", null, "TsPptx")),
5998
+ raw(el("dcterms:created", { "xsi:type": "dcterms:W3CDTF" }, (/* @__PURE__ */ new Date()).toISOString())),
5999
+ raw(el("dcterms:modified", { "xsi:type": "dcterms:W3CDTF" }, (/* @__PURE__ */ new Date()).toISOString()))
6000
+ ]));
6001
+ zipExcel.add("xl/_rels/workbook.xml.rels", XML_DECL + relationships([
6002
+ relationship$2("rId3", OFFICE_REL + "styles", "styles.xml"),
6003
+ relationship$2("rId2", OFFICE_REL + "theme", "theme/theme1.xml"),
6004
+ relationship$2("rId1", OFFICE_REL + "worksheet", "worksheets/sheet1.xml"),
6005
+ relationship$2("rId4", OFFICE_REL + "sharedStrings", "sharedStrings.xml")
6006
+ ]));
6007
+ zipExcel.add("xl/styles.xml", XML_DECL + XLSX_STYLES_XML);
6008
+ zipExcel.add("xl/theme/theme1.xml", XML_DECL + XLSX_THEME_XML);
6009
+ zipExcel.add("xl/workbook.xml", XML_DECL + el("workbook", {
6010
+ xmlns: SML_NS,
6011
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
6012
+ "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
6013
+ "mc:Ignorable": "x15",
6014
+ "xmlns:x15": "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"
6015
+ }, [
6016
+ raw(voidEl("fileVersion", {
6017
+ appName: "xl",
6018
+ lastEdited: 7,
6019
+ lowestEdited: 6,
6020
+ rupBuild: 10507
6021
+ })),
6022
+ raw(voidEl("workbookPr")),
6023
+ raw(el("bookViews", null, raw(voidEl("workbookView", {
6024
+ xWindow: 0,
6025
+ yWindow: 500,
6026
+ windowWidth: 20960,
6027
+ windowHeight: 15960
6028
+ })))),
6029
+ raw(el("sheets", null, raw(voidEl("sheet", {
6030
+ name: "Sheet1",
6031
+ sheetId: 1,
6032
+ "r:id": "rId1"
6033
+ })))),
6034
+ raw(voidEl("calcPr", {
6035
+ calcId: 0,
6036
+ concurrentCalc: 0
6037
+ }))
6038
+ ]) + "\n");
6039
+ zipExcel.add("xl/worksheets/_rels/sheet1.xml.rels", XML_DECL + relationships([relationship$2("rId1", OFFICE_REL + "table", "../tables/table1.xml")]) + "\n");
6040
+ }
5375
6041
  zipExcel.add("xl/sharedStrings.xml", buildXlsxSharedStrings(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
5376
6042
  zipExcel.add("xl/tables/table1.xml", buildXlsxTable(chartObject, data, intBubbleCols));
5377
6043
  zipExcel.add("xl/worksheets/sheet1.xml", buildXlsxSheet(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
@@ -5382,167 +6048,201 @@ function buildEmbeddedWorksheet(chartObject) {
5382
6048
  * Build the embedded workbook's `xl/sharedStrings.xml` (series names + category labels).
5383
6049
  */
5384
6050
  function buildXlsxSharedStrings(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES) {
5385
- let strSharedStrings = XML_DECL;
5386
- if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") strSharedStrings += `<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${intBubbleCols}" uniqueCount="${intBubbleCols}">`;
5387
- else if (chartObject.opts._type === "scatter") strSharedStrings += `<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${data.length}" uniqueCount="${data.length}">`;
6051
+ const isBubble = chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D";
6052
+ const isScatter = chartObject.opts._type === "scatter";
6053
+ let count;
6054
+ let uniqueCount;
6055
+ let blank = "";
6056
+ if (isBubble) count = uniqueCount = intBubbleCols;
6057
+ else if (isScatter) count = uniqueCount = data.length;
5388
6058
  else if (IS_MULTI_CAT_AXES) {
5389
6059
  let totCount = data.length + 1;
5390
6060
  dataLabels(data[0]).forEach((arrLabel) => totCount += arrLabel.filter((label) => label && label !== "").length);
5391
- strSharedStrings += `<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${totCount}" uniqueCount="${totCount}">`;
5392
- strSharedStrings += "<si><t/></si>";
6061
+ count = uniqueCount = totCount;
6062
+ blank = el("si", null, raw(voidEl("t")));
5393
6063
  } else {
5394
- const totCount = data.length + dataLabels(data[0]).length * firstLabelGroup(data[0]).length + dataLabels(data[0]).length;
5395
- const unqCount = data.length + dataLabels(data[0]).length * firstLabelGroup(data[0]).length + 1;
5396
- strSharedStrings += `<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${totCount}" uniqueCount="${unqCount}">`;
5397
- strSharedStrings += "<si><t xml:space=\"preserve\"></t></si>";
5398
- }
5399
- if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") data.forEach((objData, idx) => {
5400
- if (idx === 0) strSharedStrings += "<si><t>X-Axis</t></si>";
5401
- else {
5402
- strSharedStrings += `<si><t>${encodeXmlEntities(objData.name || `Y-Axis${idx}`)}</t></si>`;
5403
- strSharedStrings += `<si><t>${encodeXmlEntities(`Size${idx}`)}</t></si>`;
5404
- }
5405
- });
5406
- else data.forEach((objData) => {
5407
- strSharedStrings += `<si><t>${encodeXmlEntities((objData.name || " ").replace("X-Axis", "X-Values"))}</t></si>`;
5408
- });
5409
- if (chartObject.opts._type !== "bubble" && chartObject.opts._type !== "bubble3D" && chartObject.opts._type !== "scatter") dataLabels(data[0]).slice().reverse().forEach((labelsGroup) => {
5410
- labelsGroup.filter((label) => label && label !== "").forEach((label) => {
5411
- strSharedStrings += `<si><t>${encodeXmlEntities(label)}</t></si>`;
5412
- });
5413
- });
5414
- strSharedStrings += "</sst>\n";
5415
- return strSharedStrings;
6064
+ count = data.length + dataLabels(data[0]).length * firstLabelGroup(data[0]).length + dataLabels(data[0]).length;
6065
+ uniqueCount = data.length + dataLabels(data[0]).length * firstLabelGroup(data[0]).length + 1;
6066
+ blank = el("si", null, raw(el("t", { "xml:space": "preserve" })));
6067
+ }
6068
+ const names = isBubble ? data.map((objData, idx) => idx === 0 ? sharedString("X-Axis") : sharedString(objData.name || `Y-Axis${idx}`) + sharedString(`Size${idx}`)).join("") : data.map((objData) => sharedString((objData.name || " ").replace("X-Axis", "X-Values"))).join("");
6069
+ const labels = isBubble || isScatter ? "" : dataLabels(data[0]).slice().reverse().map((labelsGroup) => labelsGroup.filter((label) => label && label !== "").map((label) => sharedString(label)).join("")).join("");
6070
+ return XML_DECL + el("sst", {
6071
+ xmlns: SML_NS,
6072
+ count,
6073
+ uniqueCount
6074
+ }, [
6075
+ raw(blank),
6076
+ raw(names),
6077
+ raw(labels)
6078
+ ]) + "\n";
5416
6079
  }
5417
6080
  /**
5418
6081
  * Build the embedded workbook's `xl/tables/table1.xml` (the data table over the sheet range).
5419
6082
  */
5420
6083
  function buildXlsxTable(chartObject, data, intBubbleCols) {
5421
- let strTableXml = XML_DECL;
6084
+ const labelCols = dataLabels(data[0]).length;
6085
+ let ref;
6086
+ let columns;
5422
6087
  if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") {
5423
- strTableXml += `<table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="A1:${getExcelColName(intBubbleCols)}${intBubbleCols}" totalsRowShown="0">`;
5424
- strTableXml += `<tableColumns count="${intBubbleCols}">`;
6088
+ ref = `A1:${getExcelColName(intBubbleCols)}${intBubbleCols}`;
5425
6089
  let idxColLtr = 1;
5426
- data.forEach((obj, idx) => {
5427
- if (idx === 0) strTableXml += `<tableColumn id="${idx + 1}" name="X-Values"/>`;
5428
- else {
5429
- strTableXml += `<tableColumn id="${idx + idxColLtr}" name="${obj.name}"/>`;
5430
- idxColLtr++;
5431
- strTableXml += `<tableColumn id="${idx + idxColLtr}" name="Size${idx}"/>`;
5432
- }
5433
- });
6090
+ columns = data.map((obj, idx) => {
6091
+ if (idx === 0) return voidEl("tableColumn", {
6092
+ id: idx + 1,
6093
+ name: "X-Values"
6094
+ });
6095
+ const nameCol = voidEl("tableColumn", {
6096
+ id: idx + idxColLtr,
6097
+ name: obj.name
6098
+ });
6099
+ idxColLtr++;
6100
+ return nameCol + voidEl("tableColumn", {
6101
+ id: idx + idxColLtr,
6102
+ name: `Size${idx}`
6103
+ });
6104
+ }).join("");
5434
6105
  } else if (chartObject.opts._type === "scatter") {
5435
- strTableXml += `<table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="A1:${getExcelColName(data.length)}${dataValues(data[0]).length + 1}" totalsRowShown="0">`;
5436
- strTableXml += `<tableColumns count="${data.length}">`;
5437
- data.forEach((_obj, idx) => {
5438
- strTableXml += `<tableColumn id="${idx + 1}" name="${idx === 0 ? "X-Values" : "Y-Value "}${idx}"/>`;
5439
- });
6106
+ ref = `A1:${getExcelColName(data.length)}${dataValues(data[0]).length + 1}`;
6107
+ columns = data.map((_obj, idx) => voidEl("tableColumn", {
6108
+ id: idx + 1,
6109
+ name: `${idx === 0 ? "X-Values" : "Y-Value "}${idx}`
6110
+ })).join("");
5440
6111
  } else {
5441
- strTableXml += `<table xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" id="1" name="Table1" displayName="Table1" ref="A1:${getExcelColName(data.length + dataLabels(data[0]).length)}${firstLabelGroup(data[0]).length + 1}" totalsRowShown="0">`;
5442
- strTableXml += `<tableColumns count="${data.length + dataLabels(data[0]).length}">`;
5443
- dataLabels(data[0]).forEach((_labelsGroup, idx) => {
5444
- strTableXml += `<tableColumn id="${idx + 1}" name="Column${idx + 1}"/>`;
5445
- });
5446
- data.forEach((obj, idx) => {
5447
- strTableXml += `<tableColumn id="${idx + dataLabels(data[0]).length + 1}" name="${encodeXmlEntities(obj.name ?? "")}"/>`;
5448
- });
6112
+ ref = `A1:${getExcelColName(data.length + labelCols)}${firstLabelGroup(data[0]).length + 1}`;
6113
+ columns = dataLabels(data[0]).map((_labelsGroup, idx) => voidEl("tableColumn", {
6114
+ id: idx + 1,
6115
+ name: `Column${idx + 1}`
6116
+ })).join("") + data.map((obj, idx) => voidEl("tableColumn", {
6117
+ id: idx + labelCols + 1,
6118
+ name: obj.name ?? ""
6119
+ })).join("");
5449
6120
  }
5450
- strTableXml += "</tableColumns>";
5451
- strTableXml += "<tableStyleInfo showFirstColumn=\"0\" showLastColumn=\"0\" showRowStripes=\"1\" showColumnStripes=\"0\"/>";
5452
- strTableXml += "</table>";
5453
- return strTableXml;
6121
+ const columnCount = chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D" ? intBubbleCols : chartObject.opts._type === "scatter" ? data.length : data.length + labelCols;
6122
+ return XML_DECL + el("table", {
6123
+ xmlns: SML_NS,
6124
+ id: 1,
6125
+ name: "Table1",
6126
+ displayName: "Table1",
6127
+ ref,
6128
+ totalsRowShown: 0
6129
+ }, [raw(el("tableColumns", { count: columnCount }, raw(columns))), raw(voidEl("tableStyleInfo", {
6130
+ showFirstColumn: 0,
6131
+ showLastColumn: 0,
6132
+ showRowStripes: 1,
6133
+ showColumnStripes: 0
6134
+ }))]);
5454
6135
  }
5455
6136
  /**
5456
6137
  * Build the embedded workbook's `xl/worksheets/sheet1.xml` (header row + per-series data rows).
5457
6138
  */
5458
6139
  function buildXlsxSheet(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES) {
5459
- let strSheetXml = XML_DECL;
5460
- strSheetXml += "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x14ac\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\">";
5461
- if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") strSheetXml += `<dimension ref="A1:${getExcelColName(intBubbleCols)}${dataValues(data[0]).length + 1}"/>`;
5462
- else if (chartObject.opts._type === "scatter") strSheetXml += `<dimension ref="A1:${getExcelColName(data.length)}${dataValues(data[0]).length + 1}"/>`;
5463
- else strSheetXml += `<dimension ref="A1:${getExcelColName(data.length + dataLabels(data[0]).length)}${dataValues(data[0]).length + 1}"/>`;
5464
- strSheetXml += "<sheetViews><sheetView tabSelected=\"1\" workbookViewId=\"0\"><selection activeCell=\"B1\" sqref=\"B1\"/></sheetView></sheetViews>";
5465
- strSheetXml += "<sheetFormatPr baseColWidth=\"10\" defaultRowHeight=\"16\"/>";
5466
- if (chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D") {
5467
- strSheetXml += "<sheetData>";
5468
- strSheetXml += `<row r="1" spans="1:${intBubbleCols}">`;
5469
- strSheetXml += "<c r=\"A1\" t=\"s\"><v>0</v></c>";
5470
- for (let idx = 1; idx < intBubbleCols; idx++) strSheetXml += `<c r="${getExcelColName(idx + 1)}1" t="s"><v>${idx}</v></c>`;
5471
- strSheetXml += "</row>";
6140
+ const isBubble = chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D";
6141
+ const isScatter = chartObject.opts._type === "scatter";
6142
+ const labelCols = dataLabels(data[0]).length;
6143
+ /** One cell. `t="s"` marks a shared-string index; without it the value is a number. */
6144
+ const cell = (col, row, value, shared = false) => el("c", {
6145
+ r: `${getExcelColName(col)}${row}`,
6146
+ t: shared ? "s" : void 0
6147
+ }, raw(el("v", null, value)));
6148
+ /** One row, spanning the sheet's full column count. */
6149
+ const sheetRow = (row, span, cells) => el("row", {
6150
+ r: row,
6151
+ spans: `1:${span}`
6152
+ }, raw(cells));
6153
+ const colCount = isBubble ? intBubbleCols : isScatter ? data.length : data.length + labelCols;
6154
+ let rows = "";
6155
+ if (isBubble) {
6156
+ let header = "";
6157
+ for (let idx = 0; idx < intBubbleCols; idx++) header += cell(idx + 1, 1, idx, true);
6158
+ rows += sheetRow(1, intBubbleCols, header);
5472
6159
  dataValues(data[0]).forEach((val, idx) => {
5473
- strSheetXml += `<row r="${idx + 2}" spans="1:${intBubbleCols}">`;
5474
- strSheetXml += `<c r="A${idx + 2}"><v>${val}</v></c>`;
6160
+ let cells = cell(1, idx + 2, val);
5475
6161
  let idxColLtr = 2;
5476
6162
  for (let idy = 1; idy < data.length; idy++) {
5477
- strSheetXml += `<c r="${getExcelColName(idxColLtr)}${idx + 2}"><v>${dataValues(data[idy])[idx] ?? ""}</v></c>`;
5478
- idxColLtr++;
5479
- strSheetXml += `<c r="${getExcelColName(idxColLtr)}${idx + 2}"><v>${dataSizes(data[idy])[idx] ?? ""}</v></c>`;
5480
- idxColLtr++;
6163
+ cells += cell(idxColLtr++, idx + 2, dataValues(data[idy])[idx] ?? "");
6164
+ cells += cell(idxColLtr++, idx + 2, dataSizes(data[idy])[idx] ?? "");
5481
6165
  }
5482
- strSheetXml += "</row>";
6166
+ rows += sheetRow(idx + 2, intBubbleCols, cells);
5483
6167
  });
5484
- } else if (chartObject.opts._type === "scatter") {
5485
- strSheetXml += "<sheetData>";
5486
- strSheetXml += `<row r="1" spans="1:${data.length}">`;
5487
- for (let idx = 0; idx < data.length; idx++) strSheetXml += `<c r="${getExcelColName(idx + 1)}1" t="s"><v>${idx}</v></c>`;
5488
- strSheetXml += "</row>";
6168
+ } else if (isScatter) {
6169
+ let header = "";
6170
+ for (let idx = 0; idx < data.length; idx++) header += cell(idx + 1, 1, idx, true);
6171
+ rows += sheetRow(1, data.length, header);
5489
6172
  dataValues(data[0]).forEach((val, idx) => {
5490
- strSheetXml += `<row r="${idx + 2}" spans="1:${data.length}">`;
5491
- strSheetXml += `<c r="A${idx + 2}"><v>${val}</v></c>`;
5492
- for (let idy = 1; idy < data.length; idy++) strSheetXml += `<c r="${getExcelColName(idy + 1)}${idx + 2}"><v>${dataValues(data[idy])[idx] || dataValues(data[idy])[idx] === 0 ? dataValues(data[idy])[idx] : ""}</v></c>`;
5493
- strSheetXml += "</row>";
6173
+ let cells = cell(1, idx + 2, val);
6174
+ for (let idy = 1; idy < data.length; idy++) {
6175
+ const yValue = dataValues(data[idy])[idx];
6176
+ cells += cell(idy + 1, idx + 2, yValue || yValue === 0 ? yValue : "");
6177
+ }
6178
+ rows += sheetRow(idx + 2, data.length, cells);
5494
6179
  });
6180
+ } else if (!IS_MULTI_CAT_AXES) {
6181
+ let header = "";
6182
+ for (let idx = 0; idx < labelCols; idx++) header += cell(idx + 1, 1, 0, true);
6183
+ for (let idx = 0; idx < data.length; idx++) header += cell(idx + 1 + labelCols, 1, idx + 1, true);
6184
+ rows += sheetRow(1, data.length + labelCols, header);
6185
+ const rowCount = firstLabelGroup(data[0]).length || Math.max(0, ...data.map((series) => dataValues(series).length));
6186
+ for (let idx = 0; idx < rowCount; idx++) {
6187
+ let cells = "";
6188
+ for (let idx2 = labelCols - 1; idx2 >= 0; idx2--) cells += cell(labelCols - idx2, idx + 2, data.length + idx + 1, true);
6189
+ for (let idy = 0; idy < data.length; idy++) cells += cell(labelCols + idy + 1, idx + 2, dataValues(data[idy])[idx] ?? "");
6190
+ rows += sheetRow(idx + 2, data.length + labelCols, cells);
6191
+ }
5495
6192
  } else {
5496
- strSheetXml += "<sheetData>";
5497
- if (!IS_MULTI_CAT_AXES) {
5498
- strSheetXml += `<row r="1" spans="1:${data.length + dataLabels(data[0]).length}">`;
5499
- dataLabels(data[0]).forEach((_labelsGroup, idx) => {
5500
- strSheetXml += `<c r="${getExcelColName(idx + 1)}1" t="s"><v>0</v></c>`;
5501
- });
5502
- for (let idx = 0; idx < data.length; idx++) strSheetXml += `<c r="${getExcelColName(idx + 1 + dataLabels(data[0]).length)}1" t="s"><v>${idx + 1}</v></c>`;
5503
- strSheetXml += "</row>";
5504
- const rowCount = firstLabelGroup(data[0]).length || Math.max(0, ...data.map((series) => dataValues(series).length));
5505
- for (let idx = 0; idx < rowCount; idx++) {
5506
- strSheetXml += `<row r="${idx + 2}" spans="1:${data.length + dataLabels(data[0]).length}">`;
5507
- for (let idx2 = dataLabels(data[0]).length - 1; idx2 >= 0; idx2--) {
5508
- strSheetXml += `<c r="${getExcelColName(dataLabels(data[0]).length - idx2)}${idx + 2}" t="s">`;
5509
- strSheetXml += `<v>${data.length + idx + 1}</v>`;
5510
- strSheetXml += "</c>";
5511
- }
5512
- for (let idy = 0; idy < data.length; idy++) strSheetXml += `<c r="${getExcelColName(dataLabels(data[0]).length + idy + 1)}${idx + 2}"><v>${dataValues(data[idy])[idx] ?? ""}</v></c>`;
5513
- strSheetXml += "</row>";
5514
- }
5515
- } else {
5516
- const TOT_SER = data.length;
5517
- const TOT_CAT = firstLabelGroup(data[0]).length;
5518
- const TOT_LVL = dataLabels(data[0]).length;
5519
- const revLabelGroups = dataLabels(data[0]).slice().reverse();
5520
- const ssLabelMap = /* @__PURE__ */ new Map();
5521
- let ssIdx = TOT_SER + 1;
5522
- revLabelGroups.forEach((labelsGroup, revLevelIdx) => {
5523
- labelsGroup.forEach((label, rowIdx) => {
5524
- if (label && label !== "") ssLabelMap.set(`${revLevelIdx}:${rowIdx}`, ssIdx++);
5525
- });
6193
+ const TOT_SER = data.length;
6194
+ const TOT_CAT = firstLabelGroup(data[0]).length;
6195
+ const revLabelGroups = dataLabels(data[0]).slice().reverse();
6196
+ const ssLabelMap = /* @__PURE__ */ new Map();
6197
+ let ssIdx = TOT_SER + 1;
6198
+ revLabelGroups.forEach((labelsGroup, revLevelIdx) => {
6199
+ labelsGroup.forEach((label, rowIdx) => {
6200
+ if (label && label !== "") ssLabelMap.set(`${revLevelIdx}:${rowIdx}`, ssIdx++);
5526
6201
  });
5527
- strSheetXml += `<row r="1" spans="1:${TOT_SER + TOT_LVL}">`;
5528
- for (let col = 1; col <= TOT_LVL; col++) strSheetXml += `<c r="${getExcelColName(col)}1" t="s"><v>0</v></c>`;
5529
- for (let ser = 0; ser < TOT_SER; ser++) strSheetXml += `<c r="${getExcelColName(TOT_LVL + ser + 1)}1" t="s"><v>${ser + 1}</v></c>`;
5530
- strSheetXml += "</row>";
5531
- for (let idx = 0; idx < TOT_CAT; idx++) {
5532
- strSheetXml += `<row r="${idx + 2}" spans="1:${TOT_SER + TOT_LVL}">`;
5533
- revLabelGroups.forEach((labelsGroup, idy) => {
5534
- const colLabel = labelsGroup[idx];
5535
- if (colLabel && colLabel !== "") strSheetXml += `<c r="${getExcelColName(idy + 1)}${idx + 2}" t="s"><v>${ssLabelMap.get(`${idy}:${idx}`)}</v></c>`;
5536
- });
5537
- for (let idy = 0; idy < TOT_SER; idy++) strSheetXml += `<c r="${getExcelColName(TOT_LVL + idy + 1)}${idx + 2}"><v>${dataValues(data[idy])[idx] ?? ""}</v></c>`;
5538
- strSheetXml += "</row>";
5539
- }
6202
+ });
6203
+ let header = "";
6204
+ for (let col = 1; col <= labelCols; col++) header += cell(col, 1, 0, true);
6205
+ for (let ser = 0; ser < TOT_SER; ser++) header += cell(labelCols + ser + 1, 1, ser + 1, true);
6206
+ rows += sheetRow(1, TOT_SER + labelCols, header);
6207
+ for (let idx = 0; idx < TOT_CAT; idx++) {
6208
+ let cells = revLabelGroups.map((labelsGroup, idy) => {
6209
+ const colLabel = labelsGroup[idx];
6210
+ return colLabel && colLabel !== "" ? cell(idy + 1, idx + 2, ssLabelMap.get(`${idy}:${idx}`) ?? "", true) : "";
6211
+ }).join("");
6212
+ for (let idy = 0; idy < TOT_SER; idy++) cells += cell(labelCols + idy + 1, idx + 2, dataValues(data[idy])[idx] ?? "");
6213
+ rows += sheetRow(idx + 2, TOT_SER + labelCols, cells);
5540
6214
  }
5541
6215
  }
5542
- strSheetXml += "</sheetData>";
5543
- strSheetXml += "<pageMargins left=\"0.7\" right=\"0.7\" top=\"0.75\" bottom=\"0.75\" header=\"0.3\" footer=\"0.3\"/>";
5544
- strSheetXml += "</worksheet>\n";
5545
- return strSheetXml;
6216
+ const sheetView = el("sheetViews", null, raw(el("sheetView", {
6217
+ tabSelected: 1,
6218
+ workbookViewId: 0
6219
+ }, raw(voidEl("selection", {
6220
+ activeCell: "B1",
6221
+ sqref: "B1"
6222
+ })))));
6223
+ return XML_DECL + el("worksheet", {
6224
+ xmlns: SML_NS,
6225
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
6226
+ "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
6227
+ "mc:Ignorable": "x14ac",
6228
+ "xmlns:x14ac": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"
6229
+ }, [
6230
+ raw(voidEl("dimension", { ref: `A1:${getExcelColName(colCount)}${dataValues(data[0]).length + 1}` })),
6231
+ raw(sheetView),
6232
+ raw(voidEl("sheetFormatPr", {
6233
+ baseColWidth: 10,
6234
+ defaultRowHeight: 16
6235
+ })),
6236
+ raw(el("sheetData", null, raw(rows))),
6237
+ raw(voidEl("pageMargins", {
6238
+ left: .7,
6239
+ right: .7,
6240
+ top: .75,
6241
+ bottom: .75,
6242
+ header: .3,
6243
+ footer: .3
6244
+ }))
6245
+ ]) + "\n";
5546
6246
  }
5547
6247
  /**
5548
6248
  * Build the standalone `.rels` for a chart part: a single `rId1` relationship to
@@ -7012,17 +7712,17 @@ function fallbackPic(picId, objectName, altText, model, xf) {
7012
7712
  return el("p:pic", null, [
7013
7713
  raw(el("p:nvPicPr", null, [
7014
7714
  raw(cNvPrOpen(picId, objectName, altText) + "/>"),
7015
- raw(el("p:cNvPicPr", null, raw(voidEl("a:picLocks", {
7016
- noGrp: "1",
7017
- noRot: "1",
7018
- noChangeAspect: "1",
7019
- noMove: "1",
7020
- noResize: "1",
7021
- noEditPoints: "1",
7022
- noAdjustHandles: "1",
7023
- noChangeArrowheads: "1",
7024
- noChangeShapeType: "1",
7025
- noCrop: "1"
7715
+ raw(el("p:cNvPicPr", null, raw(genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
7716
+ noGrp: true,
7717
+ noRot: true,
7718
+ noChangeAspect: true,
7719
+ noMove: true,
7720
+ noResize: true,
7721
+ noEditPoints: true,
7722
+ noAdjustHandles: true,
7723
+ noChangeArrowheads: true,
7724
+ noChangeShapeType: true,
7725
+ noCrop: true
7026
7726
  })))),
7027
7727
  raw(voidEl("p:nvPr"))
7028
7728
  ])),
@@ -7064,7 +7764,7 @@ function renderModel3dObject(slideItemObj, idx, x, y, cx, cy) {
7064
7764
  const graphicFrame = el("p:graphicFrame", null, [
7065
7765
  raw(el("p:nvGraphicFramePr", null, [
7066
7766
  raw(cNvPrOpen(idx + 2, opts.objectName, altText) + "/>"),
7067
- raw(el("p:cNvGraphicFramePr", null, raw(voidEl("a:graphicFrameLocks")))),
7767
+ raw(el("p:cNvGraphicFramePr", null, raw(genXmlObjectLock("a:graphicFrameLocks", GRAPHIC_FRAME_LOCK_ATTRS, opts.objectLock, opts.objectName) || voidEl("a:graphicFrameLocks")))),
7068
7768
  raw(voidEl("p:nvPr"))
7069
7769
  ])),
7070
7770
  raw(el("p:xfrm", null, [raw(voidEl("a:off", {
@@ -7355,7 +8055,7 @@ function renderTableObject(slideItemObj, idx, x, y, cx, cy, placeholderObj, item
7355
8055
  let intColCnt = 0;
7356
8056
  let tblInner = "";
7357
8057
  let cellOpts = null;
7358
- arrTabRows = (slideItemObj.arrTabRows ?? []).map((row) => [...row]);
8058
+ arrTabRows = withCheckedSpans((slideItemObj.arrTabRows ?? []).map((row) => [...row]));
7359
8059
  objTabOpts = itemOpts;
7360
8060
  intColCnt = 0;
7361
8061
  (arrTabRows[0] ?? []).forEach((cell) => {
@@ -7639,16 +8339,16 @@ function zoomFallbackPic(picId, objectName, tile, absXf) {
7639
8339
  "r:id": `rId${tile.fallbackSlideRid}`,
7640
8340
  action: "ppaction://hlinksldjump"
7641
8341
  }) + "</p:cNvPr>"),
7642
- raw(el("p:cNvPicPr", null, raw(voidEl("a:picLocks", {
7643
- noGrp: "1",
7644
- noRot: "1",
7645
- noChangeAspect: "1",
7646
- noMove: "1",
7647
- noResize: "1",
7648
- noEditPoints: "1",
7649
- noAdjustHandles: "1",
7650
- noChangeArrowheads: "1",
7651
- noChangeShapeType: "1"
8342
+ raw(el("p:cNvPicPr", null, raw(genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
8343
+ noGrp: true,
8344
+ noRot: true,
8345
+ noChangeAspect: true,
8346
+ noMove: true,
8347
+ noResize: true,
8348
+ noEditPoints: true,
8349
+ noAdjustHandles: true,
8350
+ noChangeArrowheads: true,
8351
+ noChangeShapeType: true
7652
8352
  })))),
7653
8353
  raw(voidEl("p:nvPr"))
7654
8354
  ])),
@@ -7675,7 +8375,8 @@ function zoomFallbackPic(picId, objectName, tile, absXf) {
7675
8375
  function renderZoomObject(slideItemObj, idx, x, y, cx, cy) {
7676
8376
  const zoom = slideItemObj.zoom;
7677
8377
  if (!zoom) return "";
7678
- const objectName = (slideItemObj.options || {}).objectName;
8378
+ const opts = slideItemObj.options || {};
8379
+ const objectName = opts.objectName;
7679
8380
  const { uri, prefix, zm } = ZOOM_VARIANTS[zoom.variant];
7680
8381
  const firstTile = zoom.tiles[0];
7681
8382
  if (!firstTile) return "";
@@ -7693,7 +8394,10 @@ function renderZoomObject(slideItemObj, idx, x, y, cx, cy) {
7693
8394
  const graphicFrame = el("p:graphicFrame", null, [
7694
8395
  raw(el("p:nvGraphicFramePr", null, [
7695
8396
  raw(cNvPrOpen(idx + 2, objectName, "") + "/>"),
7696
- raw(el("p:cNvGraphicFramePr", null, raw(voidEl("a:graphicFrameLocks", { noChangeAspect: "1" })))),
8397
+ raw(el("p:cNvGraphicFramePr", null, raw(genXmlObjectLock("a:graphicFrameLocks", GRAPHIC_FRAME_LOCK_ATTRS, {
8398
+ noChangeAspect: true,
8399
+ ...opts.objectLock
8400
+ }, objectName)))),
7697
8401
  raw(voidEl("p:nvPr"))
7698
8402
  ])),
7699
8403
  raw(el("p:xfrm", null, [raw(voidEl("a:off", {
@@ -8352,56 +9056,18 @@ function buildThemeClrScheme(scheme) {
8352
9056
  return raw(el(`a:${slot}`, null, child));
8353
9057
  }));
8354
9058
  }
8355
- const MAJOR_FONT_LIST = [
8356
- ["Jpan", "游ゴシック Light"],
8357
- ["Hang", "맑은 고딕"],
8358
- ["Hans", "等线 Light"],
8359
- ["Hant", "新細明體"],
8360
- ["Arab", "Times New Roman"],
8361
- ["Hebr", "Times New Roman"],
8362
- ["Thai", "Angsana New"],
8363
- ["Ethi", "Nyala"],
8364
- ["Beng", "Vrinda"],
8365
- ["Gujr", "Shruti"],
8366
- ["Khmr", "MoolBoran"],
8367
- ["Knda", "Tunga"],
8368
- ["Guru", "Raavi"],
8369
- ["Cans", "Euphemia"],
8370
- ["Cher", "Plantagenet Cherokee"],
8371
- ["Yiii", "Microsoft Yi Baiti"],
8372
- ["Tibt", "Microsoft Himalaya"],
8373
- ["Thaa", "MV Boli"],
8374
- ["Deva", "Mangal"],
8375
- ["Telu", "Gautami"],
8376
- ["Taml", "Latha"],
8377
- ["Syrc", "Estrangelo Edessa"],
8378
- ["Orya", "Kalinga"],
8379
- ["Mlym", "Kartika"],
8380
- ["Laoo", "DokChampa"],
8381
- ["Sinh", "Iskoola Pota"],
8382
- ["Mong", "Mongolian Baiti"],
8383
- ["Viet", "Times New Roman"],
8384
- ["Uigh", "Microsoft Uighur"],
8385
- ["Geor", "Sylfaen"],
8386
- ["Armn", "Arial"],
8387
- ["Bugi", "Leelawadee UI"],
8388
- ["Bopo", "Microsoft JhengHei"],
8389
- ["Java", "Javanese Text"],
8390
- ["Lisu", "Segoe UI"],
8391
- ["Mymr", "Myanmar Text"],
8392
- ["Nkoo", "Ebrima"],
8393
- ["Olck", "Nirmala UI"],
8394
- ["Osma", "Ebrima"],
8395
- ["Phag", "Phagspa"],
8396
- ["Syrn", "Estrangelo Edessa"],
8397
- ["Syrj", "Estrangelo Edessa"],
8398
- ["Syre", "Estrangelo Edessa"],
8399
- ["Sora", "Nirmala UI"],
8400
- ["Tale", "Microsoft Tai Le"],
8401
- ["Talu", "Microsoft New Tai Lue"],
8402
- ["Tfng", "Ebrima"]
8403
- ];
8404
- const MINOR_FONT_LIST = [
9059
+ /**
9060
+ * The per-script theme font fallback table (`a:fontScheme`'s CT_TextFont lists), in OOXML
9061
+ * document order. Static Office defaults — no caller data, no escaping risk — unlike the
9062
+ * `<a:latin>`/`<a:ea>`/`<a:cs>` slots above.
9063
+ *
9064
+ * Forty-one of the forty-seven scripts take the same face in the major and minor lists, so the
9065
+ * shared table below is the whole thing and the two override maps hold the six-odd rows that
9066
+ * actually differ. Written out twice, ninety-four lines said less than these do, and one row
9067
+ * mistyped in one copy produces a theme that opens fine and paints a script in the wrong face —
9068
+ * a difference nobody reviewing a diff of two 47-row tables would see.
9069
+ */
9070
+ const THEME_FONT_LIST = [
8405
9071
  ["Jpan", "游ゴシック"],
8406
9072
  ["Hang", "맑은 고딕"],
8407
9073
  ["Hans", "等线"],
@@ -8450,6 +9116,24 @@ const MINOR_FONT_LIST = [
8450
9116
  ["Talu", "Microsoft New Tai Lue"],
8451
9117
  ["Tfng", "Ebrima"]
8452
9118
  ];
9119
+ /**
9120
+ * The seven scripts whose *heading* face differs from the body one. Latin and CJK headings take
9121
+ * the Light weight; the scripts with no Light cut take a serif (or, for Khmer, a different face
9122
+ * entirely) where the body takes a sans. Every other script in {@link THEME_FONT_LIST} uses one
9123
+ * face for both.
9124
+ */
9125
+ const MAJOR_FONT_OVERRIDES = {
9126
+ Jpan: "游ゴシック Light",
9127
+ Hans: "等线 Light",
9128
+ Arab: "Times New Roman",
9129
+ Hebr: "Times New Roman",
9130
+ Thai: "Angsana New",
9131
+ Khmr: "MoolBoran",
9132
+ Viet: "Times New Roman"
9133
+ };
9134
+ /** The major (heading) and minor (body) lists, both derived from the one table. */
9135
+ const MAJOR_FONT_LIST = THEME_FONT_LIST.map(([script, face]) => [script, MAJOR_FONT_OVERRIDES[script] ?? face]);
9136
+ const MINOR_FONT_LIST = THEME_FONT_LIST;
8453
9137
  function fontListChildren(scriptFaces) {
8454
9138
  return scriptFaces.map(([script, typeface]) => raw(voidEl("a:font", {
8455
9139
  script,
@@ -9390,20 +10074,7 @@ function makeXmlMaster(slide, layouts) {
9390
10074
  id: LAYOUT_IDX_SERIES_BASE + idx,
9391
10075
  "r:id": `rId${slide._rels.length + idx + 1}`
9392
10076
  })).join("");
9393
- const clrMap = voidEl("p:clrMap", {
9394
- bg1: "lt1",
9395
- tx1: "dk1",
9396
- bg2: "lt2",
9397
- tx2: "dk2",
9398
- accent1: "accent1",
9399
- accent2: "accent2",
9400
- accent3: "accent3",
9401
- accent4: "accent4",
9402
- accent5: "accent5",
9403
- accent6: "accent6",
9404
- hlink: "hlink",
9405
- folHlink: "folHlink"
9406
- });
10077
+ const clrMap = voidEl("p:clrMap", DEFAULT_COLOR_MAP);
9407
10078
  const hf = voidEl("p:hf", {
9408
10079
  sldNum: slide._slideNumberProps ? null : 0,
9409
10080
  hdr: 0,
@@ -9671,7 +10342,7 @@ async function writePackage(source, props) {
9671
10342
  * the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
9672
10343
  * license text is in `LICENSE` at the repository root.
9673
10344
  */
9674
- const VERSION = "3.5.0";
10345
+ const VERSION = "3.7.0";
9675
10346
  function standardLayoutToPresLayout(layout) {
9676
10347
  return {
9677
10348
  name: layout.name,
@@ -10651,6 +11322,6 @@ function textRuns(runs) {
10651
11322
  return runs;
10652
11323
  }
10653
11324
  //#endregion
10654
- export { fetchMediaBase64 as a, PresentationCore as c, fetchFontBytes as i, textRuns as n, isRemote as o, clipPath as r, placeholderSvgPreview as s, textRun as t };
11325
+ export { fetchMediaBase64 as a, PresentationCore as c, fetchFontBytes as i, IMG_SVG_PLACEHOLDER as l, textRuns as n, isRemote as o, clipPath as r, placeholderSvgPreview as s, textRun as t };
10655
11326
 
10656
- //# sourceMappingURL=text-D04NUMvA.js.map
11327
+ //# sourceMappingURL=text-B8SLEaZx.js.map