@shbernal/ts-pptx 3.5.0 → 3.6.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 (65) hide show
  1. package/dist/{autopage-BYlTzX-l.js → autopage-DkjYZUFD.js} +3 -3
  2. package/dist/{autopage-BYlTzX-l.js.map → autopage-DkjYZUFD.js.map} +1 -1
  3. package/dist/browser.d.ts +5 -5
  4. package/dist/browser.js +4 -4
  5. package/dist/browser.js.map +1 -1
  6. package/dist/{diagnostics-BDuR8UkG.d.ts → diagnostics-Du_s75MT.d.ts} +20 -5
  7. package/dist/diagnostics-Du_s75MT.d.ts.map +1 -0
  8. package/dist/{errors-CgnueTYL.d.ts → errors-PflMPWBn.d.ts} +2 -2
  9. package/dist/{errors-CgnueTYL.d.ts.map → errors-PflMPWBn.d.ts.map} +1 -1
  10. package/dist/{fit-TjpNbMru.js → fit-BKs0zxMI.js} +3 -3
  11. package/dist/{fit-TjpNbMru.js.map → fit-BKs0zxMI.js.map} +1 -1
  12. package/dist/{html-dom-yUSdCExA.js → html-dom-B0I0QSbU.js} +4 -4
  13. package/dist/{html-dom-yUSdCExA.js.map → html-dom-B0I0QSbU.js.map} +1 -1
  14. package/dist/html.d.ts +3 -3
  15. package/dist/html.js +1 -1
  16. package/dist/index.d.ts +5 -5
  17. package/dist/index.js +3 -3
  18. package/dist/inspect.d.ts +1 -1
  19. package/dist/inspect.js +4 -5
  20. package/dist/inspect.js.map +1 -1
  21. package/dist/math.d.ts +1 -1
  22. package/dist/measure.d.ts +2 -2
  23. package/dist/measure.js +1 -1
  24. package/dist/{namespaces-CcKuzeuE.js → namespaces-BVnNu7sg.js} +74 -3
  25. package/dist/namespaces-BVnNu7sg.js.map +1 -0
  26. package/dist/node.d.ts +5 -5
  27. package/dist/node.js +3 -3
  28. package/dist/{notes-DNqAggXx.js → notes-RLCwdlTE.js} +289 -313
  29. package/dist/{notes-DNqAggXx.js.map → notes-RLCwdlTE.js.map} +1 -1
  30. package/dist/{pres-Dybf7ZUl.d.ts → pres-C5__xkDh.d.ts} +2 -2
  31. package/dist/{pres-Dybf7ZUl.d.ts.map → pres-C5__xkDh.d.ts.map} +1 -1
  32. package/dist/{presentation-VfJ_FT7E.js → presentation-Cp7vzlAw.js} +414 -515
  33. package/dist/presentation-Cp7vzlAw.js.map +1 -0
  34. package/dist/{presentation-types-CpkTx9Xa.d.ts → presentation-types-DP_8jog7.d.ts} +351 -274
  35. package/dist/presentation-types-DP_8jog7.d.ts.map +1 -0
  36. package/dist/read.d.ts +31 -48
  37. package/dist/read.d.ts.map +1 -1
  38. package/dist/read.js +3 -3
  39. package/dist/script.d.ts +2 -2
  40. package/dist/script.d.ts.map +1 -1
  41. package/dist/script.js +18 -47
  42. package/dist/script.js.map +1 -1
  43. package/dist/{shapes-Kt51vry6.js → shapes-BemroU-z.js} +625 -265
  44. package/dist/shapes-BemroU-z.js.map +1 -0
  45. package/dist/{slide-B_aInw3N.d.ts → slide-js0QpRV-.d.ts} +1278 -1254
  46. package/dist/slide-js0QpRV-.d.ts.map +1 -0
  47. package/dist/{text-D04NUMvA.js → text-CyetbF6p.js} +2039 -1417
  48. package/dist/text-CyetbF6p.js.map +1 -0
  49. package/dist/units-1kRzYShk.js +231 -0
  50. package/dist/units-1kRzYShk.js.map +1 -0
  51. package/dist/units-internal-Dg95w_YD.js +753 -0
  52. package/dist/units-internal-Dg95w_YD.js.map +1 -0
  53. package/dist/zip.d.ts +1 -1
  54. package/package.json +2 -1
  55. package/dist/diagnostics-BDuR8UkG.d.ts.map +0 -1
  56. package/dist/diagnostics-CPVADSL1.js +0 -90
  57. package/dist/diagnostics-CPVADSL1.js.map +0 -1
  58. package/dist/namespaces-CcKuzeuE.js.map +0 -1
  59. package/dist/presentation-VfJ_FT7E.js.map +0 -1
  60. package/dist/presentation-types-CpkTx9Xa.d.ts.map +0 -1
  61. package/dist/shapes-Kt51vry6.js.map +0 -1
  62. package/dist/slide-B_aInw3N.d.ts.map +0 -1
  63. package/dist/text-D04NUMvA.js.map +0 -1
  64. package/dist/units-internal-DNvK2I6o.js +0 -896
  65. 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 { $ 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";
4
+ import { A as getNewRelId, C as createShadowEffectLst, D as voidEl, E as raw, M as isHyperlinkRel, N as mediaSlideKey, O as encodeXmlAttrValue, P as validateObjectName, 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 getUuid, k as getDuplicateObjectNames, 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-RLCwdlTE.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: " "
508
501
  });
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;
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: " "
527
+ });
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;
@@ -2310,6 +2449,26 @@ function groupObjectsDefinition(target, objectNames, opts) {
2310
2449
  target._slideObjects.splice(0, target._slideObjects.length, ...regrouped);
2311
2450
  }
2312
2451
  //#endregion
2452
+ //#region src/media/placeholders.ts
2453
+ /**
2454
+ * The three base64 placeholder images the library falls back to.
2455
+ *
2456
+ * Their own module because of where the bundler puts them, not because they group naturally.
2457
+ * The chunker splits by module, so a constant is pulled into every chunk that reaches the module
2458
+ * declaring it — and these lived in `constants-internal.ts` beside `XML_DECL`, `DEF_FONT_SIZE`
2459
+ * and the rest of the vocabulary all of `src/gen/` imports. The result was that `IMG_PLAYBTN`,
2460
+ * a 74 KB PNG wanted by exactly one module, landed in two chunks and cost 149 KB of the shipped
2461
+ * package, 13% of the largest chunk. A caller who only ever wrote text boxes paid for the video
2462
+ * poster.
2463
+ *
2464
+ * So the rule this module encodes: a payload measured in tens of kilobytes does not share a
2465
+ * module with vocabulary, however tidy that grouping reads.
2466
+ */
2467
+ /** Shown in place of media that failed to load, under `onMediaError: 'placeholder'`. */
2468
+ 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==";
2469
+ /** 1x1 transparent PNG, standing in for an SVG the runtime could not rasterize. */
2470
+ const IMG_SVG_PLACEHOLDER = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
2471
+ //#endregion
2313
2472
  //#region src/gen/define/media.ts
2314
2473
  /**
2315
2474
  * Adds a media object to a slide definition.
@@ -2436,21 +2595,11 @@ function registerPreviewImage(target, cover) {
2436
2595
  if (!strImagePath && !strImageData) strImageData = "image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAxSURBVFhH7c4hAQAACMAw+geFGOAJAGbi5mpRmf1Z7HEdAAAAAAAAAAAAAAAAAADAAPwOyNPFH3F8AAAAAElFTkSuQmCC";
2437
2596
  const extn = imageExtensionForSource(strImagePath, strImageData);
2438
2597
  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
- });
2598
+ registerImageMediaRel(target, {
2599
+ path: strImagePath,
2600
+ data: strImageData,
2601
+ extn
2602
+ }, rId);
2454
2603
  return rId;
2455
2604
  }
2456
2605
  //#endregion
@@ -2555,7 +2704,8 @@ function addModel3dDefinition(target, opt) {
2555
2704
  w: opt.w ?? 4,
2556
2705
  h: opt.h ?? 3,
2557
2706
  objectName,
2558
- ...opt.altText ? { altText: opt.altText } : {}
2707
+ ...opt.altText ? { altText: opt.altText } : {},
2708
+ ...opt.objectLock ? { objectLock: opt.objectLock } : {}
2559
2709
  },
2560
2710
  model3d: {
2561
2711
  modelRid,
@@ -2709,6 +2859,70 @@ function addOleObjectDefinition(target, opt) {
2709
2859
  target._slideObjects.push(slideData);
2710
2860
  }
2711
2861
  //#endregion
2862
+ //#region src/gen/table/spans.ts
2863
+ /**
2864
+ * The largest `colspan`/`rowspan` this library will honour.
2865
+ *
2866
+ * Unlike the read side's point-cache ceiling there is no format number to borrow: `a:tc@gridSpan`
2867
+ * is a bare `xsd:int`. So this is a sanity bound rather than a schema one, and it is set an order
2868
+ * of magnitude past PowerPoint's own maximum table (its Insert Table dialog stops at 75 columns
2869
+ * and 75 rows), which is what makes it something no authored table can reach. What it stops is
2870
+ * the other end, where the allocation takes the host process with it.
2871
+ */
2872
+ const MAX_TABLE_SPAN = 1e3;
2873
+ /**
2874
+ * Validate one cell's `colspan`/`rowspan`, warning and falling back to `1` when it is not a whole
2875
+ * number in `1..MAX_TABLE_SPAN`.
2876
+ *
2877
+ * `undefined` is the common case and means "no span" — it warns about nothing and returns `1`.
2878
+ * Everything else is caller error: `NaN`, a negative, a fraction and a span past the ceiling all
2879
+ * describe a table that cannot be built (a fractional span silently truncates, a negative one
2880
+ * shifts every column after it, an enormous one is the allocation above).
2881
+ * @param value - the raw option value
2882
+ * @param kind - `'colspan'` or `'rowspan'`, for the diagnostic
2883
+ * @returns the span to use, at least 1
2884
+ */
2885
+ function resolveSpan(value, kind) {
2886
+ if (value === void 0) return 1;
2887
+ if (!Number.isInteger(value) || value < 1 || value > 1e3) {
2888
+ 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.`);
2889
+ return 1;
2890
+ }
2891
+ return value;
2892
+ }
2893
+ /**
2894
+ * Re-clone `rows` with every out-of-range `colspan`/`rowspan` corrected to `1`.
2895
+ *
2896
+ * Runs once, up front, so the column count, the `_hmerge`/`_vmerge` dummies, the auto-pager's
2897
+ * grid and the emitted `gridSpan`/`rowSpan` attributes all read the same number — checking at
2898
+ * each of those sites instead would warn several times per bad cell and could still disagree.
2899
+ *
2900
+ * A cell whose spans are fine is passed through by identity, not copied: `_spanOrigin` links the
2901
+ * merge dummies back to the origin *object*, and the all-valid path is every real table, which
2902
+ * must stay allocation-free and byte-identical.
2903
+ * @param rows - the rows to check; row arrays are never mutated
2904
+ * @returns the same rows with only the offending cells replaced
2905
+ */
2906
+ function withCheckedSpans(rows) {
2907
+ return rows.map((cells) => cells.map((cell) => {
2908
+ const opts = cell?.options;
2909
+ if (!opts) return cell;
2910
+ const colspan = resolveSpan(opts.colspan, "colspan");
2911
+ const rowspan = resolveSpan(opts.rowspan, "rowspan");
2912
+ const colspanOk = opts.colspan === void 0 || opts.colspan === colspan;
2913
+ const rowspanOk = opts.rowspan === void 0 || opts.rowspan === rowspan;
2914
+ if (colspanOk && rowspanOk) return cell;
2915
+ return {
2916
+ ...cell,
2917
+ options: {
2918
+ ...opts,
2919
+ ...opts.colspan === void 0 ? {} : { colspan },
2920
+ ...opts.rowspan === void 0 ? {} : { rowspan }
2921
+ }
2922
+ };
2923
+ }));
2924
+ }
2925
+ //#endregion
2712
2926
  //#region src/gen/define/table.ts
2713
2927
  /**
2714
2928
  * Expand a cell/table border into the 4-side tuple the rest of the pipeline expects.
@@ -2907,7 +3121,8 @@ function registerTableImageFills(target, rows, opt) {
2907
3121
  }
2908
3122
  function addTableDefinition(target, tableRows, options, slideLayout, presLayout, addSlide, getSlide) {
2909
3123
  const slides = [target];
2910
- const opt = options && typeof options === "object" ? options : {};
3124
+ const opt = options && typeof options === "object" ? { ...options } : {};
3125
+ if (Array.isArray(opt.border)) opt.border = [...opt.border];
2911
3126
  opt.objectName = opt.objectName ? encodeXmlAttrValue(validateObjectName(opt.objectName, "table")) : `Table ${target._slideObjects.filter((obj) => obj._type === "table").length}`;
2912
3127
  if (opt.placeholder && slideLayout?._slideObjects) {
2913
3128
  const placeHold = slideLayout._slideObjects.find((item) => item._type === "placeholder" && item.options?.placeholder === opt.placeholder);
@@ -2920,7 +3135,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
2920
3135
  }
2921
3136
  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
3137
  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);
3138
+ const arrRows = withCheckedSpans(normalizeTableRows(applyTableHeaderColumnSugar(tableRows, opt), opt));
2924
3139
  if (opt.x === void 0 || opt.x === null) opt.x = .5;
2925
3140
  if (opt.y === void 0 || opt.y === null) opt.y = .5;
2926
3141
  opt.fontSize = opt.fontSize || 12;
@@ -3091,7 +3306,8 @@ function pushZoomObject(target, variant, opts, zoom) {
3091
3306
  y: opts.y ?? 0,
3092
3307
  w: opts.w ?? 0,
3093
3308
  h: opts.h ?? 0,
3094
- objectName
3309
+ objectName,
3310
+ ...opts.objectLock ? { objectLock: opts.objectLock } : {}
3095
3311
  },
3096
3312
  zoom: {
3097
3313
  variant,
@@ -3567,16 +3783,17 @@ var SlideBuilder = class {
3567
3783
  * than one parameterized one. Called by {@link ./chart-xml}'s axis region.
3568
3784
  *
3569
3785
  * 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.
3786
+ * `<c:spPr>`, which differs between the three only by indent width. `<c:txPr>` stays
3787
+ * duplicated — its inherited indentation is irregular *within* each copy as well as between
3788
+ * them (the category axis closes `</a:defRPr>` at three spaces, the value axis at six), so
3789
+ * one parameterised builder would take about as many indent arguments as it has lines, and
3790
+ * would hide the quirks rather than share the structure. Each copy states its own layout at
3791
+ * its own call site instead.
3575
3792
  *
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.
3793
+ * Regularising that whitespace so one builder *could* serve all three is still open, and
3794
+ * still a byte change — AGENTS.md is explicit that a whitespace-only diff is a STOP, not a
3795
+ * known divergence. It is a decision to take deliberately, with a re-baselined gate, not a
3796
+ * cleanup to fold into something else.
3580
3797
  */
3581
3798
  /**
3582
3799
  * The `<c:spPr>` axis-line block, identical in shape on all three axes.
@@ -3596,89 +3813,103 @@ var SlideBuilder = class {
3596
3813
  */
3597
3814
  function axisLineSpPr(indent, widthEmu, show, color, dash) {
3598
3815
  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>`;
3816
+ const line = el("a:ln", {
3817
+ w: widthEmu,
3818
+ cap: "flat"
3819
+ }, [
3820
+ raw(!show ? voidEl("a:noFill") : genXmlColorSelection(color || DEF_GRIDLINE_COLOR)),
3821
+ raw(voidEl("a:prstDash", { val: dash }, { openPrefix: at(4) })),
3822
+ raw(voidEl("a:round", null, { openPrefix: at(4) }))
3823
+ ], {
3824
+ openPrefix: at(2),
3825
+ closePrefix: at(2)
3826
+ });
3827
+ return el("c:spPr", null, raw(line), {
3828
+ openPrefix: at(0),
3829
+ closePrefix: at(0)
3830
+ });
3600
3831
  }
3601
3832
  function makeCatAxis(opts, axisId, valAxisId) {
3602
- let strXml = "";
3603
3833
  const usesValueAxisForCategories = opts._type === "scatter" || opts._type === "bubble" || opts._type === "bubble3D";
3604
3834
  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", {
3835
+ const tag = usesValueAxisForCategories ? "c:valAx" : opts.catLabelFormatCode ? "c:dateAx" : "c:catAx";
3836
+ const scaling = el("c:scaling", null, [
3837
+ raw(voidEl("c:orientation", { val: opts.catAxisOrientation || "minMax" })),
3838
+ opts.catAxisMaxVal || opts.catAxisMaxVal === 0 ? raw(voidEl("c:max", { val: opts.catAxisMaxVal })) : null,
3839
+ opts.catAxisMinVal || opts.catAxisMinVal === 0 ? raw(voidEl("c:min", { val: opts.catAxisMinVal })) : null
3840
+ ], { openPrefix: " " });
3841
+ const numFmt = usesValueAxisForCategories ? voidEl("c:numFmt", {
3842
+ formatCode: (opts.catAxisLabelFormatCode ?? opts.valAxisLabelFormatCode) || "General",
3843
+ sourceLinked: 1
3844
+ }, { openPrefix: " " }) : voidEl("c:numFmt", {
3630
3845
  formatCode: (opts.catLabelFormatCode ?? "") || "General",
3631
3846
  sourceLinked: 1
3847
+ }, { openPrefix: " " });
3848
+ 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: " " });
3849
+ const defRPr = el("a:defRPr", {
3850
+ sz: ptToHundredths(opts.catAxisLabelFontSize || 12),
3851
+ b: opts.catAxisLabelFontBold ? 1 : 0,
3852
+ i: opts.catAxisLabelFontItalic ? 1 : 0,
3853
+ u: "none",
3854
+ strike: "noStrike"
3855
+ }, [raw(genXmlColorSelection(opts.catAxisLabelColor || "000000")), raw(" " + createChartTextFonts(opts.catAxisLabelFontFace || "Arial"))], {
3856
+ openPrefix: " ",
3857
+ closePrefix: " "
3858
+ });
3859
+ const txPr = el("c:txPr", null, [
3860
+ raw(voidEl("a:bodyPr", { rot: opts.catAxisLabelRotate ? convertAngleUnits(opts.catAxisLabelRotate, "catAxisLabelRotate") : void 0 })),
3861
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
3862
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
3863
+ openPrefix: " ",
3864
+ closePrefix: " "
3865
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
3866
+ openPrefix: " ",
3867
+ closePrefix: " "
3868
+ }))
3869
+ ], {
3870
+ openPrefix: " ",
3871
+ closePrefix: " "
3632
3872
  });
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
3873
  const valAxisCrossTag = typeof opts.valAxisCrossesAt === "number" ? "crossesAt" : "crosses";
