@shbernal/pptxgenjs 5.3.0 → 5.4.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.
@@ -3844,6 +3844,7 @@ let MASTER_OBJECTS = /* @__PURE__ */ function(MASTER_OBJECTS) {
3844
3844
  }({});
3845
3845
  let SLIDE_OBJECT_TYPES = /* @__PURE__ */ function(SLIDE_OBJECT_TYPES) {
3846
3846
  SLIDE_OBJECT_TYPES["chart"] = "chart";
3847
+ SLIDE_OBJECT_TYPES["connector"] = "connector";
3847
3848
  SLIDE_OBJECT_TYPES["hyperlink"] = "hyperlink";
3848
3849
  SLIDE_OBJECT_TYPES["image"] = "image";
3849
3850
  SLIDE_OBJECT_TYPES["media"] = "media";
@@ -3855,6 +3856,16 @@ let SLIDE_OBJECT_TYPES = /* @__PURE__ */ function(SLIDE_OBJECT_TYPES) {
3855
3856
  SLIDE_OBJECT_TYPES["notes"] = "notes";
3856
3857
  return SLIDE_OBJECT_TYPES;
3857
3858
  }({});
3859
+ /**
3860
+ * Maps a friendly `ConnectorType` to its OOXML connector preset geometry (`prst`).
3861
+ * These are the canonical 1-segment straight, 3-segment bent (elbow), and 3-segment
3862
+ * curved connector presets PowerPoint uses by default.
3863
+ */
3864
+ const CONNECTOR_PRESETS = {
3865
+ straight: "straightConnector1",
3866
+ elbow: "bentConnector3",
3867
+ curved: "curvedConnector3"
3868
+ };
3858
3869
  let PLACEHOLDER_TYPES = /* @__PURE__ */ function(PLACEHOLDER_TYPES) {
3859
3870
  PLACEHOLDER_TYPES["title"] = "title";
3860
3871
  PLACEHOLDER_TYPES["body"] = "body";
@@ -4398,15 +4409,16 @@ function decodeBase64ToBytes(b64) {
4398
4409
  }
4399
4410
  }
4400
4411
  /**
4401
- * Read the intrinsic pixel dimensions of a raster image from its header bytes.
4412
+ * Read the intrinsic dimensions of an image from its header bytes.
4402
4413
  * - synchronous: parses only file-format headers, never decodes pixels
4403
- * - supports PNG, JPEG, GIF, BMP, and WebP (VP8 / VP8L / VP8X)
4404
- * - vector (SVG) and unrecognized formats return `null` (no intrinsic pixel size)
4414
+ * - raster: PNG, JPEG, GIF, BMP, and WebP (VP8 / VP8L / VP8X) — natural pixels
4415
+ * - vector: SVG intrinsic size from the root `<svg>` width/height or viewBox
4416
+ * - unrecognized formats return `null` (no measurable intrinsic size)
4405
4417
  *
4406
4418
  * Used by image `sizing: 'cover' | 'contain'` to compute an aspect-correct
4407
4419
  * `<a:srcRect>` crop from the *natural* image ratio rather than the displayed box.
4408
4420
  * @param {string} dataB64 - base64 image payload or `data:` URI
4409
- * @returns {{ w: number, h: number } | null} natural pixel size, or `null` when unmeasurable
4421
+ * @returns {{ w: number, h: number } | null} natural size, or `null` when unmeasurable
4410
4422
  */
4411
4423
  function getImageSizeFromBase64(dataB64) {
4412
4424
  const b = decodeBase64ToBytes(dataB64);
@@ -4492,8 +4504,46 @@ function getImageSizeFromBase64(dataB64) {
4492
4504
  }
4493
4505
  return null;
4494
4506
  }
4507
+ const text = utf8Decode(b);
4508
+ if (/<svg[\s>]/i.test(text)) return getSvgSizeFromMarkup(text);
4495
4509
  return null;
4496
4510
  }
4511
+ /**
4512
+ * Read the intrinsic size of an SVG document from its root `<svg>` element.
4513
+ * Follows the SVG sizing model: an explicit absolute `width`/`height` pair wins;
4514
+ * otherwise the `viewBox` width/height defines the size (and thus aspect ratio).
4515
+ * Percentage or missing `width`/`height` fall through to `viewBox`.
4516
+ * @param {string} svg - SVG markup
4517
+ * @returns {{ w: number, h: number } | null} intrinsic size, or `null` when undeterminable
4518
+ */
4519
+ function getSvgSizeFromMarkup(svg) {
4520
+ const openTag = /<svg\b[^>]*>/i.exec(svg)?.[0];
4521
+ if (!openTag) return null;
4522
+ const attr = (name) => new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, "i").exec(openTag)?.[1] ?? null;
4523
+ const absLength = (val) => {
4524
+ if (val == null || /%\s*$/.test(val)) return NaN;
4525
+ const m = /^\s*\+?(\d*\.?\d+)/.exec(val);
4526
+ return m ? parseFloat(m[1]) : NaN;
4527
+ };
4528
+ let w = absLength(attr("width"));
4529
+ let h = absLength(attr("height"));
4530
+ if (!(w > 0 && h > 0)) {
4531
+ const vb = attr("viewBox");
4532
+ const p = vb ? vb.trim().split(/[\s,]+/).map(Number) : [];
4533
+ if (p.length === 4 && p[2] > 0 && p[3] > 0) {
4534
+ w = p[2];
4535
+ h = p[3];
4536
+ }
4537
+ }
4538
+ return w > 0 && h > 0 ? {
4539
+ w,
4540
+ h
4541
+ } : null;
4542
+ }
4543
+ /** Decode UTF-8 bytes to a string, isomorphic across Node and browsers. */
4544
+ function utf8Decode(bytes) {
4545
+ return new TextDecoder().decode(bytes);
4546
+ }
4497
4547
  //#endregion
4498
4548
  //#region src/gen-tables.ts