3659
3874
  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
- }
3875
+ let units = "";
3667
3876
  if (opts.catLabelFormatCode || usesValueAxisForCategories) {
3668
3877
  if (opts.catLabelFormatCode) {
3669
3878
  const baseTimeUnit = validTimeUnit(opts.catAxisBaseTimeUnit, "catAxisBaseTimeUnit");
3670
3879
  const majorTimeUnit = validTimeUnit(opts.catAxisMajorTimeUnit, "catAxisMajorTimeUnit");
3671
3880
  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 });
3881
+ if (baseTimeUnit) units += voidEl("c:baseTimeUnit", { val: baseTimeUnit });
3882
+ if (majorTimeUnit) units += voidEl("c:majorTimeUnit", { val: majorTimeUnit });
3883
+ if (minorTimeUnit) units += voidEl("c:minorTimeUnit", { val: minorTimeUnit });
3675
3884
  }
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;
3885
+ if (opts.catAxisMajorUnit) units += voidEl("c:majorUnit", { val: opts.catAxisMajorUnit });
3886
+ if (opts.catAxisMinorUnit) units += voidEl("c:minorUnit", { val: opts.catAxisMinorUnit });
3887
+ }
3888
+ return el(tag, null, [
3889
+ raw(voidEl("c:axId", { val: axisId }, { openPrefix: " " })),
3890
+ raw(scaling),
3891
+ raw(voidEl("c:delete", { val: opts.catAxisHidden ? 1 : 0 }, { openPrefix: " " })),
3892
+ raw(voidEl("c:axPos", { val: opts.barDir === "col" ? "b" : "l" }, { openPrefix: " " })),
3893
+ raw(opts.catGridLine && opts.catGridLine.style !== "none" ? createGridLineElement(opts.catGridLine) : ""),
3894
+ opts.showCatAxisTitle ? raw(genXmlTitle({
3895
+ color: opts.catAxisTitleColor,
3896
+ fontFace: opts.catAxisTitleFontFace,
3897
+ fontSize: opts.catAxisTitleFontSize,
3898
+ titleRotate: opts.catAxisTitleRotate,
3899
+ title: opts.catAxisTitle || "Axis Title"
3900
+ })) : null,
3901
+ raw(numFmt),
3902
+ raw(ticks),
3903
+ raw(axisLineSpPr(2, opts.catAxisLineSize ? ptsToEmuLenient(opts.catAxisLineSize) : EMU_PER_POINT, opts.catAxisLineShow, opts.catAxisLineColor, opts.catAxisLineStyle || "solid")),
3904
+ raw(txPr),
3905
+ raw(voidEl("c:crossAx", { val: valAxisId }, { openPrefix: " " })),
3906
+ raw(voidEl(`c:${valAxisCrossTag}`, { val: valAxisCrossValue }, { openPrefix: " " })),
3907
+ usesValueAxisForCategories ? null : raw(voidEl("c:auto", { val: 1 }, { openPrefix: " " })),
3908
+ usesCategoryAxis ? raw(voidEl("c:lblAlgn", { val: "ctr" }, { openPrefix: " " })) : null,
3909
+ usesCategoryAxis && opts.catAxisLabelFrequency ? raw(voidEl("c:tickLblSkip", { val: opts.catAxisLabelFrequency }, { openPrefix: " " })) : null,
3910
+ usesCategoryAxis ? raw(voidEl("c:noMultiLvlLbl", { val: opts.catAxisMultiLevelLabels ? 0 : 1 }, { openPrefix: " " })) : null,
3911
+ raw(units)
3912
+ ]);
3682
3913
  }
3683
3914
  /**
3684
3915
  * Create Value Axis (Used by `bar3D`)
@@ -3690,61 +3921,68 @@ function makeValAxis(opts, valAxisId) {
3690
3921
  let axisPos = valAxisId === "2094734552" ? opts.barDir === "col" ? "l" : "b" : opts.barDir !== "col" ? "r" : "t";
3691
3922
  if (valAxisId === "2094734553") axisPos = "r";
3692
3923
  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
3924
+ const scaling = el("c:scaling", null, [
3925
+ opts.valAxisLogScaleBase ? raw(voidEl("c:logBase", { val: opts.valAxisLogScaleBase })) : null,
3926
+ raw(voidEl("c:orientation", { val: opts.valAxisOrientation || "minMax" })),
3927
+ opts.valAxisMaxVal || opts.valAxisMaxVal === 0 ? raw(voidEl("c:max", { val: opts.valAxisMaxVal })) : null,
3928
+ opts.valAxisMinVal || opts.valAxisMinVal === 0 ? raw(voidEl("c:min", { val: opts.valAxisMinVal })) : null
3929
+ ], {
3930
+ openPrefix: " ",
3931
+ closePrefix: " "
3715
3932
  });
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;
3933
+ 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: " " });
3934
+ const defRPr = el("a:defRPr", {
3935
+ sz: ptToHundredths(opts.valAxisLabelFontSize || 12),
3936
+ b: opts.valAxisLabelFontBold ? 1 : 0,
3937
+ i: opts.valAxisLabelFontItalic ? 1 : 0,
3938
+ u: "none",
3939
+ strike: "noStrike"
3940
+ }, [raw(genXmlColorSelection(opts.valAxisLabelColor || "000000")), raw(" " + createChartTextFonts(opts.valAxisLabelFontFace || "Arial"))], {
3941
+ openPrefix: " ",
3942
+ closePrefix: " "
3943
+ });
3944
+ const txPr = el("c:txPr", null, [
3945
+ raw(voidEl("a:bodyPr", { rot: opts.valAxisLabelRotate ? convertAngleUnits(opts.valAxisLabelRotate, "valAxisLabelRotate") : void 0 }, { openPrefix: " " })),
3946
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
3947
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
3948
+ openPrefix: " ",
3949
+ closePrefix: " "
3950
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
3951
+ openPrefix: " ",
3952
+ closePrefix: " "
3953
+ }))
3954
+ ], {
3955
+ openPrefix: " ",
3956
+ closePrefix: " "
3957
+ });
3958
+ 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: " " });
3959
+ const crossBetween = opts.valAxisCrossBetween || (opts._type === "scatter" || !!(Array.isArray(opts._type) && opts._type.some((type) => asChartType(type.type) === "area")) ? "midCat" : "between");
3960
+ return el("c:valAx", null, [
3961
+ raw(voidEl("c:axId", { val: valAxisId }, { openPrefix: " " })),
3962
+ raw(scaling),
3963
+ raw(voidEl("c:delete", { val: opts.valAxisHidden ? 1 : 0 }, { openPrefix: " " })),
3964
+ raw(voidEl("c:axPos", { val: axisPos }, { openPrefix: " " })),
3965
+ opts.valGridLine && opts.valGridLine.style !== "none" ? raw(createGridLineElement(opts.valGridLine)) : null,
3966
+ opts.showValAxisTitle ? raw(genXmlTitle({
3967
+ color: opts.valAxisTitleColor,
3968
+ fontFace: opts.valAxisTitleFontFace,
3969
+ fontSize: opts.valAxisTitleFontSize,
3970
+ titleRotate: opts.valAxisTitleRotate,
3971
+ title: opts.valAxisTitle || "Axis Title"
3972
+ })) : null,
3973
+ raw(voidEl("c:numFmt", {
3974
+ formatCode: opts.valAxisLabelFormatCode || "General",
3975
+ sourceLinked: 0
3976
+ })),
3977
+ raw(ticks),
3978
+ raw(axisLineSpPr(1, opts.valAxisLineSize ? ptsToEmuLenient(opts.valAxisLineSize) : EMU_PER_POINT, opts.valAxisLineShow, opts.valAxisLineColor, opts.valAxisLineStyle || "solid")),
3979
+ raw(txPr),
3980
+ raw(voidEl("c:crossAx", { val: crossAxId }, { openPrefix: " " })),
3981
+ raw(crosses),
3982
+ raw(voidEl("c:crossBetween", { val: crossBetween }, { openPrefix: " " })),
3983
+ opts.valAxisMajorUnit ? raw(voidEl("c:majorUnit", { val: opts.valAxisMajorUnit }, { openPrefix: " " })) : null,
3984
+ opts.valAxisDisplayUnit ? raw(el("c:dispUnits", null, [raw(voidEl("c:builtInUnit", { val: opts.valAxisDisplayUnit })), raw(opts.valAxisDisplayUnitLabel ? voidEl("c:dispUnitsLbl") : "")])) : null
3985
+ ]);
3748
3986
  }
3749
3987
  /**
3750
3988
  * Create Series Axis (Used by `bar3D`)
@@ -3754,651 +3992,850 @@ function makeValAxis(opts, valAxisId) {
3754
3992
  * @return {string} XML
3755
3993
  */
3756
3994
  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
3995
+ const defRPr = el("a:defRPr", {
3996
+ sz: ptToHundredths(opts.serAxisLabelFontSize || 12),
3997
+ b: opts.serAxisLabelFontBold ? 1 : 0,
3998
+ i: opts.serAxisLabelFontItalic ? 1 : 0,
3999
+ u: "none",
4000
+ strike: "noStrike"
4001
+ }, [raw(" " + genXmlColorSelection(opts.serAxisLabelColor || "000000")), raw(" " + createChartTextFonts(opts.serAxisLabelFontFace || "Arial"))], {
4002
+ openPrefix: " ",
4003
+ closePrefix: " "
3774
4004
  });
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 + "\"/>";
4005
+ const txPr = el("c:txPr", null, [
4006
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
4007
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4008
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
4009
+ openPrefix: " ",
4010
+ closePrefix: " "
4011
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
4012
+ openPrefix: " ",
4013
+ closePrefix: " "
4014
+ }))
4015
+ ], {
4016
+ openPrefix: " ",
4017
+ closePrefix: " "
4018
+ });
4019
+ let units = "";
3795
4020
  if (opts.serLabelFormatCode) {
3796
4021
  const baseTimeUnit = validTimeUnit(opts.serAxisBaseTimeUnit, "serAxisBaseTimeUnit");
3797
4022
  const majorTimeUnit = validTimeUnit(opts.serAxisMajorTimeUnit, "serAxisMajorTimeUnit");
3798
4023
  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;
4024
+ if (baseTimeUnit) units += ` <c:baseTimeUnit val="${baseTimeUnit}"/>`;
4025
+ if (majorTimeUnit) units += voidEl("c:majorTimeUnit", { val: majorTimeUnit }, { openPrefix: " " });
4026
+ if (minorTimeUnit) units += voidEl("c:minorTimeUnit", { val: minorTimeUnit }, { openPrefix: " " });
4027
+ if (opts.serAxisMajorUnit) units += voidEl("c:majorUnit", { val: opts.serAxisMajorUnit }, { openPrefix: " " });
4028
+ if (opts.serAxisMinorUnit) units += voidEl("c:minorUnit", { val: opts.serAxisMinorUnit }, { openPrefix: " " });
4029
+ }
4030
+ return el("c:serAx", null, [
4031
+ raw(voidEl("c:axId", { val: axisId }, { openPrefix: " " })),
4032
+ raw(el("c:scaling", null, raw(voidEl("c:orientation", { val: opts.serAxisOrientation || "minMax" })), { openPrefix: " " })),
4033
+ raw(voidEl("c:delete", { val: opts.serAxisHidden ? 1 : 0 }, { openPrefix: " " })),
4034
+ raw(voidEl("c:axPos", { val: opts.barDir === "col" ? "b" : "l" }, { openPrefix: " " })),
4035
+ raw(opts.serGridLine && opts.serGridLine.style !== "none" ? createGridLineElement(opts.serGridLine) : ""),
4036
+ opts.showSerAxisTitle ? raw(genXmlTitle({
4037
+ color: opts.serAxisTitleColor,
4038
+ fontFace: opts.serAxisTitleFontFace,
4039
+ fontSize: opts.serAxisTitleFontSize,
4040
+ titleRotate: opts.serAxisTitleRotate,
4041
+ title: opts.serAxisTitle || "Axis Title"
4042
+ })) : null,
4043
+ raw(voidEl("c:numFmt", {
4044
+ formatCode: (opts.serLabelFormatCode ?? "") || "General",
4045
+ sourceLinked: 0
4046
+ }, { openPrefix: " " })),
4047
+ raw(voidEl("c:majorTickMark", { val: "out" }, { openPrefix: " " })),
4048
+ raw(voidEl("c:minorTickMark", { val: "none" }, { openPrefix: " " })),
4049
+ raw(voidEl("c:tickLblPos", { val: opts.serAxisLabelPos || (opts.barDir === "col" ? "low" : "nextTo") }, { openPrefix: " " })),
4050
+ raw(axisLineSpPr(2, EMU_PER_POINT, opts.serAxisLineShow, opts.serAxisLineColor, "solid")),
4051
+ raw(txPr),
4052
+ raw(voidEl("c:crossAx", { val: valAxisId }, { openPrefix: " " })),
4053
+ raw(voidEl("c:crosses", { val: "autoZero" }, { openPrefix: " " })),
4054
+ opts.serAxisLabelFrequency ? raw(voidEl("c:tickLblSkip", { val: opts.serAxisLabelFrequency }, { openPrefix: " " })) : null,
4055
+ raw(units)
4056
+ ]);
3807
4057
  }
3808
- /**
3809
- * Create char title elements
3810
- * @param {ChartPropsTitle} opts - options
3811
- * @return {string} XML `<c:title>`
3812
- */
3813
4058
  //#endregion
3814
4059
  //#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>`;
4060
+ /** The chart types drawn as a line (a marker per point, an optional smooth) rather than as a body. */
4061
+ const isLineLike = (chartType) => chartType === "line" || chartType === "radar";
4062
+ /** The two bar families, which share `<c:barDir>`, `<c:invertIfNegative>` and a gap width. */
4063
+ const isBarLike = (chartType) => chartType === "bar" || chartType === "bar3D";
4064
+ /** The type-gated children that open a plot element, before `<c:varyColors>`. */
4065
+ function plotGrouping(chartType, opts) {
3823
4066
  if (chartType === "area" || chartType === "line") {
3824
4067
  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") + "\"/>";
4068
+ return voidEl("c:grouping", { val: lineGrouping });
3830
4069
  }
4070
+ if (isBarLike(chartType)) return voidEl("c:barDir", { val: opts.barDir }) + voidEl("c:grouping", { val: opts.barGrouping || "clustered" });
3831
4071
  if (chartType === "radar") {
3832
4072
  const radarStyleWire = {
3833
4073
  radar: "standard",
3834
4074
  markers: "marker",
3835
4075
  filled: "filled"
3836
4076
  }[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({
4077
+ return voidEl("c:radarStyle", { val: radarStyleWire });
4078
+ }
4079
+ return "";
4080
+ }
4081
+ /** Fill, outline and shadow for one series, from its resolved colour and the line/border options. */
4082
+ function serShapeProps(chartType, opts, seriesColor, lineSize, serIndex) {
4083
+ 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);
4084
+ let line = "";
4085
+ if (isLineLike(chartType)) {
4086
+ const effectiveLineSize = lineSize ?? opts.lineSize ?? 2;
4087
+ line = effectiveLineSize === 0 ? el("a:ln", null, raw(voidEl("a:noFill"))) : el("a:ln", {
4088
+ w: ptsToEmuLenient(effectiveLineSize),
4089
+ cap: createLineCap(opts.lineCap)
4090
+ }, [
4091
+ raw(chartColorLineFill(seriesColor)),
4092
+ raw(voidEl("a:prstDash", { val: opts.lineDashValues?.[serIndex] ?? opts.lineDash ?? "solid" })),
4093
+ raw(voidEl("a:round"))
4094
+ ]);
4095
+ } else if (opts.dataBorder) line = el("a:ln", {
4096
+ w: ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75)),
4097
+ cap: createLineCap(opts.lineCap)
4098
+ }, [
4099
+ raw(genXmlColorSelection({
3865
4100
  color: opts.dataBorder.color ?? "363636",
3866
4101
  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>";
4102
+ })),
4103
+ raw(voidEl("a:prstDash", { val: "solid" })),
4104
+ raw(voidEl("a:round"))
4105
+ ]);
4106
+ return el("c:spPr", null, [
4107
+ raw(fill),
4108
+ raw(line),
4109
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4110
+ ], {
4111
+ openPrefix: " ",
4112
+ closePrefix: " "
3964
4113
  });
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;
4114
+ }
4115
+ /**
4116
+ * The point marker for a line/radar series. `<c:marker>` must precede `<c:dLbls>` in CT_LineSer
4117
+ * (schema order: spPr → marker → dPt → dLbls).
4118
+ */
4119
+ function serMarker(opts, markerColor, seriesColor) {
4120
+ const spPr = el("c:spPr", null, [
4121
+ raw(markerColor === "transparent" ? voidEl("a:noFill") : genXmlColorSelection(markerColor)),
4122
+ raw(el("a:ln", {
4123
+ w: opts.lineDataSymbolLineSize,
4124
+ cap: "flat"
4125
+ }, [
4126
+ raw(chartColorLineFill(opts.lineDataSymbolLineColor || seriesColor)),
4127
+ raw(voidEl("a:prstDash", { val: "solid" })),
4128
+ raw(voidEl("a:round"))
4129
+ ], { openPrefix: " " })),
4130
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
4131
+ ], {
4132
+ openPrefix: " ",
4133
+ closePrefix: " "
4134
+ });
4135
+ return el("c:marker", null, [
4136
+ raw(voidEl("c:symbol", { val: opts.lineDataSymbol }, { openPrefix: " " })),
4137
+ opts.lineDataSymbolSize ? raw(voidEl("c:size", { val: opts.lineDataSymbolSize })) : null,
4138
+ raw(spPr)
4139
+ ]);
4140
+ }
4141
+ /** Per-series `<c:dLbls>`: number format, an optional label background, text style, show flags. */
4142
+ function serDataLabels(obj, opts, seriesColor) {
4143
+ const over = opts.seriesOptions?.[obj._dataIndex];
4144
+ const defRPr = el("a:defRPr", {
4145
+ b: over?.dataLabelFontBold ?? opts.dataLabelFontBold ?? false ? 1 : 0,
4146
+ i: over?.dataLabelFontItalic ?? opts.dataLabelFontItalic ?? false ? 1 : 0,
4147
+ strike: "noStrike",
4148
+ sz: ptToHundredths(over?.dataLabelFontSize ?? opts.dataLabelFontSize ?? 12),
4149
+ u: "none"
4150
+ }, [raw(genXmlColorSelection(over?.dataLabelColor ?? opts.dataLabelColor ?? "000000")), raw(createChartTextFonts(over?.dataLabelFontFace ?? opts.dataLabelFontFace ?? "Arial"))]);
4151
+ const txPr = el("c:txPr", null, [
4152
+ raw(voidEl("a:bodyPr")),
4153
+ raw(voidEl("a:lstStyle")),
4154
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(defRPr)))))
4155
+ ]);
4156
+ const lblFmtCode = over?.dataLabelFormatCode ?? opts.dataLabelFormatCode;
4157
+ return el("c:dLbls", null, [
4158
+ raw((obj.customLabels ?? []).map((lbl, idx) => lbl ? makeCustomDLblXml(idx, lbl, opts) : "").join("")),
4159
+ raw(voidEl("c:numFmt", {
4160
+ formatCode: (lblFmtCode ?? "") || "General",
4161
+ sourceLinked: 0
4162
+ })),
4163
+ opts.dataLabelBkgrdColors ? raw(el("c:spPr", null, raw(genXmlColorSelection(seriesColor)))) : null,
4164
+ raw(txPr),
4165
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition })) : null,
4166
+ raw(voidEl("c:showLegendKey", { val: 0 })),
4167
+ raw(voidEl("c:showVal", { val: opts.showValue ? 1 : 0 })),
4168
+ raw(voidEl("c:showCatName", { val: 0 })),
4169
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 })),
4170
+ raw(voidEl("c:showPercent", { val: 0 })),
4171
+ raw(voidEl("c:showBubbleSize", { val: 0 })),
4172
+ raw(voidEl("c:showLeaderLines", { val: opts.showLeaderLines ? 1 : 0 }))
4173
+ ]);
4174
+ }
4175
+ /**
4176
+ * The `<c:cat>` category reference. Numeric (date) categories and single-level text ones are
4177
+ * {@link catRefBlock}; multi-level labels nest a `<c:lvl>` per group, which is a different shape
4178
+ * rather than a different spelling of the same one.
4179
+ */
4180
+ function serCategories(obj, opts) {
4181
+ const groups = dataLabels(obj);
4182
+ const cats = firstLabelGroup(obj);
4183
+ const catRef = `Sheet1!$A$2:$A$${cats.length + 1}`;
4184
+ if (opts.catLabelFormatCode) return el("c:cat", null, raw(catRefBlock("num", catRef, cats, opts.catLabelFormatCode || "General", "indented")));
4185
+ if (groups.length === 1) return el("c:cat", null, raw(catRefBlock("str", catRef, cats, void 0, "indented")));
4186
+ 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("");
4187
+ const cache = el("c:multiLvlStrCache", null, [raw(voidEl("c:ptCount", { val: cats.length }, { openPrefix: " " })), raw(lvls)], {
4188
+ openPrefix: " ",
4189
+ closePrefix: " "
4190
+ });
4191
+ const ref = el("c:multiLvlStrRef", null, [raw(el("c:f", null, sheetRangeRef(1, 2, groups.length, cats.length + 1), { openPrefix: " " })), raw(cache)], {
4192
+ openPrefix: " ",
4193
+ closePrefix: " "
4194
+ });
4195
+ return el("c:cat", null, raw(ref));
4196
+ }
4197
+ /** The `<c:val>` numeric cache: the series' own sheet column, one point per category. */
4198
+ function serValues(obj, valFmtCode) {
4199
+ const valCol = obj._dataIndex + dataLabels(obj).length + 1;
4200
+ const catCount = firstLabelGroup(obj).length;
4201
+ const numCache = el("c:numCache", null, [
4202
+ raw(el("c:formatCode", null, valFmtCode, { openPrefix: " " })),
4203
+ raw(voidEl("c:ptCount", { val: catCount }, { openPrefix: " " })),
4204
+ raw(dataValues(obj).map((value, idx) => numCachePt(idx, value)).join(""))
4205
+ ], {
4206
+ openPrefix: " ",
4207
+ closePrefix: " "
4208
+ });
4209
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, sheetRangeRef(valCol, 2, valCol, catCount + 1))), raw(numCache)], {
4210
+ openPrefix: " ",
4211
+ closePrefix: " "
4212
+ });
4213
+ return el("c:val", null, raw(numRef));
4214
+ }
4215
+ /**
4216
+ * Plot a category-axis chart family (area / bar / bar3d / line / radar) into a
4217
+ * `<c:xxxChart>` element. These share the grouping / series / cat+val axis structure.
4218
+ */
4219
+ function makeCatAxisPlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
4220
+ const chartColors = resolveChartPalette(opts);
4221
+ 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;
4222
+ const sers = data.map((obj, serIndex) => {
4223
+ const seriesOverride = opts.seriesOptions?.[obj._dataIndex];
4224
+ const seriesColor = seriesOverride?.color ?? paletteColor(chartColors, serIndex);
4225
+ return el("c:ser", null, [
4226
+ raw(voidEl("c:idx", { val: obj._dataIndex }, { openPrefix: " " })),
4227
+ raw(voidEl("c:order", { val: obj._dataIndex })),
4228
+ raw(strRefBlock(sheetCellRef(obj._dataIndex + dataLabels(obj).length + 1, 1), obj.name ?? "")),
4229
+ raw(serShapeProps(chartType, opts, seriesColor, seriesOverride?.lineSize, serIndex)),
4230
+ isBarLike(chartType) ? raw(voidEl("c:invertIfNegative", { val: 0 }, { openPrefix: " " })) : null,
4231
+ isLineLike(chartType) ? raw(serMarker(opts, paletteColor(chartColors, obj._dataIndex), seriesColor)) : null,
4232
+ raw(makeSeriesDataPointsXml(chartType, obj, opts, barVaryColors)),
4233
+ chartType === "radar" ? null : raw(serDataLabels(obj, opts, seriesColor)),
4234
+ chartType === "radar" ? null : raw(makeChartErrorBarsXml(chartType, obj.errorBars, obj)),
4235
+ raw(serCategories(obj, opts)),
4236
+ raw(serValues(obj, valFmtCode)),
4237
+ chartType === "line" ? raw(voidEl("c:smooth", { val: opts.lineSmooth ? 1 : 0 })) : null
4238
+ ]);
4239
+ }).join("");
4240
+ let plotOptions = "";
4241
+ 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);
4242
+ 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: " " });
4243
+ else if (chartType === "line") plotOptions = voidEl("c:marker", { val: 1 }, { openPrefix: " " });
4244
+ return el(`c:${chartType}Chart`, null, [
4245
+ raw(plotGrouping(chartType, opts)),
4246
+ raw(voidEl("c:varyColors", { val: 0 })),
4247
+ raw(sers),
4248
+ raw(chartDataLabels(opts, true)),
4249
+ raw(plotOptions),
4250
+ raw(voidEl("c:axId", { val: catAxisId })),
4251
+ raw(voidEl("c:axId", { val: valAxisId })),
4252
+ chartType === "bar3D" ? raw(voidEl("c:axId", { val: AXIS_ID_SERIES_PRIMARY })) : null
4253
+ ]);
3979
4254
  }
3980
4255
  //#endregion
3981
4256
  //#region src/gen/chart/plot-scatter.ts
4257
+ /** The font attributes a scatter label's `<a:defRPr>` and `<a:rPr>` both carry, in schema order. */
4258
+ function labelFontAttrs(opts) {
4259
+ return {
4260
+ sz: ptToHundredths(opts.dataLabelFontSize || 12),
4261
+ b: opts.dataLabelFontBold ? 1 : 0,
4262
+ i: opts.dataLabelFontItalic ? 1 : 0,
4263
+ u: "none",
4264
+ strike: "noStrike"
4265
+ };
4266
+ }
4267
+ /** The colour and typeface children of those run properties; `indent` is theirs, not the parent's. */
4268
+ function labelFontChildren(opts, indent) {
4269
+ return [raw(indent + el("a:solidFill", null, raw(createColorElement(opts.dataLabelColor || "000000")))), raw(indent + createChartTextFonts(opts.dataLabelFontFace || "Arial"))];
4270
+ }
4271
+ /**
4272
+ * The `(x, y)` runs appended to a `customXY` label: a literal `" ("`, an `XVALUE` field, a
4273
+ * `", "`, a `YVALUE` field and a `")"`.
4274
+ *
4275
+ * Each `<a:fld>` id is minted per build. A field id has to be unique, which is a property a
4276
+ * derived id would have to reproduce without an oracle for how far that uniqueness has to
4277
+ * reach — so the ids stay random and the *comparison* gives: `NORMALIZERS` in
4278
+ * `scripts/pptx-parts.mjs` erases exactly these two field types before the byte-identity gate
4279
+ * diffs a part. Do not "fix" the nondeterminism here.
4280
+ */
4281
+ function customXYRuns(obj, opts) {
4282
+ const lang = opts.lang || "en-US";
4283
+ const literal = (text) => el("a:r", null, [raw(voidEl("a:rPr", {
4284
+ lang,
4285
+ baseline: 0,
4286
+ dirty: 0
4287
+ }, { openPrefix: " " })), raw(el("a:t", null, text, { openPrefix: " " }))], {
4288
+ openPrefix: " ",
4289
+ closePrefix: " "
4290
+ });
4291
+ const field = (type, text) => el("a:fld", {
4292
+ id: `{${getUuid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")}}`,
4293
+ type
4294
+ }, [
4295
+ raw(voidEl("a:rPr", {
4296
+ lang,
4297
+ baseline: 0
4298
+ }, { openPrefix: " " })),
4299
+ raw(el("a:pPr", null, raw(voidEl("a:defRPr", null, { openPrefix: " " })), {
4300
+ openPrefix: " ",
4301
+ closePrefix: " "
4302
+ })),
4303
+ raw(" " + el("a:t", null, text))
4304
+ ], {
4305
+ openPrefix: " ",
4306
+ closePrefix: " "
4307
+ });
4308
+ return literal(" (") + field("XVALUE", "[" + (obj.name ?? "")) + literal(", ") + field("YVALUE", "[" + (obj.name ?? "") + "]") + literal(")") + voidEl("a:endParaRPr", {
4309
+ lang,
4310
+ dirty: 0
4311
+ }, { openPrefix: " " });
4312
+ }
4313
+ /**
4314
+ * One per-point `<c:dLbl>` for the `custom` / `customXY` label formats: rich text carrying the
4315
+ * caller's label, optionally followed by the X/Y value fields.
4316
+ */
4317
+ function scatterCustomLabel(obj, opts, label, idx, chartUuid) {
4318
+ const rich = el("c:rich", null, [
4319
+ raw(el("a:bodyPr", null, raw(voidEl("a:spAutoFit", null, { openPrefix: " " })), {
4320
+ openPrefix: " ",
4321
+ closePrefix: " "
4322
+ })),
4323
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4324
+ raw(el("a:p", null, [
4325
+ raw(el("a:pPr", null, raw(el("a:defRPr", labelFontAttrs(opts), labelFontChildren(opts, " "), {
4326
+ openPrefix: " ",
4327
+ closePrefix: " "
4328
+ })), {
4329
+ openPrefix: " ",
4330
+ closePrefix: " "
4331
+ })),
4332
+ raw(el("a:r", null, [raw(el("a:rPr", {
4333
+ lang: opts.lang || "en-US",
4334
+ ...labelFontAttrs(opts),
4335
+ dirty: 0
4336
+ }, labelFontChildren(opts, " "), {
4337
+ openPrefix: " ",
4338
+ closePrefix: " "
4339
+ })), raw(" " + el("a:t", null, label))], {
4340
+ openPrefix: " ",
4341
+ closePrefix: " "
4342
+ })),
4343
+ opts.dataLabelFormatScatter === "customXY" && !/^ *$/.test(label) ? raw(customXYRuns(obj, opts)) : null
4344
+ ], {
4345
+ openPrefix: " ",
4346
+ closePrefix: " "
4347
+ }))
4348
+ ], {
4349
+ openPrefix: " ",
4350
+ closePrefix: " "
4351
+ });
4352
+ const spPr = el("c:spPr", null, [
4353
+ raw(voidEl("a:noFill", null, { openPrefix: " " })),
4354
+ raw(el("a:ln", null, raw(voidEl("a:noFill", null, { openPrefix: " " })), {
4355
+ openPrefix: " ",
4356
+ closePrefix: " "
4357
+ })),
4358
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
4359
+ ], {
4360
+ openPrefix: " ",
4361
+ closePrefix: " "
4362
+ });
4363
+ const extLst = el("c:extLst", null, [raw(voidEl("c:ext", {
4364
+ uri: "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}",
4365
+ "xmlns:c15": "http://schemas.microsoft.com/office/drawing/2012/chart"
4366
+ }, { openPrefix: " " })), raw(el("c:ext", {
4367
+ uri: "{C3380CC4-5D6E-409C-BE32-E72D297353CC}",
4368
+ "xmlns:c16": "http://schemas.microsoft.com/office/drawing/2014/chart"
4369
+ }, raw(voidEl("c16:uniqueId", { val: `{${String(idx + 1).padStart(8, "0")}${chartUuid}}` }, { openPrefix: " " })), {
4370
+ openPrefix: " ",
4371
+ closePrefix: " "
4372
+ }))], {
4373
+ openPrefix: " ",
4374
+ closePrefix: " "
4375
+ });
4376
+ return el("c:dLbl", null, [
4377
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4378
+ raw(el("c:tx", null, raw(rich), {
4379
+ openPrefix: " ",
4380
+ closePrefix: " "
4381
+ })),
4382
+ raw(spPr),
4383
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition }, { openPrefix: " " })) : null,
4384
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4385
+ raw(voidEl("c:showVal", { val: 0 }, { openPrefix: " " })),
4386
+ raw(voidEl("c:showCatName", { val: 0 }, { openPrefix: " " })),
4387
+ raw(voidEl("c:showSerName", { val: 0 }, { openPrefix: " " })),
4388
+ raw(voidEl("c:showPercent", { val: 0 }, { openPrefix: " " })),
4389
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " })),
4390
+ raw(voidEl("c:showLeaderLines", { val: 1 }, { openPrefix: " " })),
4391
+ raw(extLst)
4392
+ ], { openPrefix: " " });
4393
+ }
4394
+ /** The single chart-level `<c:dLbls>` of the `XY` label format: PowerPoint composes the text. */
4395
+ function scatterXYLabels(opts) {
4396
+ const spPr = el("c:spPr", null, [
4397
+ raw(voidEl("a:noFill", null, { openPrefix: " " })),
4398
+ raw(el("a:ln", null, raw(voidEl("a:noFill", null, { openPrefix: " " })), {
4399
+ openPrefix: " ",
4400
+ closePrefix: " "
4401
+ })),
4402
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
4403
+ ], {
4404
+ openPrefix: " ",
4405
+ closePrefix: " "
4406
+ });
4407
+ const txPr = el("c:txPr", null, [
4408
+ raw(el("a:bodyPr", null, raw(voidEl("a:spAutoFit", null, { openPrefix: " " })), {
4409
+ openPrefix: " ",
4410
+ closePrefix: " "
4411
+ })),
4412
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4413
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(el("a:defRPr", labelFontAttrs(opts), labelFontChildren(opts, " "), {
4414
+ openPrefix: " ",
4415
+ closePrefix: " "
4416
+ })), {
4417
+ openPrefix: " ",
4418
+ closePrefix: " "
4419
+ })), raw(voidEl("a:endParaRPr", { lang: opts.lang || "en-US" }, { openPrefix: " " }))], {
4420
+ openPrefix: " ",
4421
+ closePrefix: " "
4422
+ }))
4423
+ ], {
4424
+ openPrefix: " ",
4425
+ closePrefix: " "
4426
+ });
4427
+ const extLst = el("c:extLst", null, raw(el("c:ext", {
4428
+ uri: "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}",
4429
+ "xmlns:c15": "http://schemas.microsoft.com/office/drawing/2012/chart"
4430
+ }, raw(voidEl("c15:showLeaderLines", { val: 1 }, { openPrefix: " " })), {
4431
+ openPrefix: " ",
4432
+ closePrefix: " "
4433
+ })), {
4434
+ openPrefix: " ",
4435
+ closePrefix: " "
4436
+ });
4437
+ return el("c:dLbls", null, [
4438
+ raw(spPr),
4439
+ raw(txPr),
4440
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition }, { openPrefix: " " })) : null,
4441
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4442
+ raw(voidEl("c:showVal", { val: opts.showLabel ? 1 : 0 }, { openPrefix: " " })),
4443
+ raw(voidEl("c:showCatName", { val: opts.showLabel ? 1 : 0 }, { openPrefix: " " })),
4444
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 }, { openPrefix: " " })),
4445
+ raw(voidEl("c:showPercent", { val: 0 }, { openPrefix: " " })),
4446
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " })),
4447
+ raw(extLst)
4448
+ ]);
4449
+ }
4450
+ /** Fill, outline and shadow for one scatter series. */
4451
+ function scatterSerShapeProps(opts, serColor, serIndex) {
4452
+ 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);
4453
+ const line = opts.lineSize === 0 ? el("a:ln", null, raw(voidEl("a:noFill"))) : el("a:ln", {
4454
+ w: ptsToEmuLenient(opts.lineSize ?? 2),
4455
+ cap: createLineCap(opts.lineCap)
4456
+ }, [
4457
+ raw(chartColorLineFill(serColor)),
4458
+ raw(voidEl("a:prstDash", { val: opts.lineDashValues?.[serIndex] ?? opts.lineDash ?? "solid" })),
4459
+ raw(voidEl("a:round"))
4460
+ ]);
4461
+ return el("c:spPr", null, [
4462
+ raw(fill),
4463
+ raw(line),
4464
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4465
+ ], {
4466
+ openPrefix: " ",
4467
+ closePrefix: " "
4468
+ });
4469
+ }
4470
+ /** The point marker: symbol, optional size, and its own fill and outline. */
4471
+ function scatterMarker(opts, markerColor) {
4472
+ const spPr = el("c:spPr", null, [
4473
+ raw(markerColor === "transparent" ? voidEl("a:noFill") : genXmlColorSelection(markerColor)),
4474
+ raw(el("a:ln", {
4475
+ w: opts.lineDataSymbolLineSize,
4476
+ cap: "flat"
4477
+ }, [
4478
+ raw(chartColorLineFill(opts.lineDataSymbolLineColor || markerColor)),
4479
+ raw(voidEl("a:prstDash", { val: "solid" })),
4480
+ raw(voidEl("a:round"))
4481
+ ])),
4482
+ raw(voidEl("a:effectLst"))
4483
+ ]);
4484
+ return el("c:marker", null, [
4485
+ raw(voidEl("c:symbol", { val: opts.lineDataSymbol }, { openPrefix: " " })),
4486
+ opts.lineDataSymbolSize ? raw(voidEl("c:size", { val: opts.lineDataSymbolSize })) : null,
4487
+ raw(spPr)
4488
+ ]);
4489
+ }
3982
4490
  /**
3983
4491
  * Plot an XY scatter chart into `<c:scatterChart>` (paired X/Y numeric series).
3984
4492
  */