4499
4549
  /**
@@ -4644,6 +4694,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
4644
4694
  let emuSlideTabH = EMU * 1;
4645
4695
  let emuTabCurrH = 0;
4646
4696
  let numCols = 0;
4697
+ let warnedNoTabH = false;
4647
4698
  const tableRowSlides = [];
4648
4699
  const tablePropX = getSmartParseNumber(tableProps.x, "X", presLayout);
4649
4700
  const tablePropY = getSmartParseNumber(tableProps.y, "Y", presLayout);
@@ -4663,6 +4714,15 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
4663
4714
  if (emuSlideTabH < tablePropH) emuSlideTabH = tablePropH;
4664
4715
  }
4665
4716
  }
4717
+ if (emuSlideTabH <= 0) {
4718
+ const emuStartY = tableRowSlides.length === 0 ? tablePropY || inch2Emu(arrInchMargins[0]) : inch2Emu(tableProps.autoPageSlideStartY || tableProps.newSlideStartY || arrInchMargins[0]);
4719
+ const fallbackH = presLayout.height - emuStartY - inch2Emu(arrInchMargins[2]);
4720
+ if (!warnedNoTabH) {
4721
+ console.warn("addTable/autoPage: the table height (`h`) leaves no room to paginate; ignoring it and using the slide height. Increase `h` or decrease `y`.");
4722
+ warnedNoTabH = true;
4723
+ }
4724
+ emuSlideTabH = fallbackH > 0 ? fallbackH : presLayout.height;
4725
+ }
4666
4726
  }
4667
4727
  if (tableProps.verbose) {
4668
4728
  console.log("[[VERBOSE MODE]]");
@@ -4835,7 +4895,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
4835
4895
  console.log("|-----------------------------------------------------------------------|\n\n");
4836
4896
  }
4837
4897
  if (currTableRow.length > 0 && currTableRow.map((cell) => Array.isArray(cell.text) ? cell.text.length : 0).reduce((p, n) => p + n) > 0) newTableRowSlide.rows.push(currTableRow);
4838
- tableRowSlides.push(newTableRowSlide);
4898
+ if (newTableRowSlide.rows.length > 0) tableRowSlides.push(newTableRowSlide);
4839
4899
  newTableRowSlide = { rows: [] };
4840
4900
  currTableRow = [];
4841
4901
  row.forEach((cell) => currTableRow.push({
@@ -4884,7 +4944,7 @@ function getSlidesForTableRows(tableRows = [], tableProps = {}, presLayout, mast
4884
4944
  for (let c = 0; c < numCols; c++) if (colSpanDepths[c] > 0) colSpanDepths[c]--;
4885
4945
  if (tableProps.verbose) console.log(`- SLIDE [${tableRowSlides.length}]: ROW [${iRow}]: ...COMPLETE ...... emuTabCurrH = ${(emuTabCurrH / EMU).toFixed(2)} ( emuSlideTabH = ${(emuSlideTabH / EMU).toFixed(2)} )`);
4886
4946
  });
4887
- tableRowSlides.push(newTableRowSlide);
4947
+ if (newTableRowSlide.rows.length > 0 || tableRowSlides.length === 0) tableRowSlides.push(newTableRowSlide);
4888
4948
  if (tableProps.verbose) {
4889
4949
  console.log("\n|================================================|");
4890
4950
  console.log(`| FINAL: tableRowSlides.length = ${tableRowSlides.length}`);
@@ -4916,6 +4976,25 @@ function htmlBorderToProps(widthStr, colorStr) {
4916
4976
  };
4917
4977
  }
4918
4978
  /**
4979
+ * Resolve a single HTML-table column width for `tableToSlides`.
4980
+ *
4981
+ * Precedence: an explicit `data-pptx-width` wins outright; otherwise the proportional width
4982
+ * derived from the live table is used, raised to `data-pptx-min-width` when that floor is larger.
4983
+ *
4984
+ * Hidden tables report `offsetWidth` 0 for every cell, which makes `calcWidth` non-finite (a 0/0
4985
+ * proportional calc). Fall back to `0` there so an explicit `data-pptx-width` / `data-pptx-min-width`
4986
+ * override still drives the column instead of emitting a `NaN` width (upstream gitbrent/PptxGenJS#1157).
4987
+ * @param {number} calcWidth - proportional width derived from `offsetWidth` (may be `NaN` for hidden tables)
4988
+ * @param {number} setWidth - `data-pptx-width` override (`0`/`NaN` when absent or invalid)
4989
+ * @param {number} minWidth - `data-pptx-min-width` floor (`0`/`NaN` when absent or invalid)
4990
+ * @returns {number} resolved column width
4991
+ */
4992
+ function resolveHtmlColWidth(calcWidth, setWidth, minWidth) {
4993
+ const safeCalc = isFinite(calcWidth) ? calcWidth : 0;
4994
+ if (isFinite(setWidth) && setWidth > 0) return setWidth;
4995
+ return isFinite(minWidth) && minWidth > safeCalc ? minWidth : safeCalc;
4996
+ }
4997
+ /**
4919
4998
  * Reproduces an HTML table as a PowerPoint table - including column widths, style, etc. - creates 1 or more slides as needed
4920
4999
  * @param {TableToSlidesHost} pptx - pptxgenjs instance
4921
5000
  * @param {string} tabEleId - HTMLElementID of the table
@@ -4979,12 +5058,10 @@ function genTableToSlides(pptx, tabEleId, options = {}, masterSlide) {
4979
5058
  });
4980
5059
  arrTabColW.forEach((colW, idxW) => {
4981
5060
  const intCalcWidth = Number((Number(emuSlideTabW) * (colW / intTabW * 100) / 100 / EMU).toFixed(2));
4982
- let intMinWidth = 0;
4983
- const colSelectorMin = document.querySelector(`#${tabEleId} thead tr:first-child th:nth-child(${idxW + 1})`);
4984
- if (colSelectorMin) intMinWidth = Number(colSelectorMin.getAttribute("data-pptx-min-width"));
4985
- const colSelectorSet = document.querySelector(`#${tabEleId} thead tr:first-child th:nth-child(${idxW + 1})`);
4986
- if (colSelectorSet) intMinWidth = Number(colSelectorSet.getAttribute("data-pptx-width"));
4987
- arrColW.push(intMinWidth > intCalcWidth ? intMinWidth : intCalcWidth);
5061
+ const headCell = document.querySelector(`#${tabEleId} thead tr:first-child th:nth-child(${idxW + 1})`);
5062
+ const intSetWidth = headCell ? Number(headCell.getAttribute("data-pptx-width")) : 0;
5063
+ const intMinWidth = headCell ? Number(headCell.getAttribute("data-pptx-min-width")) : 0;
5064
+ arrColW.push(resolveHtmlColWidth(intCalcWidth, intSetWidth, intMinWidth));
4988
5065
  });
4989
5066
  if (opts.verbose) console.log(`| arrColW ......................................... = [${arrColW.join(", ")}]`);
4990
5067
  [
@@ -5158,7 +5235,7 @@ function createSlideMaster(props, target) {
5158
5235
  else if ("line" in object) addShapeDefinition(tgt, "line", object.line);
5159
5236
  else if ("rect" in object) addShapeDefinition(tgt, "rect", object.rect);
5160
5237
  else if ("roundRect" in object) addShapeDefinition(tgt, "roundRect", object.roundRect);
5161
- else if ("text" in object) addTextDefinition(tgt, [{ text: object.text.text }], object.text.options || {}, false);
5238
+ else if ("text" in object) addTextDefinition(tgt, Array.isArray(object.text.text) ? object.text.text : [{ text: object.text.text }], object.text.options || {}, false);
5162
5239
  else if ("placeholder" in object) {
5163
5240
  const placeholder = object.placeholder;
5164
5241
  const { name, type, ...rawPlaceholderOptions } = placeholder.options;
@@ -5768,6 +5845,51 @@ function addShapeDefinition(target, shapeName, opts) {
5768
5845
  target._slideObjects.push(newObject);
5769
5846
  }
5770
5847
  /**
5848
+ * Adds a connector object to a slide definition.
5849
+ * A connector is a line between two points emitted as a PowerPoint connector (`<p:cxnSp>`).
5850
+ * Endpoints are converted to a bounding box (`x/y/w/h`) plus `flipH`/`flipV` so the box can be
5851
+ * oriented from any corner; the connector preset geometry is derived from `type`.
5852
+ * @param {PresSlideInternal} target - slide the connector is added to
5853
+ * @param {ConnectorProps} opts - connector options (endpoints + line styling)
5854
+ */
5855
+ function addConnectorDefinition(target, opts) {
5856
+ if (!opts || [
5857
+ opts.x1,
5858
+ opts.y1,
5859
+ opts.x2,
5860
+ opts.y2
5861
+ ].some((v) => typeof v === "undefined")) throw new Error("addConnector requires { x1, y1, x2, y2 }. Example: `slide.addConnector({ x1:1, y1:1, x2:4, y2:3 })`");
5862
+ const preset = CONNECTOR_PRESETS[opts.type || "straight"];
5863
+ if (!preset) throw new Error(`Invalid connector type "${String(opts.type)}". Use 'straight', 'elbow', or 'curved'.`);
5864
+ const x1 = getSmartParseNumber(opts.x1, "X", target._presLayout) / EMU;
5865
+ const y1 = getSmartParseNumber(opts.y1, "Y", target._presLayout) / EMU;
5866
+ const x2 = getSmartParseNumber(opts.x2, "X", target._presLayout) / EMU;
5867
+ const y2 = getSmartParseNumber(opts.y2, "Y", target._presLayout) / EMU;
5868
+ const newObject = {
5869
+ _type: "connector",
5870
+ shape: preset,
5871
+ options: {
5872
+ x: Math.min(x1, x2),
5873
+ y: Math.min(y1, y2),
5874
+ w: Math.abs(x2 - x1),
5875
+ h: Math.abs(y2 - y1),
5876
+ flipH: x2 < x1,
5877
+ flipV: y2 < y1,
5878
+ line: {
5879
+ type: "solid",
5880
+ color: opts.color || "333333",
5881
+ width: typeof opts.width === "number" ? opts.width : 1,
5882
+ dashType: opts.dashType || "solid",
5883
+ beginArrowType: opts.beginArrowType,
5884
+ endArrowType: opts.endArrowType
5885
+ },
5886
+ altText: opts.altText,
5887
+ objectName: opts.objectName ? encodeXmlEntities(validateObjectName(opts.objectName, "connector")) : `Connector ${target._slideObjects.filter((obj) => obj._type === "connector").length}`
5888
+ }
5889
+ };
5890
+ target._slideObjects.push(newObject);
5891
+ }
5892
+ /**
5771
5893
  * Adds a table object to a slide definition.
5772
5894
  * @param {PresSlideInternal} target - slide object that the table should be added to
5773
5895
  * @param {TableRow[]} tableRows - table data
@@ -5869,6 +5991,7 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
5869
5991
  });
5870
5992
  }
5871
5993
  opt.autoPage = typeof opt.autoPage === "boolean" ? opt.autoPage : false;
5994
+ opt.autoPagePlaceholder = typeof opt.autoPagePlaceholder === "boolean" ? opt.autoPagePlaceholder : false;
5872
5995
  opt.autoPageRepeatHeader = typeof opt.autoPageRepeatHeader === "boolean" ? opt.autoPageRepeatHeader : false;
5873
5996
  opt.autoPageHeaderRows = typeof opt.autoPageHeaderRows !== "undefined" && !isNaN(Number(opt.autoPageHeaderRows)) ? Number(opt.autoPageHeaderRows) : 1;
5874
5997
  opt.autoPageLineWeight = typeof opt.autoPageLineWeight !== "undefined" && !isNaN(Number(opt.autoPageLineWeight)) ? Number(opt.autoPageLineWeight) : 0;
@@ -5932,12 +6055,14 @@ function addTableDefinition(target, tableRows, options, slideLayout, presLayout,
5932
6055
  });
5933
6056
  } else {
5934
6057
  if (opt.autoPageRepeatHeader) opt._arrObjTabHeadRows = arrRows.filter((_row, idx) => idx < (opt.autoPageHeaderRows || 1));
6058
+ const sourcePlaceholders = opt.autoPagePlaceholder && Array.isArray(target._slideObjects) ? target._slideObjects.filter((obj) => obj._type !== "table" && obj.options?.placeholder) : [];
5935
6059
  getSlidesForTableRows(arrRows, opt, presLayout, slideLayout).forEach((slide, idx) => {
5936
6060
  if (!getSlide(target._slideNum + idx)) slides.push(addSlide({ masterName: slideLayout?._name || void 0 }));
5937
6061
  if (idx > 0) opt.y = opt.autoPageSlideStartY || opt.newSlideStartY || arrTableMargin[0];
5938
6062
  {
5939
6063
  const newSlide = getSlide(target._slideNum + idx);
5940
6064
  opt.autoPage = false;
6065
+ if (idx > 0 && sourcePlaceholders.length > 0) sourcePlaceholders.forEach((ph) => newSlide._slideObjects.push(structuredClone(ph)));
5941
6066
  createHyperlinkRels(newSlide, slide.rows);
5942
6067
  newSlide.addTable(slide.rows, { ...opt });
5943
6068
  if (idx > 0) newAutoPagedSlides.push(newSlide);
@@ -6290,6 +6415,16 @@ var Slide = class {
6290
6415
  return this;
6291
6416
  }
6292
6417
  /**
6418
+ * Add a connector (a line drawn between two points, emitted as a PowerPoint `<p:cxnSp>`).
6419
+ * @param {ConnectorProps} options - connector endpoints (`x1,y1,x2,y2`) and line styling
6420
+ * @return {Slide} this Slide
6421
+ * @example slide.addConnector({ type: 'elbow', x1: 1, y1: 1, x2: 5, y2: 3, endArrowType: 'triangle' })
6422
+ */
6423
+ addConnector(options) {
6424
+ addConnectorDefinition(this, options);
6425
+ return this;
6426
+ }
6427
+ /**
6293
6428
  * Add table to Slide
6294
6429
  * @param {TableRow[]} tableRows - table rows
6295
6430
  * @param {TableProps} options - table options
@@ -7303,6 +7438,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
7303
7438
  strXml += " <c:showPercent val=\"1\"/>";
7304
7439
  strXml += " <c:showBubbleSize val=\"0\"/>";
7305
7440
  strXml += ` <c:showLeaderLines val="${opts.showLeaderLines ? "1" : "0"}"/>`;
7441
+ strXml += createLeaderLinesElement(opts);
7306
7442
  strXml += "</c:dLbls>";
7307
7443
  strXml += "<c:cat>";
7308
7444
  strXml += " <c:strRef>";
@@ -7735,6 +7871,24 @@ function createSerLinesElement(opt) {
7735
7871
  strXml += "</a:ln></c:spPr></c:serLines>";
7736
7872
  return strXml;
7737
7873
  }
7874
+ /**
7875
+ * Build the `<c:leaderLines>` element for pie/doughnut data labels.
7876
+ *
7877
+ * Schema position: inside `<c:dLbls>`, immediately after `<c:showLeaderLines>`
7878
+ * (CT_DLbls / Group_DLbls order: showLeaderLines → leaderLines).
7879
+ *
7880
+ * Returns `''` unless the caller both enabled leader lines (`showLeaderLines`)
7881
+ * and configured their appearance (`leaderLineColor` / `leaderLineSize`). When
7882
+ * appearance is unset we leave the element off so PowerPoint applies its
7883
+ * automatic leader-line color, matching prior behavior.
7884
+ *
7885
+ * @param opts - chart options (reads `showLeaderLines`, `leaderLineColor`, `leaderLineSize`)
7886
+ */
7887
+ function createLeaderLinesElement(opts) {
7888
+ if (!opts.showLeaderLines) return "";
7889
+ if (!opts.leaderLineColor && opts.leaderLineSize == null) return "";
7890
+ return `<c:leaderLines><c:spPr><a:ln w="${valToPts(opts.leaderLineSize ?? .75)}" cap="flat"><a:solidFill>${createColorElement(opts.leaderLineColor || "808080")}</a:solidFill><a:prstDash val="solid"/><a:round/></a:ln><a:effectLst/></c:spPr></c:leaderLines>`;
7891
+ }
7738
7892
  function makeCustomDLblXml(idx, text, opts) {
7739
7893
  const sz = Math.round((opts.dataLabelFontSize || 12) * 100);
7740
7894
  const bold = opts.dataLabelFontBold ? "1" : "0";
@@ -7810,9 +7964,11 @@ function hasEncodingPath(rel) {
7810
7964
  /**
7811
7965
  * Encode Image/Audio/Video into base64
7812
7966
  * @param {PresSlideInternal | SlideLayoutInternal} layout - slide layout
7967
+ * @param {RuntimeAdapter} runtime - runtime adapter (Node/browser media loader)
7968
+ * @param {'throw' | 'placeholder'} onMediaError - failure policy: reject the export (default) or substitute a placeholder and warn
7813
7969
  * @return {Promise} promise
7814
7970
  */
7815
- function encodeSlideMediaRels(layout, runtime) {
7971
+ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
7816
7972
  const imageProms = [];
7817
7973
  const candidateRels = layout._relsMedia.filter((rel) => rel.type !== "online" && !rel.data && hasEncodingPath(rel));
7818
7974
  const unqPaths = [];
@@ -7830,9 +7986,13 @@ function encodeSlideMediaRels(layout, runtime) {
7830
7986
  if (rel.isSvgPng) await runtime.createSvgPngPreview(rel);
7831
7987
  return "done";
7832
7988
  } catch (ex) {
7833
- rel.data = IMG_BROKEN;
7834
- candidateRels.filter((dupe) => dupe.isDuplicate && dupe.path === rel.path).forEach((dupe) => dupe.data = rel.data);
7835
- throw ex;
7989
+ if (onMediaError === "placeholder") {
7990
+ console.warn(`[WARNING] Failed to load media "${rel.path}"; embedding a broken-image placeholder. (${String(ex)})`);
7991
+ rel.data = IMG_BROKEN;
7992
+ candidateRels.filter((dupe) => dupe.isDuplicate && dupe.path === rel.path).forEach((dupe) => dupe.data = rel.data);
7993
+ return "done";
7994
+ }
7995
+ throw new Error(`Failed to load media "${rel.path}" during export.`, { cause: ex });
7836
7996
  }
7837
7997
  })());