3985
4493
  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
- }
4494
+ const chartColors = resolveChartPalette(opts);
4495
+ const xValues = dataValues(data[0]);
4496
+ const scatterVaryColors = data.length === 1 && opts.chartColors !== BARCHART_COLORS ? opts.chartColors || BARCHART_COLORS : null;
4497
+ const sers = data.slice(1).map((obj, idx) => {
4498
+ const serColor = paletteColor(chartColors, idx);
4499
+ const yValues = dataValues(obj);
4500
+ 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]));
4501
+ let labels = "";
4034
4502
  if (opts.showLabel) {
4035
4503
  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
- }
4158
- }
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]));
4504
+ const isCustom = opts.dataLabelFormatScatter === "custom" || opts.dataLabelFormatScatter === "customXY";
4505
+ if (dataLabels(obj)[0] && isCustom) labels += el("c:dLbls", null, raw(firstLabelGroup(obj).map((label, pointIdx) => scatterCustomLabel(obj, opts, label, pointIdx, chartUuid)).join("")));
4506
+ if (opts.dataLabelFormatScatter === "XY") labels += scatterXYLabels(opts);
4165
4507
  }
4166
- strXml += "<c:smooth val=\"" + (opts.lineSmooth ? "1" : "0") + "\"/>";
4167
- strXml += "</c:ser>";
4508
+ return el("c:ser", null, [
4509
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4510
+ raw(voidEl("c:order", { val: idx }, { openPrefix: " " })),
4511
+ raw(strRefBlock(sheetCellRef(idx + 2, 1), obj.name ?? "")),
4512
+ raw(scatterSerShapeProps(opts, serColor, idx)),
4513
+ raw(scatterMarker(opts, serColor)),
4514
+ raw(makeSeriesDataPointsXml(chartType, obj, opts, scatterVaryColors)),
4515
+ raw(labels),
4516
+ raw(makeChartErrorBarsXml(chartType, obj.errorBars, obj)),
4517
+ raw(values),
4518
+ raw(voidEl("c:smooth", { val: opts.lineSmooth ? 1 : 0 }))
4519
+ ]);
4520
+ }).join("");
4521
+ return el(`c:${chartType}Chart`, null, [
4522
+ raw(voidEl("c:scatterStyle", { val: "lineMarker" })),
4523
+ raw(voidEl("c:varyColors", { val: 0 })),
4524
+ raw(sers),
4525
+ raw(chartDataLabels(opts, false)),
4526
+ raw(voidEl("c:axId", { val: catAxisId })),
4527
+ raw(voidEl("c:axId", { val: valAxisId }))
4528
+ ]);
4529
+ }
4530
+ //#endregion
4531
+ //#region src/gen/chart/plot-bubble.ts
4532
+ /** Fill + outline + shadow for one bubble series, from the palette colour and the line options. */
4533
+ function bubbleSerShapeProps(opts, serColor, serIndex) {
4534
+ 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);
4535
+ const line = opts.lineSize === 0 ? el("a:ln", null, raw(voidEl("a:noFill"))) : opts.dataBorder ? el("a:ln", {
4536
+ w: ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75)),
4537
+ cap: "flat"
4538
+ }, [
4539
+ raw(genXmlColorSelection({
4540
+ color: opts.dataBorder.color ?? "363636",
4541
+ transparency: opts.dataBorder.transparency
4542
+ })),
4543
+ raw(voidEl("a:prstDash", { val: "solid" })),
4544
+ raw(voidEl("a:round"))
4545
+ ]) : el("a:ln", {
4546
+ w: ptsToEmuLenient(opts.lineSize ?? 2),
4547
+ cap: "flat"
4548
+ }, [
4549
+ raw(genXmlColorSelection(serColor)),
4550
+ raw(voidEl("a:prstDash", { val: opts.lineDashValues?.[serIndex] ?? opts.lineDash ?? "solid" })),
4551
+ raw(voidEl("a:round"))
4552
+ ]);
4553
+ return el("c:spPr", null, [
4554
+ raw(fill),
4555
+ raw(line),
4556
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4557
+ ]);
4558
+ }
4559
+ /**
4560
+ * The `<c:bubbleSize>` cache: the per-point sizes that scale each bubble. Its own block rather
4561
+ * than {@link numRefBlock} because the wrapping tag differs and so does the indentation — the
4562
+ * `<c:ptCount>` here sits three spaces further in than anything around it.
4563
+ */
4564
+ function bubbleSizeBlock(obj, ref) {
4565
+ const sizes = dataSizes(obj);
4566
+ const numCache = el("c:numCache", null, [
4567
+ raw(el("c:formatCode", null, "General", { openPrefix: " " })),
4568
+ raw(voidEl("c:ptCount", { val: sizes.length }, { openPrefix: " " })),
4569
+ raw(sizes.map((value, idx) => numCachePt(idx, value)).join(""))
4570
+ ], {
4571
+ openPrefix: " ",
4572
+ closePrefix: " "
4168
4573
  });
4169
- strXml += chartDataLabels(opts, false);
4170
- strXml += `<c:axId val="${catAxisId}"/><c:axId val="${valAxisId}"/>`;
4171
- strXml += "</c:" + chartType + "Chart>";
4172
- return strXml;
4574
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, ref)), raw(numCache)], {
4575
+ openPrefix: " ",
4576
+ closePrefix: " "
4577
+ });
4578
+ return el("c:bubbleSize", null, raw(numRef), {
4579
+ openPrefix: " ",
4580
+ closePrefix: " "
4581
+ });
4582
+ }
4583
+ /** The shared `<c:dLbls>` block: number format, label text style, and which parts are shown. */
4584
+ function bubbleDataLabels(opts) {
4585
+ const defRPr = el("a:defRPr", {
4586
+ b: opts.dataLabelFontBold ? 1 : 0,
4587
+ i: opts.dataLabelFontItalic ? 1 : 0,
4588
+ strike: "noStrike",
4589
+ sz: ptToHundredths(opts.dataLabelFontSize || 12),
4590
+ u: "none"
4591
+ }, [raw(genXmlColorSelection(opts.dataLabelColor || "000000")), raw(createChartTextFonts(opts.dataLabelFontFace || "Arial"))]);
4592
+ const txPr = el("c:txPr", null, [
4593
+ raw(voidEl("a:bodyPr")),
4594
+ raw(voidEl("a:lstStyle")),
4595
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(defRPr)))))
4596
+ ]);
4597
+ const extLst = el("c:extLst", null, raw(el("c:ext", {
4598
+ uri: "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}",
4599
+ "xmlns:c15": "http://schemas.microsoft.com/office/drawing/2012/chart"
4600
+ }, raw(voidEl("c15:showLeaderLines", { val: opts.showLeaderLines ? 1 : 0 }, { openPrefix: " " })), {
4601
+ openPrefix: " ",
4602
+ closePrefix: " "
4603
+ })));
4604
+ return el("c:dLbls", null, [
4605
+ raw(voidEl("c:numFmt", {
4606
+ formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4607
+ sourceLinked: 0
4608
+ })),
4609
+ raw(txPr),
4610
+ opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition })) : null,
4611
+ raw(voidEl("c:showLegendKey", { val: 0 })),
4612
+ raw(voidEl("c:showVal", { val: opts.showValue ? 1 : 0 })),
4613
+ raw(voidEl("c:showCatName", { val: 0 })),
4614
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 })),
4615
+ raw(voidEl("c:showPercent", { val: 0 })),
4616
+ raw(voidEl("c:showBubbleSize", { val: opts.showBubbleSize ? 1 : 0 })),
4617
+ raw(extLst)
4618
+ ]);
4173
4619
  }
4174
- //#endregion
4175
- //#region src/gen/chart/plot-bubble.ts
4176
4620
  /**
4177
4621
  * Plot a bubble / bubble3d chart into `<c:bubbleChart>` (X/Y plus per-point size).
4178
4622
  */
4179
4623
  function makeBubblePlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
4180
- let colorIndex = -1;
4624
+ const chartColors = resolveChartPalette(opts);
4625
+ const xValues = dataValues(data[0]);
4181
4626
  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>`;
4627
+ const sers = data.slice(1).map((obj, idx) => {
4628
+ const name = strRefBlock(sheetCellRef(idxColLtr + 1, 1), obj.name ?? "");
4629
+ const spPr = bubbleSerShapeProps(opts, paletteColor(chartColors, idx), idx);
4630
+ const yValues = dataValues(obj);
4631
+ const xVal = numRefBlock("c:xVal", `Sheet1!$A$2:$A$${xValues.length + 1}`, valFmtCode, xValues);
4632
+ const yVal = numRefBlock("c:yVal", sheetRangeRef(idxColLtr + 1, 2, idxColLtr + 1, xValues.length + 1), valFmtCode, xValues.map((_value, i) => yValues[i]), "");
4225
4633
  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;
4634
+ const sizeRef = sheetRangeRef(idxColLtr + 1, 2, idxColLtr + 1, dataSizes(obj).length + 1);
4635
+ idxColLtr++;
4636
+ return el("c:ser", null, [
4637
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4638
+ raw(voidEl("c:order", { val: idx }, { openPrefix: " " })),
4639
+ raw(name),
4640
+ raw(spPr),
4641
+ raw(xVal),
4642
+ raw(yVal),
4643
+ raw(bubbleSizeBlock(obj, sizeRef)),
4644
+ raw(voidEl("c:bubble3D", { val: chartType === "bubble3D" ? 1 : 0 }, { openPrefix: " " }))
4645
+ ]);
4646
+ }).join("");
4647
+ return el("c:bubbleChart", null, [
4648
+ raw(voidEl("c:varyColors", { val: 0 })),
4649
+ raw(sers),
4650
+ raw(bubbleDataLabels(opts)),
4651
+ raw(voidEl("c:axId", { val: catAxisId })),
4652
+ raw(voidEl("c:axId", { val: valAxisId }))
4653
+ ]);
4261
4654
  }
4262
4655
  //#endregion
4263
4656
  //#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({
4657
+ /** The label run properties both `<c:txPr>` spellings share, differing only in their indentation. */
4658
+ function labelDefRPr(opts, fontsIndent, fmt) {
4659
+ return el("a:defRPr", {
4660
+ sz: ptToHundredths(opts.dataLabelFontSize || 12),
4661
+ b: opts.dataLabelFontBold ? 1 : 0,
4662
+ i: opts.dataLabelFontItalic ? 1 : 0,
4663
+ u: "none",
4664
+ strike: "noStrike"
4665
+ }, [raw(genXmlColorSelection(opts.dataLabelColor || "000000")), raw(fontsIndent + createChartTextFonts(opts.dataLabelFontFace || "Arial"))], fmt);
4666
+ }
4667
+ /** One `<c:dPt>`: the slice's own fill, its border override, and the shared shadow. */
4668
+ function pieDataPoint(idx, ptStyle, opts, fallbackColor) {
4669
+ const border = ptStyle?.border ? createChartBorderLine(ptStyle.border) : opts.dataBorder ? el("a:ln", {
4670
+ w: ptsToEmuLenient(resolveBorderWidth(opts.dataBorder, .75)),
4671
+ cap: "flat"
4672
+ }, [
4673
+ raw(genXmlColorSelection({
4305
4674
  color: opts.dataBorder.color ?? "363636",
4306
4675
  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", {
4676
+ })),
4677
+ raw(voidEl("a:prstDash", { val: "solid" })),
4678
+ raw(voidEl("a:round"))
4679
+ ]) : "";
4680
+ const spPr = el("c:spPr", null, [
4681
+ raw(el("a:solidFill", null, raw(createColorElement(ptStyle?.fill || fallbackColor)))),
4682
+ raw(border),
4683
+ raw(createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4684
+ ], {
4685
+ openPrefix: " ",
4686
+ closePrefix: " "
4687
+ });
4688
+ return el("c:dPt", null, [
4689
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4690
+ raw(voidEl("c:bubble3D", { val: 0 }, { openPrefix: " " })),
4691
+ raw(spPr)
4692
+ ]);
4693
+ }
4694
+ /**
4695
+ * One `<c:dLbl>`: a pie labels its points, not its series, so every slice carries its own copy of
4696
+ * the number format, text style and show flags. A `customLabels` entry replaces the value with
4697
+ * literal rich text, which is why it also forces `<c:showVal>` off.
4698
+ */
4699
+ function pieDataLabel(idx, customLbl, opts, chartType) {
4700
+ const numFmt = voidEl("c:numFmt", {
4341
4701
  formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4342
4702
  sourceLinked: 0
4703
+ }, { openPrefix: " " });
4704
+ const txPr = el("c:txPr", null, [
4705
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
4706
+ raw(voidEl("a:lstStyle")),
4707
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(labelDefRPr(opts, " ", {
4708
+ openPrefix: " ",
4709
+ closePrefix: " "
4710
+ })), { closePrefix: " " })), { openPrefix: " " }))
4711
+ ], { closePrefix: " " });
4712
+ return el("c:dLbl", null, [
4713
+ raw(voidEl("c:idx", { val: idx }, { openPrefix: " " })),
4714
+ customLbl ? raw(el("c:tx", null, raw(el("c:rich", null, [
4715
+ raw(voidEl("a:bodyPr")),
4716
+ raw(voidEl("a:lstStyle")),
4717
+ raw(el("a:p", null, raw(el("a:r", null, [raw(voidEl("a:rPr", {
4718
+ lang: opts.lang || "en-US",
4719
+ dirty: 0
4720
+ })), raw(el("a:t", null, customLbl))]))))
4721
+ ])))) : null,
4722
+ raw(numFmt),
4723
+ raw(voidEl("c:spPr", null, { openPrefix: " " })),
4724
+ raw(txPr),
4725
+ chartType === "pie" && opts.dataLabelPosition ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition })) : null,
4726
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4727
+ raw(voidEl("c:showVal", { val: customLbl ? 0 : opts.showValue ? 1 : 0 }, { openPrefix: " " })),
4728
+ raw(voidEl("c:showCatName", { val: opts.showLabel ? 1 : 0 }, { openPrefix: " " })),
4729
+ raw(voidEl("c:showSerName", { val: opts.showSerName ? 1 : 0 }, { openPrefix: " " })),
4730
+ raw(voidEl("c:showPercent", { val: opts.showPercent ? 1 : 0 }, { openPrefix: " " })),
4731
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " }))
4732
+ ], { closePrefix: " " });
4733
+ }
4734
+ /** The `<c:cat>` slice-name reference and the `<c:val>` cache, both keyed on the label count. */
4735
+ function pieCategories(labels) {
4736
+ 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(""))], {
4737
+ openPrefix: " ",
4738
+ closePrefix: " "
4739
+ });
4740
+ const strRef = el("c:strRef", null, [raw(el("c:f", null, `Sheet1!$A$2:$A$${labels.length + 1}`, { openPrefix: " " })), raw(strCache)], {
4741
+ openPrefix: " ",
4742
+ closePrefix: " "
4743
+ });
4744
+ return el("c:cat", null, raw(strRef));
4745
+ }
4746
+ /** The `<c:val>` numeric cache. A missing value keeps its `<c:pt>` with an empty `<c:v>`. */
4747
+ function pieValues(obj, count, valFmtCode) {
4748
+ const points = dataValues(obj).map((value, idx) => el("c:pt", { idx }, raw(el("c:v", null, value || value === 0 ? value : "")))).join("");
4749
+ const numCache = el("c:numCache", null, [
4750
+ raw(el("c:formatCode", null, valFmtCode, { openPrefix: " " })),
4751
+ raw(voidEl("c:ptCount", { val: count }, { openPrefix: " " })),
4752
+ raw(points)
4753
+ ], {
4754
+ openPrefix: " ",
4755
+ closePrefix: " "
4756
+ });
4757
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, `Sheet1!$B$2:$B$${count + 1}`, { openPrefix: " " })), raw(numCache)], {
4758
+ openPrefix: " ",
4759
+ closePrefix: " "
4760
+ });
4761
+ return el("c:val", null, raw(numRef), {
4762
+ openPrefix: " ",
4763
+ closePrefix: " "
4343
4764
  });
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
4765
  }
4395
4766
  /**
4396
- * Create Category axis
4397
- * @param {ChartOptsInternal} opts - chart options
4398
- * @param {string} axisId - value
4399
- * @param {string} valAxisId - value
4400
- * @return {string} XML
4767
+ * Plot a single-series pie / doughnut chart into `<c:pieChart>` / `<c:doughnutChart>`.
4401
4768
  */
4769
+ function makePiePlot(chartType, data, opts, valFmtCode) {
4770
+ const optsChartData = data[0];
4771
+ if (!optsChartData) return "";
4772
+ const chartColors = resolveChartPalette(opts);
4773
+ const labels = firstLabelGroup(optsChartData);
4774
+ const spPr = el("c:spPr", null, [
4775
+ raw(el("a:solidFill", null, raw(voidEl("a:schemeClr", { val: "accent1" })), { openPrefix: " " })),
4776
+ raw(el("a:ln", {
4777
+ w: 9525,
4778
+ cap: "flat"
4779
+ }, [
4780
+ raw(el("a:solidFill", null, raw(voidEl("a:srgbClr", { val: "F9F9F9" })))),
4781
+ raw(voidEl("a:prstDash", { val: "solid" })),
4782
+ raw(voidEl("a:round"))
4783
+ ], { openPrefix: " " })),
4784
+ raw(opts.dataNoEffects ? voidEl("a:effectLst") : createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW))
4785
+ ], {
4786
+ openPrefix: " ",
4787
+ closePrefix: " "
4788
+ });
4789
+ const dLbls = el("c:dLbls", null, [
4790
+ raw(labels.map((_label, idx) => pieDataLabel(idx, optsChartData.customLabels?.[idx], opts, chartType)).join("")),
4791
+ raw(voidEl("c:numFmt", {
4792
+ formatCode: (opts.dataLabelFormatCode ?? "") || "General",
4793
+ sourceLinked: 0
4794
+ }, { openPrefix: " " })),
4795
+ raw(el("c:txPr", null, [
4796
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
4797
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
4798
+ raw(el("a:p", null, raw(el("a:pPr", null, raw(labelDefRPr(opts, " ", {
4799
+ openPrefix: " ",
4800
+ closePrefix: " "
4801
+ })), {
4802
+ openPrefix: " ",
4803
+ closePrefix: " "
4804
+ })), {
4805
+ openPrefix: " ",
4806
+ closePrefix: " "
4807
+ }))
4808
+ ], {
4809
+ openPrefix: " ",
4810
+ closePrefix: " "
4811
+ })),
4812
+ chartType === "pie" ? raw(voidEl("c:dLblPos", { val: opts.dataLabelPosition || "ctr" })) : null,
4813
+ raw(voidEl("c:showLegendKey", { val: 0 }, { openPrefix: " " })),
4814
+ raw(voidEl("c:showVal", { val: 0 }, { openPrefix: " " })),
4815
+ raw(voidEl("c:showCatName", { val: 1 }, { openPrefix: " " })),
4816
+ raw(voidEl("c:showSerName", { val: 0 }, { openPrefix: " " })),
4817
+ raw(voidEl("c:showPercent", { val: 1 }, { openPrefix: " " })),
4818
+ raw(voidEl("c:showBubbleSize", { val: 0 }, { openPrefix: " " })),
4819
+ raw(voidEl("c:showLeaderLines", { val: opts.showLeaderLines ? 1 : 0 }, { openPrefix: " " })),
4820
+ raw(createLeaderLinesElement(opts))
4821
+ ]);
4822
+ const ser = el("c:ser", null, [
4823
+ raw(voidEl("c:idx", { val: 0 }, { openPrefix: " " })),
4824
+ raw(voidEl("c:order", { val: 0 }, { openPrefix: " " })),
4825
+ raw(strRefBlock("Sheet1!$B$1", optsChartData.name ?? "", "expanded")),
4826
+ raw(spPr),
4827
+ raw(labels.map((_label, idx) => pieDataPoint(idx, optsChartData.pointStyles?.[idx], opts, paletteColor(chartColors, idx))).join("")),
4828
+ raw(dLbls),
4829
+ raw(pieCategories(labels)),
4830
+ raw(pieValues(optsChartData, labels.length, valFmtCode))
4831
+ ], { closePrefix: " " });
4832
+ return el(`c:${chartType}Chart`, null, [
4833
+ raw(voidEl("c:varyColors", { val: 1 }, { openPrefix: " " })),
4834
+ raw(ser),
4835
+ raw(voidEl("c:firstSliceAng", { val: opts.firstSliceAng ? Math.round(opts.firstSliceAng) : 0 }, { openPrefix: " " })),
4836
+ chartType === "doughnut" ? raw(voidEl("c:holeSize", { val: typeof opts.holeSize === "number" ? opts.holeSize : 50 })) : null
4837
+ ]);
4838
+ }
4402
4839
  //#endregion
4403
4840
  //#region src/gen/chart/plot-stock.ts
4404
4841
  /**
@@ -4432,54 +4869,56 @@ const STOCK_STYLE_SPEC = {
4432
4869
  /** True when the given (already-normalized) stock style leads with a Volume bar series. */
4433
4870
  const isVolumeStockStyle = (style) => !!style && STOCK_STYLE_SPEC[style].volume;
4434
4871
  /** 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>";
4872
+ const STOCK_DLBLS = el("c:dLbls", null, [
4873
+ raw(voidEl("c:showLegendKey", { val: 0 })),
4874
+ raw(voidEl("c:showVal", { val: 0 })),
4875
+ raw(voidEl("c:showCatName", { val: 0 })),
4876
+ raw(voidEl("c:showSerName", { val: 0 })),
4877
+ raw(voidEl("c:showPercent", { val: 0 })),
4878
+ raw(voidEl("c:showBubbleSize", { val: 0 }))
4879
+ ]);
4436
4880
  /** Emit the shared `<c:cat>` + `<c:val>` refs for a stock/volume series (single-level categories). */
4437
4881
  function stockCatVal(obj, opts, valFmtCode) {
4438
4882
  const cats = firstLabelGroup(obj);
4439
4883
  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;
4884
+ const catRef = `Sheet1!$A$2:$A$${cats.length + 1}`;
4885
+ const cat = el("c:cat", null, raw(opts.catLabelFormatCode ? catRefBlock("num", catRef, cats, opts.catLabelFormatCode || "General") : catRefBlock("str", catRef, cats)));
4886
+ const numCache = el("c:numCache", null, [
4887
+ raw(el("c:formatCode", null, valFmtCode)),
4888
+ raw(voidEl("c:ptCount", { val: cats.length })),
4889
+ raw(dataValues(obj).map((value, idx) => numCachePt(idx, value)).join(""))
4890
+ ]);
4891
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, sheetRangeRef(valColRow, 2, valColRow, cats.length + 1))), raw(numCache)]);
4892
+ return cat + el("c:val", null, raw(numRef));
4466
4893
  }
4467
4894
  /** Emit the `<c:tx>` series-name reference for a stock/volume series. */
4468
4895
  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>";
4896
+ return strRefBlock(sheetCellRef(obj._dataIndex + dataLabels(obj).length + 1, 1), obj.name ?? "", "compact");
4470
4897
  }
4471
4898
  /** Emit a single stock (line) series: invisible line, optional close-marker, cat/val refs. */
4472
4899
  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;
4900
+ const spPr = el("c:spPr", null, [raw(el("a:ln", {
4901
+ w: 19050,
4902
+ cap: "rnd"
4903
+ }, [raw(voidEl("a:noFill")), raw(voidEl("a:round"))])), raw(voidEl("a:effectLst"))]);
4904
+ const marker = markCloseColor ? el("c:marker", null, [
4905
+ raw(voidEl("c:symbol", { val: "dot" })),
4906
+ raw(voidEl("c:size", { val: 5 })),
4907
+ raw(el("c:spPr", null, [
4908
+ raw(genXmlColorSelection(markCloseColor)),
4909
+ raw(el("a:ln", { w: 9525 }, raw(genXmlColorSelection(markCloseColor)))),
4910
+ raw(voidEl("a:effectLst"))
4911
+ ]))
4912
+ ]) : el("c:marker", null, raw(voidEl("c:symbol", { val: "none" })));
4913
+ return el("c:ser", null, [
4914
+ raw(voidEl("c:idx", { val: obj._dataIndex })),
4915
+ raw(voidEl("c:order", { val: obj._dataIndex })),
4916
+ raw(stockSeriesName(obj)),
4917
+ raw(spPr),
4918
+ raw(marker),
4919
+ raw(stockCatVal(obj, opts, valFmtCode)),
4920
+ raw(voidEl("c:smooth", { val: 0 }))
4921
+ ]);
4483
4922
  }
4484
4923
  /**
4485
4924
  * Plot a stock chart. For `vhlc`/`vohlc` the first data series is a Volume column drawn as a
@@ -4494,32 +4933,52 @@ function makeStockPlot(_chartType, data, opts, valAxisId, catAxisId, valFmtCode)
4494
4933
  const stockSeries = spec.volume ? data.slice(1) : data;
4495
4934
  let strXml = "";
4496
4935
  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>";
4936
+ const volumeSer = el("c:ser", null, [
4937
+ raw(voidEl("c:idx", { val: volumeSeries._dataIndex })),
4938
+ raw(voidEl("c:order", { val: volumeSeries._dataIndex })),
4939
+ raw(stockSeriesName(volumeSeries)),
4940
+ raw(el("c:spPr", null, raw(genXmlColorSelection(chartColors[0] ?? "4472C4")))),
4941
+ raw(voidEl("c:invertIfNegative", { val: 0 })),
4942
+ raw(stockCatVal(volumeSeries, opts, valFmtCode))
4943
+ ]);
4944
+ strXml += el("c:barChart", null, [
4945
+ raw(voidEl("c:barDir", { val: "col" })),
4946
+ raw(voidEl("c:grouping", { val: "clustered" })),
4947
+ raw(voidEl("c:varyColors", { val: 0 })),
4948
+ raw(volumeSer),
4949
+ raw(STOCK_DLBLS),
4950
+ raw(voidEl("c:gapWidth", { val: 150 })),
4951
+ raw(voidEl("c:axId", { val: AXIS_ID_CATEGORY_PRIMARY })),
4952
+ raw(voidEl("c:axId", { val: AXIS_ID_VALUE_PRIMARY }))
4953
+ ]);
4954
+ }
4955
+ const sers = stockSeries.map((obj, idx) => {
4956
+ return makeStockLineSer(obj, opts, valFmtCode, !spec.upDownBars && idx === stockSeries.length - 1 ? paletteColor(chartColors, obj._dataIndex, "ED7D31") : null);
4957
+ }).join("");
4958
+ const hiLowLines = el("c:hiLowLines", null, raw(el("c:spPr", null, [raw(dimmedTextLine(75e3, 25e3)), raw(voidEl("a:effectLst"))])));
4959
+ const upDownBars = spec.upDownBars ? el("c:upDownBars", null, [
4960
+ raw(voidEl("c:gapWidth", { val: 150 })),
4961
+ raw(el("c:upBars", null, raw(el("c:spPr", null, [
4962
+ raw(el("a:solidFill", null, raw(voidEl("a:schemeClr", { val: "bg1" })))),
4963
+ raw(dimmedTextLine(65e3, 35e3)),
4964
+ raw(voidEl("a:effectLst"))
4965
+ ])))),
4966
+ raw(el("c:downBars", null, raw(el("c:spPr", null, [
4967
+ raw(dimmedTextFill(65e3, 35e3)),
4968
+ raw(dimmedTextLine(65e3, 35e3)),
4969
+ raw(voidEl("a:effectLst"))
4970
+ ]))))
4971
+ ]) : "";
4519
4972
  const stockCatId = spec.volume ? AXIS_ID_CATEGORY_SECONDARY : catAxisId;
4520
4973
  const stockValId = spec.volume ? AXIS_ID_VALUE_SECONDARY : valAxisId;
4521
- strXml += `<c:axId val="${stockCatId}"/><c:axId val="${stockValId}"/>`;
4522
- strXml += "</c:stockChart>";
4974
+ strXml += el("c:stockChart", null, [
4975
+ raw(sers),
4976
+ raw(STOCK_DLBLS),
4977
+ raw(hiLowLines),
4978
+ raw(upDownBars),
4979
+ raw(voidEl("c:axId", { val: stockCatId })),
4980
+ raw(voidEl("c:axId", { val: stockValId }))
4981
+ ]);
4523
4982
  return strXml;
4524
4983
  }
4525
4984
  //#endregion
@@ -4530,28 +4989,30 @@ const isSurface3D = (opts) => opts.surface3D !== false;
4530
4989
  function surfaceCatVal(obj, valFmtCode) {
4531
4990
  const cats = firstLabelGroup(obj);
4532
4991
  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;
4992
+ const numCache = el("c:numCache", null, [
4993
+ raw(el("c:formatCode", null, valFmtCode)),
4994
+ raw(voidEl("c:ptCount", { val: cats.length })),
4995
+ raw(dataValues(obj).map((value, idx) => numCachePt(idx, value)).join(""))
4996
+ ]);
4997
+ const numRef = el("c:numRef", null, [raw(el("c:f", null, sheetRangeRef(valCol, 2, valCol, cats.length + 1))), raw(numCache)]);
4998
+ return el("c:cat", null, raw(catRefBlock("str", `Sheet1!$A$2:$A$${cats.length + 1}`, cats))) + el("c:val", null, raw(numRef));
4544
4999
  }
4545
5000
  /** Emit a single surface series: name ref, 3-D shape props, and cat/val refs. */
4546
5001
  function makeSurfaceSer(obj, valFmtCode, seriesColor) {
4547
5002
  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;
5003
+ const spPr = el("c:spPr", null, [
5004
+ raw(genXmlColorSelection(seriesColor)),
5005
+ raw(voidEl("a:ln")),
5006
+ raw(voidEl("a:effectLst")),
5007
+ raw(voidEl("a:sp3d"))
5008
+ ]);
5009
+ return el("c:ser", null, [
5010
+ raw(voidEl("c:idx", { val: obj._dataIndex })),
5011
+ raw(voidEl("c:order", { val: obj._dataIndex })),
5012
+ raw(strRefBlock(sheetCellRef(nameCol, 1), obj.name ?? "", "compact")),
5013
+ raw(spPr),
5014
+ raw(surfaceCatVal(obj, valFmtCode))
5015
+ ]);
4555
5016
  }
4556
5017
  /**
4557
5018
  * Plot a surface chart into a `<c:surface3DChart>` (3-D) or `<c:surfaceChart>` (2-D contour). Every
@@ -4561,14 +5022,14 @@ function makeSurfaceSer(obj, valFmtCode, seriesColor) {
4561
5022
  function makeSurfacePlot(_chartType, data, opts, valAxisId, catAxisId, valFmtCode) {
4562
5023
  const tag = isSurface3D(opts) ? "surface3DChart" : "surfaceChart";
4563
5024
  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;
5025
+ const sers = data.map((obj, idx) => makeSurfaceSer(obj, valFmtCode, paletteColor(chartColors, idx, "4472C4"))).join("");
5026
+ return el(`c:${tag}`, null, [
5027
+ raw(voidEl("c:wireframe", { val: opts.surfaceWireframe ? 1 : 0 })),
5028
+ raw(sers),
5029
+ raw(voidEl("c:axId", { val: catAxisId })),
5030
+ raw(voidEl("c:axId", { val: valAxisId })),
5031
+ raw(voidEl("c:axId", { val: AXIS_ID_SERIES_PRIMARY }))
5032
+ ]);
4572
5033
  }
4573
5034
  /**
4574
5035
  * Build the `<c:view3D>` + floor/side/back walls for a surface chart. A 3-D surface tilts the scene
@@ -4577,13 +5038,23 @@ function makeSurfacePlot(_chartType, data, opts, valAxisId, catAxisId, valFmtCod
4577
5038
  * `<c:plotArea>` in CT_Chart document order (view3D → floor → sideWall → backWall → plotArea).
4578
5039
  */
4579
5040
  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;
5041
+ const wall = voidEl("c:thickness", { val: 0 }) + el("c:spPr", null, [
5042
+ raw(voidEl("a:noFill")),
5043
+ raw(el("a:ln", null, raw(voidEl("a:noFill")))),
5044
+ raw(voidEl("a:effectLst")),
5045
+ raw(voidEl("a:sp3d"))
5046
+ ]);
5047
+ const scene = isSurface3D(opts) ? [
5048
+ raw(voidEl("c:rotX", { val: opts.v3DRotX })),
5049
+ raw(voidEl("c:rotY", { val: opts.v3DRotY })),
5050
+ raw(voidEl("c:rAngAx", { val: 0 }))
5051
+ ] : [
5052
+ raw(voidEl("c:rotX", { val: 90 })),
5053
+ raw(voidEl("c:rotY", { val: 0 })),
5054
+ raw(voidEl("c:rAngAx", { val: 0 })),
5055
+ raw(voidEl("c:perspective", { val: 0 }))
5056
+ ];
5057
+ 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
5058
  }
4588
5059
  //#endregion
4589
5060
  //#region src/gen/chart/chart-xml.ts
@@ -4602,6 +5073,12 @@ function makeSurfaceScene(opts) {
4602
5073
  * written by the embedded workbook ({@link ./embed-xlsx}); that mapping lives in
4603
5074
  * {@link ./data-refs}.
4604
5075
  */
5076
+ /** The three namespaces every `<c:chartSpace>` declares, in the order PowerPoint writes them. */
5077
+ const CHART_SPACE_NS = {
5078
+ "xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
5079
+ "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
5080
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
5081
+ };
4605
5082
  /**
4606
5083
  * Whether a chart-area/plot-area `fill` says anything the fill dispatch can act on.
4607
5084
  *
@@ -4624,58 +5101,52 @@ function isStatedFill(fill) {
4624
5101
  return Boolean(fill?.color || fill?.type);
4625
5102
  }
4626
5103
  /**
4627
- * Build the chartSpace/chart header: chartSpace open, title (or autoTitleDeleted),
4628
- * optional 3D view, and the plotArea open with optional manual layout.
5104
+ * The chart title (or the `autoTitleDeleted` flag that suppresses PowerPoint's default one)
5105
+ * followed by the 3-D scene, for the two families that plot into one.
4629
5106
  */
4630
5107
  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, [
5108
+ const title = rel.opts.showTitle ? genXmlTitle({
5109
+ title: rel.opts.title || "Chart Title",
5110
+ color: rel.opts.titleColor,
5111
+ fontFace: rel.opts.titleFontFace,
5112
+ fontSize: rel.opts.titleFontSize || 18,
5113
+ titleAlign: rel.opts.titleAlign,
5114
+ titleBold: rel.opts.titleBold,
5115
+ titleItalic: rel.opts.titleItalic,
5116
+ titleUnderline: rel.opts.titleUnderline,
5117
+ titlePos: rel.opts.titlePos,
5118
+ titleRotate: rel.opts.titleRotate
5119
+ }, rel.opts.x, rel.opts.y) + voidEl("c:autoTitleDeleted", { val: 0 }) : voidEl("c:autoTitleDeleted", { val: 1 });
5120
+ if (rel.opts._type === "bar3D") return title + el("c:view3D", null, [
4656
5121
  raw(voidEl("c:rotX", { val: rel.opts.v3DRotX })),
4657
5122
  raw(voidEl("c:rotY", { val: rel.opts.v3DRotY })),
4658
5123
  raw(voidEl("c:rAngAx", { val: !rel.opts.v3DRAngAx ? 0 : 1 })),
4659
5124
  raw(voidEl("c:perspective", { val: rel.opts.v3DPerspective }))
4660
5125
  ]);
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;
5126
+ if (rel.opts._type === "surface") return title + makeSurfaceScene(rel.opts);
5127
+ return title;
5128
+ }
5129
+ /**
5130
+ * The plot area's `<c:layout>`. Manual layout is emitted only when the caller asked for one:
5131
+ * an empty `<c:layout/>` lets PowerPoint auto-fit, which it does well across all four TRBL
5132
+ * legend positions.
5133
+ */
5134
+ function makePlotAreaLayoutXml(rel) {
5135
+ const layout = rel.opts.layout;
5136
+ if (!layout) return voidEl("c:layout");
5137
+ const manualLayout = el("c:manualLayout", null, [
5138
+ raw(voidEl("c:layoutTarget", { val: "inner" }, { closePrefix: " " })),
5139
+ raw(voidEl("c:xMode", { val: "edge" }, { closePrefix: " " })),
5140
+ raw(voidEl("c:yMode", { val: "edge" }, { closePrefix: " " })),
5141
+ raw(voidEl("c:x", { val: layout.x || 0 }, { closePrefix: " " })),
5142
+ raw(voidEl("c:y", { val: layout.y || 0 }, { closePrefix: " " })),
5143
+ raw(voidEl("c:w", { val: layout.w || 1 }, { closePrefix: " " })),
5144
+ raw(voidEl("c:h", { val: layout.h || 1 }, { closePrefix: " " }))
5145
+ ], {
5146
+ childPrefix: " ",
5147
+ closePrefix: " "
5148
+ });
5149
+ return el("c:layout", null, raw(manualLayout), { childPrefix: " " });
4679
5150
  }
4680
5151
  /**
4681
5152
  * Build the category/value/series axis XML (empty for pie/doughnut). Resolves combo-chart
@@ -4745,97 +5216,132 @@ function makeChartAxesXml(rel, usesSecondaryValAxis, usesSecondaryCatAxis, prima
4745
5216
  return strXml;
4746
5217
  }
4747
5218
  /**
4748
- * Build the plotArea properties (data table, fill, border), close plotArea, then the legend.
5219
+ * The `<c:spPr>` shared by the plot area and the chart space: a stated fill or none, a border
5220
+ * or an explicit no-line, and an empty effect list. The two differ only in how far in they are
5221
+ * indented, which is what `fmt` and `effectIndent` carry.
4749
5222
  */
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>";
5223
+ function chartShapeProps(fill, border, effectIndent, fmt) {
5224
+ return el("c:spPr", null, [
5225
+ raw(isStatedFill(fill) ? genXmlColorSelection(fill) : voidEl("a:noFill")),
5226
+ raw(border ? el("a:ln", {
5227
+ w: ptsToEmuLenient(resolveBorderWidth(border, 1)),
5228
+ cap: "flat"
5229
+ }, raw(genXmlColorSelection({
5230
+ color: border.color ?? "363636",
5231
+ transparency: border.transparency
5232
+ }))) : el("a:ln", null, raw(voidEl("a:noFill")))),
5233
+ raw(voidEl("a:effectLst", null, { openPrefix: effectIndent }))
5234
+ ], fmt);
5235
+ }
5236
+ /**
5237
+ * The `<c:dTable>` grid drawn under the plot. Sits between the last `</c:valAx>` and the plot
5238
+ * area's own `<c:spPr>`.
5239
+ */
5240
+ function makeDataTableXml(rel) {
5241
+ const spPr = el("c:spPr", null, [
5242
+ raw(voidEl("a:noFill", null, { openPrefix: " " })),
5243
+ raw(dimmedTextLine(15e3, 85e3, { openPrefix: " " })),
5244
+ raw(voidEl("a:effectLst", null, { openPrefix: " " }))
5245
+ ], {
5246
+ openPrefix: " ",
5247
+ closePrefix: " "
5248
+ });
5249
+ const defRPr = el("a:defRPr", {
5250
+ sz: ptToHundredths(rel.opts.dataTableFontSize || 12),
5251
+ b: 0,
5252
+ i: 0,
5253
+ u: "none",
5254
+ strike: "noStrike",
5255
+ kern: 1200,
5256
+ baseline: 0
5257
+ }, [
5258
+ raw(" " + dimmedTextFill(65e3, 35e3)),
5259
+ raw(voidEl("a:latin", { typeface: "+mn-lt" }, { openPrefix: " " })),
5260
+ raw(voidEl("a:ea", { typeface: "+mn-ea" }, { openPrefix: " " })),
5261
+ raw(voidEl("a:cs", { typeface: "+mn-cs" }, { openPrefix: " " }))
5262
+ ], {
5263
+ openPrefix: " ",
5264
+ closePrefix: " "
5265
+ });
5266
+ const txPr = el("c:txPr", null, [
5267
+ raw(voidEl("a:bodyPr", {
5268
+ rot: 0,
5269
+ spcFirstLastPara: 1,
5270
+ vertOverflow: "ellipsis",
5271
+ vert: "horz",
5272
+ wrap: "square",
5273
+ anchor: "ctr",
5274
+ anchorCtr: 1
5275
+ }, { openPrefix: " " })),
5276
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
5277
+ raw(el("a:p", null, [raw(el("a:pPr", { rtl: 0 }, raw(defRPr), {
5278
+ openPrefix: " ",
5279
+ closePrefix: " "
5280
+ })), raw(voidEl("a:endParaRPr", { lang: "en-US" }, { openPrefix: " " }))], {
5281
+ openPrefix: " ",
5282
+ closePrefix: " "
5283
+ }))
5284
+ ], {
5285
+ openPrefix: " ",
5286
+ closePrefix: " "
5287
+ });
5288
+ return el("c:dTable", null, [
5289
+ raw(voidEl("c:showHorzBorder", { val: !rel.opts.showDataTableHorzBorder ? 0 : 1 }, { openPrefix: " " })),
5290
+ raw(voidEl("c:showVertBorder", { val: !rel.opts.showDataTableVertBorder ? 0 : 1 }, { openPrefix: " " })),
5291
+ raw(` <c:showOutline val="${!rel.opts.showDataTableOutline ? 0 : 1}"/>`),
5292
+ raw(` <c:showKeys val="${!rel.opts.showDataTableKeys ? 0 : 1}"/>`),
5293
+ raw(spPr),
5294
+ raw(txPr)
5295
+ ]);
5296
+ }
5297
+ /**
5298
+ * The `<c:legend>`: position, the entries a combo chart deletes, an optional manual placement,
5299
+ * and the text properties when any legend font option is set.
5300
+ */
5301
+ function makeLegendXml(rel) {
5302
+ let entries = "";
5303
+ if (Array.isArray(rel.opts._type)) {
5304
+ let seriesIdx = 0;
5305
+ for (const type of rel.opts._type) {
5306
+ 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 }))]);
5307
+ seriesIdx += type.data.length;
4835
5308
  }
4836
- strXml += "</c:legend>";
4837
5309
  }
4838
- return strXml;
5310
+ const legendLayout = rel.opts.legendLayout;
5311
+ const placed = (key) => typeof legendLayout?.[key] === "number";
5312
+ let layout = "";
5313
+ if (placed("x") || placed("y") || placed("w") || placed("h")) {
5314
+ const modes = (placed("x") ? voidEl("c:xMode", { val: "edge" }) : "") + (placed("y") ? voidEl("c:yMode", { val: "edge" }) : "");
5315
+ const vals = [
5316
+ "x",
5317
+ "y",
5318
+ "w",
5319
+ "h"
5320
+ ].filter(placed).map((key) => voidEl(`c:${key}`, { val: legendLayout?.[key] })).join("");
5321
+ layout = el("c:layout", null, raw(el("c:manualLayout", null, raw(modes + vals))));
5322
+ }
5323
+ let txPr = "";
5324
+ if (rel.opts.legendFontFace || rel.opts.legendFontSize || rel.opts.legendColor) {
5325
+ 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: " " });
5326
+ txPr = el("c:txPr", null, [
5327
+ raw(voidEl("a:bodyPr", null, { openPrefix: " " })),
5328
+ raw(voidEl("a:lstStyle", null, { openPrefix: " " })),
5329
+ raw(el("a:p", null, [raw(el("a:pPr", null, raw(defRPr), {
5330
+ openPrefix: " ",
5331
+ closePrefix: " "
5332
+ })), raw(voidEl("a:endParaRPr", { lang: "en-US" }, { openPrefix: " " }))], {
5333
+ openPrefix: " ",
5334
+ closePrefix: " "
5335
+ }))
5336
+ ]);
5337
+ }
5338
+ return el("c:legend", null, [
5339
+ raw(voidEl("c:legendPos", { val: rel.opts.legendPos })),
5340
+ raw(entries),
5341
+ raw(layout),
5342
+ raw(voidEl("c:overlay", { val: 0 })),
5343
+ raw(txPr)
5344
+ ]);
4839
5345
  }
4840
5346
  /**
4841
5347
  * Main entry point method for create charts
@@ -4844,16 +5350,16 @@ function makeChartPlotAreaPropsXml(rel) {
4844
5350
  * @return {string} XML
4845
5351
  */
4846
5352
  function makeXmlCharts(rel) {
4847
- let strXml = XML_DECL;
4848
5353
  const chartArea = rel.opts.chartArea ?? {};
5354
+ const plotAreaOpts = rel.opts.plotArea ?? {};
4849
5355
  let usesSecondaryValAxis = false;
4850
5356
  let usesSecondaryCatAxis = false;
4851
5357
  let primaryCatAxisValType = null;
4852
5358
  let secondaryCatAxisValType = null;
4853
5359
  let primaryCatAxisHasCategoryChart = false;
4854
5360
  let secondaryCatAxisHasCategoryChart = false;
4855
- strXml += makeChartHeaderXml(rel);
4856
- if (Array.isArray(rel.opts._type)) rel.opts._type.forEach((type) => {
5361
+ let plots = "";
5362
+ if (Array.isArray(rel.opts._type)) for (const type of rel.opts._type) {
4857
5363
  const options = {
4858
5364
  ...rel.opts,
4859
5365
  ...type.options
@@ -4869,27 +5375,36 @@ function makeXmlCharts(rel) {
4869
5375
  else secondaryCatAxisHasCategoryChart = true;
4870
5376
  } else if (usesValueXAxis) primaryCatAxisValType = subType;
4871
5377
  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;
5378
+ plots += makeChartType(subType, type.data, options, valAxisId, catAxisId);
5379
+ }
5380
+ else if (rel.opts._type) plots = makeChartType(rel.opts._type, rel.data, rel.opts, AXIS_ID_VALUE_PRIMARY, AXIS_ID_CATEGORY_PRIMARY);
5381
+ const axes = makeChartAxesXml(rel, usesSecondaryValAxis, usesSecondaryCatAxis, primaryCatAxisValType, secondaryCatAxisValType, primaryCatAxisHasCategoryChart, secondaryCatAxisHasCategoryChart);
5382
+ const plotArea = el("c:plotArea", null, [
5383
+ raw(makePlotAreaLayoutXml(rel)),
5384
+ raw(plots),
5385
+ raw(axes),
5386
+ rel.opts.showDataTable ? raw(makeDataTableXml(rel)) : null,
5387
+ raw(chartShapeProps(plotAreaOpts.fill, plotAreaOpts.border, " ", {
5388
+ openPrefix: " ",
5389
+ closePrefix: " "
5390
+ }))
5391
+ ]);
5392
+ const chart = el("c:chart", null, [
5393
+ raw(makeChartHeaderXml(rel)),
5394
+ raw(plotArea),
5395
+ rel.opts.showLegend ? raw(makeLegendXml(rel)) : null,
5396
+ raw(voidEl("c:plotVisOnly", { val: 1 }, { openPrefix: " " })),
5397
+ raw(voidEl("c:dispBlanksAs", { val: rel.opts.displayBlanksAs }, { openPrefix: " " })),
5398
+ rel.opts._type === "scatter" ? raw(voidEl("c:showDLblsOverMax", { val: 1 })) : null
5399
+ ]);
5400
+ return XML_DECL + el("c:chartSpace", CHART_SPACE_NS, [
5401
+ raw(voidEl("c:date1904", { val: 0 })),
5402
+ raw(voidEl("c:roundedCorners", { val: chartArea.roundedCorners ? 1 : 0 })),
5403
+ raw(chart),
5404
+ raw(chartShapeProps(chartArea.fill, chartArea.border, " ", {})),
5405
+ raw(el("c:externalData", { "r:id": "rId1" }, raw(voidEl("c:autoUpdate", { val: 0 })))),
5406
+ raw(genXmlChartMetadata(rel.opts.metadata))
5407
+ ]);
4893
5408
  }
4894
5409
  /**
4895
5410
  * Stable ts-pptx vendor GUID identifying the chart-metadata extension on `c:chartSpace/c:extLst`.
@@ -4944,7 +5459,7 @@ function genXmlChartMetadata(metadata) {
4944
5459
  * @return {string} XML chart
4945
5460
  */
4946
5461
  function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
4947
- const valFmtCode = encodeXmlEntities(opts.valLabelFormatCode || opts.dataTableFormatCode || opts.dataLabelFormatCode || "General");
5462
+ const valFmtCode = opts.valLabelFormatCode || opts.dataTableFormatCode || opts.dataLabelFormatCode || "General";
4948
5463
  switch (chartType) {
4949
5464
  case "area":
4950
5465
  case "bar":
@@ -5328,6 +5843,13 @@ function makeChartExStyleXml() {
5328
5843
  //#region src/gen/chart/embed-xlsx.ts
5329
5844
  /** MS chart-extension relationship types (chartEx style + color-style sidecar parts). */
5330
5845
  const MS_CHART_REL = "http://schemas.microsoft.com/office/2011/relationships/";
5846
+ /** The SpreadsheetML namespace every part of the embedded workbook is written in. */
5847
+ const SML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
5848
+ /** OPC content types, and the prefix the four SpreadsheetML part types share. */
5849
+ const CT_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
5850
+ const SML_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.";
5851
+ /** One `<si>` shared string carrying literal text. */
5852
+ const sharedString = (text) => el("si", null, raw(el("t", null, text)));
5331
5853
  function relationship$2(id, type, target) {
5332
5854
  return voidEl("Relationship", {
5333
5855
  Id: id,
@@ -5340,6 +5862,17 @@ function relationships(rels) {
5340
5862
  return el("Relationships", { xmlns: PACKAGE_REL_NS }, rels.map(raw));
5341
5863
  }
5342
5864
  /**
5865
+ * The embedded workbook's style sheet, captured verbatim from an Excel-authored chart workbook.
5866
+ * @raw-xml-asset
5867
+ */
5868
+ 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";
5869
+ /**
5870
+ * The Office theme the embedded workbook ships, captured verbatim. PowerPoint reads it when a user
5871
+ * opens the chart's data, so it has to be exactly the bytes Office writes.
5872
+ * @raw-xml-asset
5873
+ */
5874
+ 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>";
5875
+ /**
5343
5876
  * Build the chart's embedded Excel workbook as a standalone OPC package and
5344
5877
  * return its bytes — the data source PowerPoint opens when a user edits the
5345
5878
  * chart's data. Pure (no zip side effects), so both the package write path
@@ -5354,24 +5887,108 @@ function buildEmbeddedWorksheet(chartObject) {
5354
5887
  const zipExcel = new ZipWriter();
5355
5888
  const intBubbleCols = (data.length - 1) * 2 + 1;
5356
5889
  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");
5890
+ {
5891
+ const override = (partName, contentType) => voidEl("Override", {
5892
+ PartName: partName,
5893
+ ContentType: contentType
5894
+ }, { openPrefix: " " });
5895
+ zipExcel.add("[Content_Types].xml", XML_DECL + el("Types", { xmlns: CT_NS }, [
5896
+ raw(voidEl("Default", {
5897
+ Extension: "rels",
5898
+ ContentType: "application/vnd.openxmlformats-package.relationships+xml"
5899
+ }, { openPrefix: " " })),
5900
+ raw(voidEl("Default", {
5901
+ Extension: "xml",
5902
+ ContentType: "application/xml"
5903
+ }, { openPrefix: " " })),
5904
+ raw(override("/xl/workbook.xml", SML_CT + "sheet.main+xml")),
5905
+ raw(override("/xl/worksheets/sheet1.xml", SML_CT + "worksheet+xml")),
5906
+ raw(override("/xl/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml")),
5907
+ raw(override("/xl/styles.xml", SML_CT + "styles+xml")),
5908
+ raw(override("/xl/sharedStrings.xml", SML_CT + "sharedStrings+xml")),
5909
+ raw(override("/xl/tables/table1.xml", SML_CT + "table+xml")),
5910
+ raw(override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml")),
5911
+ raw(override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml"))
5912
+ ]) + "\n");
5913
+ zipExcel.add("_rels/.rels", XML_DECL + relationships([
5914
+ relationship$2("rId1", PACKAGE_REL_NS + "/metadata/core-properties", "docProps/core.xml"),
5915
+ relationship$2("rId2", OFFICE_REL + "extended-properties", "docProps/app.xml"),
5916
+ relationship$2("rId3", OFFICE_REL + "officeDocument", "xl/workbook.xml")
5917
+ ]) + "\n");
5918
+ const headingPairs = el("vt:vector", {
5919
+ size: 2,
5920
+ baseType: "variant"
5921
+ }, [raw(el("vt:variant", null, raw(el("vt:lpstr", null, "Worksheets")))), raw(el("vt:variant", null, raw(el("vt:i4", null, 1))))]);
5922
+ zipExcel.add("docProps/app.xml", XML_DECL + el("Properties", {
5923
+ xmlns: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
5924
+ "xmlns:vt": "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
5925
+ }, [
5926
+ raw(el("Application", null, "Microsoft Macintosh Excel")),
5927
+ raw(el("DocSecurity", null, 0)),
5928
+ raw(el("ScaleCrop", null, "false")),
5929
+ raw(el("HeadingPairs", null, raw(headingPairs))),
5930
+ raw(el("TitlesOfParts", null, raw(el("vt:vector", {
5931
+ size: 1,
5932
+ baseType: "lpstr"
5933
+ }, raw(el("vt:lpstr", null, "Sheet1")))))),
5934
+ raw(el("Company", null)),
5935
+ raw(el("LinksUpToDate", null, "false")),
5936
+ raw(el("SharedDoc", null, "false")),
5937
+ raw(el("HyperlinksChanged", null, "false")),
5938
+ raw(el("AppVersion", null, "16.0300"))
5939
+ ]) + "\n");
5940
+ zipExcel.add("docProps/core.xml", XML_DECL + el("cp:coreProperties", {
5941
+ "xmlns:cp": "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
5942
+ "xmlns:dc": "http://purl.org/dc/elements/1.1/",
5943
+ "xmlns:dcterms": "http://purl.org/dc/terms/",
5944
+ "xmlns:dcmitype": "http://purl.org/dc/dcmitype/",
5945
+ "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance"
5946
+ }, [
5947
+ raw(el("dc:creator", null, "TsPptx")),
5948
+ raw(el("cp:lastModifiedBy", null, "TsPptx")),
5949
+ raw(el("dcterms:created", { "xsi:type": "dcterms:W3CDTF" }, (/* @__PURE__ */ new Date()).toISOString())),
5950
+ raw(el("dcterms:modified", { "xsi:type": "dcterms:W3CDTF" }, (/* @__PURE__ */ new Date()).toISOString()))
5951
+ ]));
5952
+ zipExcel.add("xl/_rels/workbook.xml.rels", XML_DECL + relationships([
5953
+ relationship$2("rId3", OFFICE_REL + "styles", "styles.xml"),
5954
+ relationship$2("rId2", OFFICE_REL + "theme", "theme/theme1.xml"),
5955
+ relationship$2("rId1", OFFICE_REL + "worksheet", "worksheets/sheet1.xml"),
5956
+ relationship$2("rId4", OFFICE_REL + "sharedStrings", "sharedStrings.xml")
5957
+ ]));
5958
+ zipExcel.add("xl/styles.xml", XML_DECL + XLSX_STYLES_XML);
5959
+ zipExcel.add("xl/theme/theme1.xml", XML_DECL + XLSX_THEME_XML);
5960
+ zipExcel.add("xl/workbook.xml", XML_DECL + el("workbook", {
5961
+ xmlns: SML_NS,
5962
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
5963
+ "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
5964
+ "mc:Ignorable": "x15",
5965
+ "xmlns:x15": "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"
5966
+ }, [
5967
+ raw(voidEl("fileVersion", {
5968
+ appName: "xl",
5969
+ lastEdited: 7,
5970
+ lowestEdited: 6,
5971
+ rupBuild: 10507
5972
+ })),
5973
+ raw(voidEl("workbookPr")),
5974
+ raw(el("bookViews", null, raw(voidEl("workbookView", {
5975
+ xWindow: 0,
5976
+ yWindow: 500,
5977
+ windowWidth: 20960,
5978
+ windowHeight: 15960
5979
+ })))),
5980
+ raw(el("sheets", null, raw(voidEl("sheet", {
5981
+ name: "Sheet1",
5982
+ sheetId: 1,
5983
+ "r:id": "rId1"
5984
+ })))),
5985
+ raw(voidEl("calcPr", {
5986
+ calcId: 0,
5987
+ concurrentCalc: 0
5988
+ }))
5989
+ ]) + "\n");
5990
+ zipExcel.add("xl/worksheets/_rels/sheet1.xml.rels", XML_DECL + relationships([relationship$2("rId1", OFFICE_REL + "table", "../tables/table1.xml")]) + "\n");
5991
+ }
5375
5992
  zipExcel.add("xl/sharedStrings.xml", buildXlsxSharedStrings(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
5376
5993
  zipExcel.add("xl/tables/table1.xml", buildXlsxTable(chartObject, data, intBubbleCols));
5377
5994
  zipExcel.add("xl/worksheets/sheet1.xml", buildXlsxSheet(chartObject, data, intBubbleCols, IS_MULTI_CAT_AXES));
@@ -5382,167 +5999,201 @@ function buildEmbeddedWorksheet(chartObject) {
5382
5999
  * Build the embedded workbook's `xl/sharedStrings.xml` (series names + category labels).
5383
6000
  */
5384
6001
  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}">`;