7838
7998
  });
@@ -8367,6 +8527,24 @@ function slideObjectToXml(slide) {
8367
8527
  strSlideXml += genXmlTextBody(slideItemObj);
8368
8528
  strSlideXml += "</p:sp>";
8369
8529
  break;
8530
+ case "connector":
8531
+ strSlideXml += "<p:cxnSp><p:nvCxnSpPr>";
8532
+ strSlideXml += `<p:cNvPr id="${idx + 2}" name="${slideItemObj.options.objectName}" descr="${encodeXmlEntities(slideItemObj.options.altText || "")}"/>`;
8533
+ strSlideXml += "<p:cNvCxnSpPr/><p:nvPr/></p:nvCxnSpPr><p:spPr>";
8534
+ strSlideXml += `<a:xfrm${locationAttr}><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
8535
+ strSlideXml += `<a:prstGeom prst="${slideItemObj.shape}"><a:avLst/></a:prstGeom>`;
8536
+ {
8537
+ const ln = slideItemObj.options.line || {};
8538
+ const lnAttrs = (ln.width ? ` w="${lineWidthToEmu(ln.width)}"` : "") + (ln.cap ? ` cap="${createLineCap(ln.cap)}"` : "");
8539
+ strSlideXml += `<a:ln${lnAttrs}>`;
8540
+ if (ln.color) strSlideXml += genXmlColorSelection(ln);
8541
+ if (ln.dashType) strSlideXml += `<a:prstDash val="${ln.dashType}"/>`;
8542
+ if (ln.beginArrowType) strSlideXml += `<a:headEnd type="${ln.beginArrowType}"/>`;
8543
+ if (ln.endArrowType) strSlideXml += `<a:tailEnd type="${ln.endArrowType}"/>`;
8544
+ strSlideXml += "</a:ln>";
8545
+ }
8546
+ strSlideXml += "</p:spPr></p:cxnSp>";
8547
+ break;
8370
8548
  case "image":
8371
8549
  strSlideXml += "<p:pic>";
8372
8550
  strSlideXml += " <p:nvPicPr>";
@@ -8410,7 +8588,7 @@ function slideObjectToXml(slide) {
8410
8588
  const relData = (slide._relsMedia || []).find((rel) => rel.rId === slideItemObj.imageRid)?.data;
8411
8589
  const natural = typeof relData === "string" ? getImageSizeFromBase64(relData) : null;
8412
8590
  if (natural) cropSize = natural;
8413
- else console.warn(`Warning: sizing '${sizing.type}' could not measure natural dimensions for image "${slideItemObj.options.objectName}"; falling back to displayed aspect ratio (crop may be inexact). Provide a raster image (PNG/JPEG/GIF/BMP/WebP) to enable an aspect-correct crop.`);
8591
+ else console.warn(`Warning: sizing '${sizing.type}' could not measure natural dimensions for image "${slideItemObj.options.objectName}"; falling back to displayed aspect ratio (crop may be inexact). Provide a raster image (PNG/JPEG/GIF/BMP/WebP) or an SVG with width/height or a viewBox to enable an aspect-correct crop.`);
8414
8592
  }
8415
8593
  strSlideXml += ImageSizingXml[sizing.type](cropSize, {
8416
8594
  w: boxW,
@@ -8429,6 +8607,16 @@ function slideObjectToXml(slide) {
8429
8607
  strSlideXml += " </a:xfrm>";
8430
8608
  if (slideItemObj.options.points) strSlideXml += " " + genXmlCustGeom(slideItemObj.options.points, imgWidth, imgHeight, slide._presLayout);
8431
8609
  else strSlideXml += " " + genXmlPresetGeom(slideItemObj.options.shape ?? (rounding ? "ellipse" : "rect"), slideItemObj.options, imgWidth, imgHeight);
8610
+ if (slideItemObj.options.line) {
8611
+ const imgLine = slideItemObj.options.line;
8612
+ const lnAttrs = (imgLine.width ? ` w="${lineWidthToEmu(imgLine.width)}"` : "") + (imgLine.cap ? ` cap="${createLineCap(imgLine.cap)}"` : "");
8613
+ strSlideXml += `<a:ln${lnAttrs}>`;
8614
+ if (imgLine.color) strSlideXml += genXmlColorSelection(imgLine);
8615
+ if (imgLine.dashType) strSlideXml += `<a:prstDash val="${imgLine.dashType}"/>`;
8616
+ if (imgLine.beginArrowType) strSlideXml += `<a:headEnd type="${imgLine.beginArrowType}"/>`;
8617
+ if (imgLine.endArrowType) strSlideXml += `<a:tailEnd type="${imgLine.endArrowType}"/>`;
8618
+ strSlideXml += "</a:ln>";
8619
+ }
8432
8620
  if (slideItemObj.options.shadow && slideItemObj.options.shadow.type !== "none") {
8433
8621
  const sh = slideItemObj.options.shadow;
8434
8622
  const shadowType = sh.type || "outer";
@@ -8631,9 +8819,17 @@ function genXmlParagraphProperties(textObj, isDefault) {
8631
8819
  if (typeof textObj.options.bullet === "object") {
8632
8820
  if (textObj?.options?.bullet?.indent) bulletMarL = valToPts(textObj.options.bullet.indent);
8633
8821
  if (textObj.options.bullet.color) strXmlBulletColor = `<a:buClr>${createColorElement(textObj.options.bullet.color)}</a:buClr>`;
8822
+ let bulletSizePct = 1e5;
8823
+ if (textObj.options.bullet.size !== void 0) {
8824
+ const bulletSize = Number(textObj.options.bullet.size);
8825
+ if (isNaN(bulletSize) || bulletSize < 25 || bulletSize > 400) console.warn("Warning: `bullet.size` must be a percentage between 25 and 400!");
8826
+ else bulletSizePct = Math.round(bulletSize * 1e3);
8827
+ }
8828
+ const strXmlBulletSize = `<a:buSzPct val="${bulletSizePct}"/>`;
8829
+ const strXmlBulletFont = textObj.options.bullet.fontFace ? `<a:buFont typeface="${encodeXmlEntities(textObj.options.bullet.fontFace)}"/>` : "";
8634
8830
  if (textObj.options.bullet.type && textObj.options.bullet.type.toString().toLowerCase() === "number") {
8635
8831
  paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
8636
- strXmlBullet = `<a:buSzPct val="100000"/><a:buFont typeface="+mj-lt"/><a:buAutoNum type="${textObj.options.bullet.style || "arabicPeriod"}" startAt="${textObj.options.bullet.numberStartAt || textObj.options.bullet.startAt || "1"}"/>`;
8832
+ strXmlBullet = `${strXmlBulletSize}${strXmlBulletFont || "<a:buFont typeface=\"+mj-lt\"/>"}<a:buAutoNum type="${textObj.options.bullet.style || "arabicPeriod"}" startAt="${textObj.options.bullet.numberStartAt || textObj.options.bullet.startAt || "1"}"/>`;
8637
8833
  } else if (textObj.options.bullet.characterCode) {
8638
8834
  let bulletCode = `&#x${textObj.options.bullet.characterCode};`;
8639
8835
  if (!/^[0-9A-Fa-f]{4}$/.test(textObj.options.bullet.characterCode)) {
@@ -8641,7 +8837,7 @@ function genXmlParagraphProperties(textObj, isDefault) {
8641
8837
  bulletCode = "&#x2022;";
8642
8838
  }
8643
8839
  paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
8644
- strXmlBullet = "<a:buSzPct val=\"100000\"/><a:buChar char=\"" + bulletCode + "\"/>";
8840
+ strXmlBullet = strXmlBulletSize + strXmlBulletFont + "<a:buChar char=\"" + bulletCode + "\"/>";
8645
8841
  } else if (textObj.options.bullet.code) {
8646
8842
  let bulletCode = `&#x${textObj.options.bullet.code};`;
8647
8843
  if (!/^[0-9A-Fa-f]{4}$/.test(textObj.options.bullet.code)) {
@@ -8649,10 +8845,10 @@ function genXmlParagraphProperties(textObj, isDefault) {
8649
8845
  bulletCode = "&#x2022;";
8650
8846
  }
8651
8847
  paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
8652
- strXmlBullet = "<a:buSzPct val=\"100000\"/><a:buChar char=\"" + bulletCode + "\"/>";
8848
+ strXmlBullet = strXmlBulletSize + strXmlBulletFont + "<a:buChar char=\"" + bulletCode + "\"/>";
8653
8849
  } else {
8654
8850
  paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
8655
- strXmlBullet = `<a:buSzPct val="100000"/><a:buChar char="&#x2022;"/>`;
8851
+ strXmlBullet = `${strXmlBulletSize}${strXmlBulletFont}<a:buChar char="&#x2022;"/>`;
8656
8852
  }
8657
8853
  } else if (textObj.options.bullet) {
8658
8854
  paragraphPropXml += ` marL="${textObj.options.indentLevel && textObj.options.indentLevel > 0 ? bulletMarL + bulletMarL * textObj.options.indentLevel : bulletMarL}" indent="-${bulletMarL}"`;
@@ -9267,11 +9463,51 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
9267
9463
  return 1;
9268
9464
  }
9269
9465
  /**
9466
+ * Theme `<a:clrScheme>` slots in OOXML document order, with their default Office color child.
9467
+ * `dk1`/`lt1` default to `sysClr` (windowText/window); the rest are `srgbClr`. A user override
9468
+ * for any slot is emitted as `<a:srgbClr>` (see `buildThemeClrScheme`).
9469
+ */
9470
+ const THEME_CLR_SCHEME_DEFAULTS = [
9471
+ ["dk1", "<a:sysClr val=\"windowText\" lastClr=\"000000\"/>"],
9472
+ ["lt1", "<a:sysClr val=\"window\" lastClr=\"FFFFFF\"/>"],
9473
+ ["dk2", "<a:srgbClr val=\"44546A\"/>"],
9474
+ ["lt2", "<a:srgbClr val=\"E7E6E6\"/>"],
9475
+ ["accent1", "<a:srgbClr val=\"4472C4\"/>"],
9476
+ ["accent2", "<a:srgbClr val=\"ED7D31\"/>"],
9477
+ ["accent3", "<a:srgbClr val=\"A5A5A5\"/>"],
9478
+ ["accent4", "<a:srgbClr val=\"FFC000\"/>"],
9479
+ ["accent5", "<a:srgbClr val=\"5B9BD5\"/>"],
9480
+ ["accent6", "<a:srgbClr val=\"70AD47\"/>"],
9481
+ ["hlink", "<a:srgbClr val=\"0563C1\"/>"],
9482
+ ["folHlink", "<a:srgbClr val=\"954F72\"/>"]
9483
+ ];
9484
+ /**
9485
+ * Build the theme `<a:clrScheme>` block, applying any caller-supplied color overrides over the
9486
+ * default Office scheme. Invalid (non 6-digit-hex) overrides warn and keep the default rather
9487
+ * than emitting a degenerate color.
9488
+ * @param {ThemeColorScheme} [scheme] - per-slot hex overrides
9489
+ * @return {string} the `<a:clrScheme>...</a:clrScheme>` XML
9490
+ */
9491
+ function buildThemeClrScheme(scheme) {
9492
+ return `<a:clrScheme name="Office">${THEME_CLR_SCHEME_DEFAULTS.map(([slot, defaultChild]) => {
9493
+ const override = scheme?.[slot];
9494
+ let child = defaultChild;
9495
+ if (typeof override === "string" && override.length > 0) {
9496
+ const hex = override.replace("#", "");
9497
+ if (REGEX_HEX_COLOR.test(hex)) child = `<a:srgbClr val="${hex.toUpperCase()}"/>`;
9498
+ else console.warn(`makeXmlTheme: colorScheme.${slot} "${override}" is not a 6-digit hex color; keeping the Office default.`);
9499
+ }
9500
+ return `<a:${slot}>${child}</a:${slot}>`;
9501
+ }).join("")}</a:clrScheme>`;
9502
+ }
9503
+ /**
9270
9504
  * Creates `ppt/theme/theme1.xml`
9271
9505
  * @return {string} XML
9272
9506
  */
9273
9507
  function makeXmlTheme(pres) {
9274
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><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>${pres.theme?.headFontFace ? `<a:latin typeface="${pres.theme?.headFontFace}"/>` : "<a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/>"}<a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック Light"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线 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="Angsana New"/><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:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></a:majorFont><a:minorFont>${pres.theme?.bodyFontFace ? `<a:latin typeface="${pres.theme?.bodyFontFace}"/>` : "<a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/>"}<a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Cordia New"/><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:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></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>`;
9508
+ const majorFont = pres.theme?.headFontFace ? `<a:latin typeface="${pres.theme?.headFontFace}"/>` : "<a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/>";
9509
+ const minorFont = pres.theme?.bodyFontFace ? `<a:latin typeface="${pres.theme?.bodyFontFace}"/>` : "<a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/>";
9510
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements>${buildThemeClrScheme(pres.theme?.colorScheme)}<a:fontScheme name="Office"><a:majorFont>${majorFont}<a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック Light"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线 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="Angsana New"/><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:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></a:majorFont><a:minorFont>${minorFont}<a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Cordia New"/><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:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></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>`;
9275
9511
  }
9276
9512
  /**
9277
9513
  * Create presentation file (`ppt/presentation.xml`)
@@ -9480,7 +9716,7 @@ function makeXmlViewProps() {
9480
9716
  * @see https://docs.microsoft.com/en-us/office/open-xml/structure-of-a-presentationml-document
9481
9717
  * @see https://docs.microsoft.com/en-us/previous-versions/office/developer/office-2010/hh273476(v=office.14)
9482
9718
  */
9483
- const VERSION = "5.3.0";
9719
+ const VERSION = "5.4.0";
9484
9720
  function standardLayoutToPresLayout(layout) {
9485
9721
  return {
9486
9722
  name: layout.name,
@@ -9732,6 +9968,7 @@ var PptxGenJS$1 = class {
9732
9968
  this._tableStyles = [];
9733
9969
  this._masterSlide = {
9734
9970
  addChart: null,
9971
+ addConnector: null,
9735
9972
  addImage: null,
9736
9973
  addMedia: null,
9737
9974
  addNotes: null,
@@ -9804,13 +10041,14 @@ var PptxGenJS$1 = class {
9804
10041
  const arrChartPromises = [];
9805
10042
  let arrMediaPromises = [];
9806
10043
  const zip = new import_jszip_min.default();
10044
+ const onMediaError = props.onMediaError ?? "throw";
9807
10045
  this._slides.forEach((slide) => {
9808
- arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(slide, this._runtime));
10046
+ arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(slide, this._runtime, onMediaError));
9809
10047
  });
9810
10048
  this._slideLayouts.forEach((layout) => {
9811
- arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(layout, this._runtime));
10049
+ arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(layout, this._runtime, onMediaError));
9812
10050
  });
9813
- arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(this._masterSlide, this._runtime));
10051
+ arrMediaPromises = arrMediaPromises.concat(encodeSlideMediaRels(this._masterSlide, this._runtime, onMediaError));
9814
10052
  return await Promise.all(arrMediaPromises).then(async () => {
9815
10053
  const canonicalMediaTargets = /* @__PURE__ */ new Map();
9816
10054
  for (const target of [
@@ -9876,14 +10114,18 @@ var PptxGenJS$1 = class {
9876
10114
  });
9877
10115
  this.createChartMediaRels(this._masterSlide, zip, arrChartPromises);
9878
10116
  return await Promise.all(arrChartPromises).then(async () => {
10117
+ const compression = props.compression === false ? "STORE" : "DEFLATE";
9879
10118
  if (props.outputType === "STREAM") return await zip.generateAsync({
9880
10119
  type: "nodebuffer",
9881
- compression: props.compression ? "DEFLATE" : "STORE"
10120
+ compression
10121
+ });
10122
+ else if (props.outputType) return await zip.generateAsync({
10123
+ type: props.outputType,
10124
+ compression
9882
10125
  });
9883
- else if (props.outputType) return await zip.generateAsync({ type: props.outputType });
9884
10126
  else return await zip.generateAsync({
9885
10127
  type: "blob",
9886
- compression: props.compression ? "DEFLATE" : "STORE"
10128
+ compression
9887
10129
  });
9888
10130
  });
9889
10131
  });
@@ -9906,10 +10148,12 @@ var PptxGenJS$1 = class {
9906
10148
  */
9907
10149
  async write(props) {
9908
10150
  const propsOutpType = typeof props === "object" && props?.outputType ? props.outputType : props ? props : null;
9909
- const propsCompress = typeof props === "object" && props?.compression ? props.compression : false;
10151
+ const propsCompress = typeof props === "object" ? props?.compression : void 0;
10152
+ const propsMediaError = typeof props === "object" ? props?.onMediaError : void 0;
9910
10153
  return await this.exportPresentation({
9911
10154
  compression: propsCompress,
9912
- outputType: propsOutpType
10155
+ outputType: propsOutpType,
10156
+ onMediaError: propsMediaError
9913
10157
  });
9914
10158
  }
9915
10159
  /**
@@ -9923,11 +10167,12 @@ var PptxGenJS$1 = class {
9923
10167
  console.warn("[WARNING] writeFile(string) is deprecated - pass { fileName } instead.");
9924
10168
  props = { fileName: props };
9925
10169
  }
9926
- const { fileName: rawName = "Presentation.pptx", compression = false } = props;
10170
+ const { fileName: rawName = "Presentation.pptx", compression, onMediaError } = props;
9927
10171
  const fileName = rawName.toLowerCase().endsWith(".pptx") ? rawName : `${rawName}.pptx`;
9928
10172
  const data = await this.exportPresentation({
9929
10173
  compression,
9930
- outputType: this._runtime.writeFileOutputType
10174
+ outputType: this._runtime.writeFileOutputType,
10175
+ onMediaError
9931
10176
  });
9932
10177
  return await this._runtime.writeFile(fileName, data);
9933
10178
  }
@@ -10175,6 +10420,6 @@ var PptxGenJS = class extends PptxGenJS$1 {
10175
10420
  }
10176
10421
  };
10177
10422
  //#endregion
10178
- export { AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_SERIES_PRIMARY, AXIS_ID_VALUE_PRIMARY, AXIS_ID_VALUE_SECONDARY, AlignH, AlignV, BARCHART_COLORS, BULLET_TYPES, CHART_TYPE, CRLF, ChartType, DEF_BULLET_MARGIN, DEF_CELL_BORDER, DEF_CELL_MARGIN_IN, DEF_CELL_MARGIN_PT, DEF_CHART_BORDER, DEF_CHART_GRIDLINE, DEF_FONT_COLOR, DEF_FONT_SIZE, DEF_FONT_TITLE_SIZE, DEF_PRES_LAYOUT, DEF_PRES_LAYOUT_NAME, DEF_SHAPE_LINE_COLOR, DEF_SHAPE_SHADOW, DEF_SLIDE_BKGD, DEF_SLIDE_MARGIN_IN, DEF_TEXT_GLOW, DEF_TEXT_SHADOW, EMU, EMU_PER_INCH, EMU_PER_POINT, IMG_BROKEN, IMG_PLAYBTN, IMG_SVG_PLACEHOLDER, LAYOUT_IDX_SERIES_BASE, LETTERS, LINEH_MODIFIER, MASTER_OBJECTS, ONEPT, OutputType, PIECHART_COLORS, PLACEHOLDER_TYPES, POINTS_PER_INCH, PptxGenJS, PptxGenJS as Presentation, PptxGenJS as default, REGEX_HEX_COLOR, SCHEME_COLOR_NAMES, SHAPE_TYPE, SLDNUMFLDID, SLIDE_OBJECT_TYPES, STANDARD_LAYOUTS, SchemeColor, ShapeType, TABLE_STYLE, TEXT_HALIGN, TEXT_VALIGN, VALID_SHAPE_PRESETS, coordToEmu, emuToInches, emuToPixels, emuToPoints, inchesToEmu, percentToEmu, pixelsToEmu, pointsToEmu, textRun, textRuns };
10423
+ export { AXIS_ID_CATEGORY_PRIMARY, AXIS_ID_CATEGORY_SECONDARY, AXIS_ID_SERIES_PRIMARY, AXIS_ID_VALUE_PRIMARY, AXIS_ID_VALUE_SECONDARY, AlignH, AlignV, BARCHART_COLORS, BULLET_TYPES, CHART_TYPE, CONNECTOR_PRESETS, CRLF, ChartType, DEF_BULLET_MARGIN, DEF_CELL_BORDER, DEF_CELL_MARGIN_IN, DEF_CELL_MARGIN_PT, DEF_CHART_BORDER, DEF_CHART_GRIDLINE, DEF_FONT_COLOR, DEF_FONT_SIZE, DEF_FONT_TITLE_SIZE, DEF_PRES_LAYOUT, DEF_PRES_LAYOUT_NAME, DEF_SHAPE_LINE_COLOR, DEF_SHAPE_SHADOW, DEF_SLIDE_BKGD, DEF_SLIDE_MARGIN_IN, DEF_TEXT_GLOW, DEF_TEXT_SHADOW, EMU, EMU_PER_INCH, EMU_PER_POINT, IMG_BROKEN, IMG_PLAYBTN, IMG_SVG_PLACEHOLDER, LAYOUT_IDX_SERIES_BASE, LETTERS, LINEH_MODIFIER, MASTER_OBJECTS, ONEPT, OutputType, PIECHART_COLORS, PLACEHOLDER_TYPES, POINTS_PER_INCH, PptxGenJS, PptxGenJS as Presentation, PptxGenJS as default, REGEX_HEX_COLOR, SCHEME_COLOR_NAMES, SHAPE_TYPE, SLDNUMFLDID, SLIDE_OBJECT_TYPES, STANDARD_LAYOUTS, SchemeColor, ShapeType, TABLE_STYLE, TEXT_HALIGN, TEXT_VALIGN, VALID_SHAPE_PRESETS, coordToEmu, emuToInches, emuToPixels, emuToPoints, inchesToEmu, percentToEmu, pixelsToEmu, pointsToEmu, textRun, textRuns };
10179
10424
 
10180
10425
  //# sourceMappingURL=standalone.js.map