6002
+ const isBubble = chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D";
6003
+ const isScatter = chartObject.opts._type === "scatter";
6004
+ let count;
6005
+ let uniqueCount;
6006
+ let blank = "";
6007
+ if (isBubble) count = uniqueCount = intBubbleCols;
6008
+ else if (isScatter) count = uniqueCount = data.length;
5388
6009
  else if (IS_MULTI_CAT_AXES) {
5389
6010
  let totCount = data.length + 1;
5390
6011
  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>";
6012
+ count = uniqueCount = totCount;
6013
+ blank = el("si", null, raw(voidEl("t")));
5393
6014
  } 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;
6015
+ count = data.length + dataLabels(data[0]).length * firstLabelGroup(data[0]).length + dataLabels(data[0]).length;
6016
+ uniqueCount = data.length + dataLabels(data[0]).length * firstLabelGroup(data[0]).length + 1;
6017
+ blank = el("si", null, raw(el("t", { "xml:space": "preserve" })));
6018
+ }
6019
+ 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("");
6020
+ const labels = isBubble || isScatter ? "" : dataLabels(data[0]).slice().reverse().map((labelsGroup) => labelsGroup.filter((label) => label && label !== "").map((label) => sharedString(label)).join("")).join("");
6021
+ return XML_DECL + el("sst", {
6022
+ xmlns: SML_NS,
6023
+ count,
6024
+ uniqueCount
6025
+ }, [
6026
+ raw(blank),
6027
+ raw(names),
6028
+ raw(labels)
6029
+ ]) + "\n";
5416
6030
  }
5417
6031
  /**
5418
6032
  * Build the embedded workbook's `xl/tables/table1.xml` (the data table over the sheet range).
5419
6033
  */
5420
6034
  function buildXlsxTable(chartObject, data, intBubbleCols) {
5421
- let strTableXml = XML_DECL;
6035
+ const labelCols = dataLabels(data[0]).length;
6036
+ let ref;
6037
+ let columns;
5422
6038
  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}">`;
6039
+ ref = `A1:${getExcelColName(intBubbleCols)}${intBubbleCols}`;
5425
6040
  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
- });
6041
+ columns = data.map((obj, idx) => {
6042
+ if (idx === 0) return voidEl("tableColumn", {
6043
+ id: idx + 1,
6044
+ name: "X-Values"
6045
+ });
6046
+ const nameCol = voidEl("tableColumn", {
6047
+ id: idx + idxColLtr,
6048
+ name: obj.name
6049
+ });
6050
+ idxColLtr++;
6051
+ return nameCol + voidEl("tableColumn", {
6052
+ id: idx + idxColLtr,
6053
+ name: `Size${idx}`
6054
+ });
6055
+ }).join("");
5434
6056
  } 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
- });
6057
+ ref = `A1:${getExcelColName(data.length)}${dataValues(data[0]).length + 1}`;
6058
+ columns = data.map((_obj, idx) => voidEl("tableColumn", {
6059
+ id: idx + 1,
6060
+ name: `${idx === 0 ? "X-Values" : "Y-Value "}${idx}`
6061
+ })).join("");
5440
6062
  } 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
- });
6063
+ ref = `A1:${getExcelColName(data.length + labelCols)}${firstLabelGroup(data[0]).length + 1}`;
6064
+ columns = dataLabels(data[0]).map((_labelsGroup, idx) => voidEl("tableColumn", {
6065
+ id: idx + 1,
6066
+ name: `Column${idx + 1}`
6067
+ })).join("") + data.map((obj, idx) => voidEl("tableColumn", {
6068
+ id: idx + labelCols + 1,
6069
+ name: obj.name ?? ""
6070
+ })).join("");
5449
6071
  }
5450
- strTableXml += "</tableColumns>";
5451
- strTableXml += "<tableStyleInfo showFirstColumn=\"0\" showLastColumn=\"0\" showRowStripes=\"1\" showColumnStripes=\"0\"/>";
5452
- strTableXml += "</table>";
5453
- return strTableXml;
6072
+ const columnCount = chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D" ? intBubbleCols : chartObject.opts._type === "scatter" ? data.length : data.length + labelCols;
6073
+ return XML_DECL + el("table", {
6074
+ xmlns: SML_NS,
6075
+ id: 1,
6076
+ name: "Table1",
6077
+ displayName: "Table1",
6078
+ ref,
6079
+ totalsRowShown: 0
6080
+ }, [raw(el("tableColumns", { count: columnCount }, raw(columns))), raw(voidEl("tableStyleInfo", {
6081
+ showFirstColumn: 0,
6082
+ showLastColumn: 0,
6083
+ showRowStripes: 1,
6084
+ showColumnStripes: 0
6085
+ }))]);
5454
6086
  }
5455
6087
  /**
5456
6088
  * Build the embedded workbook's `xl/worksheets/sheet1.xml` (header row + per-series data rows).
5457
6089
  */
5458
6090
  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>";
6091
+ const isBubble = chartObject.opts._type === "bubble" || chartObject.opts._type === "bubble3D";
6092
+ const isScatter = chartObject.opts._type === "scatter";
6093
+ const labelCols = dataLabels(data[0]).length;
6094
+ /** One cell. `t="s"` marks a shared-string index; without it the value is a number. */
6095
+ const cell = (col, row, value, shared = false) => el("c", {
6096
+ r: `${getExcelColName(col)}${row}`,
6097
+ t: shared ? "s" : void 0
6098
+ }, raw(el("v", null, value)));
6099
+ /** One row, spanning the sheet's full column count. */
6100
+ const sheetRow = (row, span, cells) => el("row", {
6101
+ r: row,
6102
+ spans: `1:${span}`
6103
+ }, raw(cells));
6104
+ const colCount = isBubble ? intBubbleCols : isScatter ? data.length : data.length + labelCols;
6105
+ let rows = "";
6106
+ if (isBubble) {
6107
+ let header = "";
6108
+ for (let idx = 0; idx < intBubbleCols; idx++) header += cell(idx + 1, 1, idx, true);
6109
+ rows += sheetRow(1, intBubbleCols, header);
5472
6110
  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>`;
6111
+ let cells = cell(1, idx + 2, val);
5475
6112
  let idxColLtr = 2;
5476
6113
  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++;
6114
+ cells += cell(idxColLtr++, idx + 2, dataValues(data[idy])[idx] ?? "");
6115
+ cells += cell(idxColLtr++, idx + 2, dataSizes(data[idy])[idx] ?? "");
5481
6116
  }
5482
- strSheetXml += "</row>";
6117
+ rows += sheetRow(idx + 2, intBubbleCols, cells);
5483
6118
  });
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>";
6119
+ } else if (isScatter) {
6120
+ let header = "";
6121
+ for (let idx = 0; idx < data.length; idx++) header += cell(idx + 1, 1, idx, true);
6122
+ rows += sheetRow(1, data.length, header);
5489
6123
  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>";
6124
+ let cells = cell(1, idx + 2, val);
6125
+ for (let idy = 1; idy < data.length; idy++) {
6126
+ const yValue = dataValues(data[idy])[idx];
6127
+ cells += cell(idy + 1, idx + 2, yValue || yValue === 0 ? yValue : "");
6128
+ }
6129
+ rows += sheetRow(idx + 2, data.length, cells);
5494
6130
  });
6131
+ } else if (!IS_MULTI_CAT_AXES) {
6132
+ let header = "";
6133
+ for (let idx = 0; idx < labelCols; idx++) header += cell(idx + 1, 1, 0, true);
6134
+ for (let idx = 0; idx < data.length; idx++) header += cell(idx + 1 + labelCols, 1, idx + 1, true);
6135
+ rows += sheetRow(1, data.length + labelCols, header);
6136
+ const rowCount = firstLabelGroup(data[0]).length || Math.max(0, ...data.map((series) => dataValues(series).length));
6137
+ for (let idx = 0; idx < rowCount; idx++) {
6138
+ let cells = "";
6139
+ for (let idx2 = labelCols - 1; idx2 >= 0; idx2--) cells += cell(labelCols - idx2, idx + 2, data.length + idx + 1, true);
6140
+ for (let idy = 0; idy < data.length; idy++) cells += cell(labelCols + idy + 1, idx + 2, dataValues(data[idy])[idx] ?? "");
6141
+ rows += sheetRow(idx + 2, data.length + labelCols, cells);
6142
+ }
5495
6143
  } 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
- });
6144
+ const TOT_SER = data.length;
6145
+ const TOT_CAT = firstLabelGroup(data[0]).length;
6146
+ const revLabelGroups = dataLabels(data[0]).slice().reverse();
6147
+ const ssLabelMap = /* @__PURE__ */ new Map();
6148
+ let ssIdx = TOT_SER + 1;
6149
+ revLabelGroups.forEach((labelsGroup, revLevelIdx) => {
6150
+ labelsGroup.forEach((label, rowIdx) => {
6151
+ if (label && label !== "") ssLabelMap.set(`${revLevelIdx}:${rowIdx}`, ssIdx++);
5526
6152
  });
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
- }
6153
+ });
6154
+ let header = "";
6155
+ for (let col = 1; col <= labelCols; col++) header += cell(col, 1, 0, true);
6156
+ for (let ser = 0; ser < TOT_SER; ser++) header += cell(labelCols + ser + 1, 1, ser + 1, true);
6157
+ rows += sheetRow(1, TOT_SER + labelCols, header);
6158
+ for (let idx = 0; idx < TOT_CAT; idx++) {
6159
+ let cells = revLabelGroups.map((labelsGroup, idy) => {
6160
+ const colLabel = labelsGroup[idx];
6161
+ return colLabel && colLabel !== "" ? cell(idy + 1, idx + 2, ssLabelMap.get(`${idy}:${idx}`) ?? "", true) : "";
6162
+ }).join("");
6163
+ for (let idy = 0; idy < TOT_SER; idy++) cells += cell(labelCols + idy + 1, idx + 2, dataValues(data[idy])[idx] ?? "");
6164
+ rows += sheetRow(idx + 2, TOT_SER + labelCols, cells);
5540
6165
  }
5541
6166
  }
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;
6167
+ const sheetView = el("sheetViews", null, raw(el("sheetView", {
6168
+ tabSelected: 1,
6169
+ workbookViewId: 0
6170
+ }, raw(voidEl("selection", {
6171
+ activeCell: "B1",
6172
+ sqref: "B1"
6173
+ })))));
6174
+ return XML_DECL + el("worksheet", {
6175
+ xmlns: SML_NS,
6176
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
6177
+ "xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
6178
+ "mc:Ignorable": "x14ac",
6179
+ "xmlns:x14ac": "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"
6180
+ }, [
6181
+ raw(voidEl("dimension", { ref: `A1:${getExcelColName(colCount)}${dataValues(data[0]).length + 1}` })),
6182
+ raw(sheetView),
6183
+ raw(voidEl("sheetFormatPr", {
6184
+ baseColWidth: 10,
6185
+ defaultRowHeight: 16
6186
+ })),
6187
+ raw(el("sheetData", null, raw(rows))),
6188
+ raw(voidEl("pageMargins", {
6189
+ left: .7,
6190
+ right: .7,
6191
+ top: .75,
6192
+ bottom: .75,
6193
+ header: .3,
6194
+ footer: .3
6195
+ }))
6196
+ ]) + "\n";
5546
6197
  }
5547
6198
  /**
5548
6199
  * Build the standalone `.rels` for a chart part: a single `rId1` relationship to
@@ -7012,17 +7663,17 @@ function fallbackPic(picId, objectName, altText, model, xf) {
7012
7663
  return el("p:pic", null, [
7013
7664
  raw(el("p:nvPicPr", null, [
7014
7665
  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"
7666
+ raw(el("p:cNvPicPr", null, raw(genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
7667
+ noGrp: true,
7668
+ noRot: true,
7669
+ noChangeAspect: true,
7670
+ noMove: true,
7671
+ noResize: true,
7672
+ noEditPoints: true,
7673
+ noAdjustHandles: true,
7674
+ noChangeArrowheads: true,
7675
+ noChangeShapeType: true,
7676
+ noCrop: true
7026
7677
  })))),
7027
7678
  raw(voidEl("p:nvPr"))
7028
7679
  ])),
@@ -7064,7 +7715,7 @@ function renderModel3dObject(slideItemObj, idx, x, y, cx, cy) {
7064
7715
  const graphicFrame = el("p:graphicFrame", null, [
7065
7716
  raw(el("p:nvGraphicFramePr", null, [
7066
7717
  raw(cNvPrOpen(idx + 2, opts.objectName, altText) + "/>"),
7067
- raw(el("p:cNvGraphicFramePr", null, raw(voidEl("a:graphicFrameLocks")))),
7718
+ raw(el("p:cNvGraphicFramePr", null, raw(genXmlObjectLock("a:graphicFrameLocks", GRAPHIC_FRAME_LOCK_ATTRS, opts.objectLock, opts.objectName) || voidEl("a:graphicFrameLocks")))),
7068
7719
  raw(voidEl("p:nvPr"))
7069
7720
  ])),
7070
7721
  raw(el("p:xfrm", null, [raw(voidEl("a:off", {
@@ -7355,7 +8006,7 @@ function renderTableObject(slideItemObj, idx, x, y, cx, cy, placeholderObj, item
7355
8006
  let intColCnt = 0;
7356
8007
  let tblInner = "";
7357
8008
  let cellOpts = null;
7358
- arrTabRows = (slideItemObj.arrTabRows ?? []).map((row) => [...row]);
8009
+ arrTabRows = withCheckedSpans((slideItemObj.arrTabRows ?? []).map((row) => [...row]));
7359
8010
  objTabOpts = itemOpts;
7360
8011
  intColCnt = 0;
7361
8012
  (arrTabRows[0] ?? []).forEach((cell) => {
@@ -7639,16 +8290,16 @@ function zoomFallbackPic(picId, objectName, tile, absXf) {
7639
8290
  "r:id": `rId${tile.fallbackSlideRid}`,
7640
8291
  action: "ppaction://hlinksldjump"
7641
8292
  }) + "</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"
8293
+ raw(el("p:cNvPicPr", null, raw(genXmlObjectLock("a:picLocks", PICTURE_LOCK_ATTRS, {
8294
+ noGrp: true,
8295
+ noRot: true,
8296
+ noChangeAspect: true,
8297
+ noMove: true,
8298
+ noResize: true,
8299
+ noEditPoints: true,
8300
+ noAdjustHandles: true,
8301
+ noChangeArrowheads: true,
8302
+ noChangeShapeType: true
7652
8303
  })))),
7653
8304
  raw(voidEl("p:nvPr"))
7654
8305
  ])),
@@ -7675,7 +8326,8 @@ function zoomFallbackPic(picId, objectName, tile, absXf) {
7675
8326
  function renderZoomObject(slideItemObj, idx, x, y, cx, cy) {
7676
8327
  const zoom = slideItemObj.zoom;
7677
8328
  if (!zoom) return "";
7678
- const objectName = (slideItemObj.options || {}).objectName;
8329
+ const opts = slideItemObj.options || {};
8330
+ const objectName = opts.objectName;
7679
8331
  const { uri, prefix, zm } = ZOOM_VARIANTS[zoom.variant];
7680
8332
  const firstTile = zoom.tiles[0];
7681
8333
  if (!firstTile) return "";
@@ -7693,7 +8345,10 @@ function renderZoomObject(slideItemObj, idx, x, y, cx, cy) {
7693
8345
  const graphicFrame = el("p:graphicFrame", null, [
7694
8346
  raw(el("p:nvGraphicFramePr", null, [
7695
8347
  raw(cNvPrOpen(idx + 2, objectName, "") + "/>"),
7696
- raw(el("p:cNvGraphicFramePr", null, raw(voidEl("a:graphicFrameLocks", { noChangeAspect: "1" })))),
8348
+ raw(el("p:cNvGraphicFramePr", null, raw(genXmlObjectLock("a:graphicFrameLocks", GRAPHIC_FRAME_LOCK_ATTRS, {
8349
+ noChangeAspect: true,
8350
+ ...opts.objectLock
8351
+ }, objectName)))),
7697
8352
  raw(voidEl("p:nvPr"))
7698
8353
  ])),
7699
8354
  raw(el("p:xfrm", null, [raw(voidEl("a:off", {
@@ -8352,56 +9007,18 @@ function buildThemeClrScheme(scheme) {
8352
9007
  return raw(el(`a:${slot}`, null, child));
8353
9008
  }));
8354
9009
  }
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 = [
9010
+ /**
9011
+ * The per-script theme font fallback table (`a:fontScheme`'s CT_TextFont lists), in OOXML
9012
+ * document order. Static Office defaults — no caller data, no escaping risk — unlike the
9013
+ * `<a:latin>`/`<a:ea>`/`<a:cs>` slots above.
9014
+ *
9015
+ * Forty-one of the forty-seven scripts take the same face in the major and minor lists, so the
9016
+ * shared table below is the whole thing and the two override maps hold the six-odd rows that
9017
+ * actually differ. Written out twice, ninety-four lines said less than these do, and one row
9018
+ * mistyped in one copy produces a theme that opens fine and paints a script in the wrong face —
9019
+ * a difference nobody reviewing a diff of two 47-row tables would see.
9020
+ */
9021
+ const THEME_FONT_LIST = [
8405
9022
  ["Jpan", "游ゴシック"],
8406
9023
  ["Hang", "맑은 고딕"],
8407
9024
  ["Hans", "等线"],
@@ -8450,6 +9067,24 @@ const MINOR_FONT_LIST = [
8450
9067
  ["Talu", "Microsoft New Tai Lue"],
8451
9068
  ["Tfng", "Ebrima"]
8452
9069
  ];
9070
+ /**
9071
+ * The seven scripts whose *heading* face differs from the body one. Latin and CJK headings take
9072
+ * the Light weight; the scripts with no Light cut take a serif (or, for Khmer, a different face
9073
+ * entirely) where the body takes a sans. Every other script in {@link THEME_FONT_LIST} uses one
9074
+ * face for both.
9075
+ */
9076
+ const MAJOR_FONT_OVERRIDES = {
9077
+ Jpan: "游ゴシック Light",
9078
+ Hans: "等线 Light",
9079
+ Arab: "Times New Roman",
9080
+ Hebr: "Times New Roman",
9081
+ Thai: "Angsana New",
9082
+ Khmr: "MoolBoran",
9083
+ Viet: "Times New Roman"
9084
+ };
9085
+ /** The major (heading) and minor (body) lists, both derived from the one table. */
9086
+ const MAJOR_FONT_LIST = THEME_FONT_LIST.map(([script, face]) => [script, MAJOR_FONT_OVERRIDES[script] ?? face]);
9087
+ const MINOR_FONT_LIST = THEME_FONT_LIST;
8453
9088
  function fontListChildren(scriptFaces) {
8454
9089
  return scriptFaces.map(([script, typeface]) => raw(voidEl("a:font", {
8455
9090
  script,
@@ -9390,20 +10025,7 @@ function makeXmlMaster(slide, layouts) {
9390
10025
  id: LAYOUT_IDX_SERIES_BASE + idx,
9391
10026
  "r:id": `rId${slide._rels.length + idx + 1}`
9392
10027
  })).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
- });
10028
+ const clrMap = voidEl("p:clrMap", DEFAULT_COLOR_MAP);
9407
10029
  const hf = voidEl("p:hf", {
9408
10030
  sldNum: slide._slideNumberProps ? null : 0,
9409
10031
  hdr: 0,
@@ -9671,7 +10293,7 @@ async function writePackage(source, props) {
9671
10293
  * the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
9672
10294
  * license text is in `LICENSE` at the repository root.
9673
10295
  */
9674
- const VERSION = "3.5.0";
10296
+ const VERSION = "3.6.0";
9675
10297
  function standardLayoutToPresLayout(layout) {
9676
10298
  return {
9677
10299
  name: layout.name,
@@ -10651,6 +11273,6 @@ function textRuns(runs) {
10651
11273
  return runs;
10652
11274
  }
10653
11275
  //#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 };
11276
+ 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
11277
 
10656
- //# sourceMappingURL=text-D04NUMvA.js.map
11278
+ //# sourceMappingURL=text-CyetbF6p.js.map