pptx-react-viewer 1.1.2 → 1.1.4

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 (25) hide show
  1. package/dist/index.js +587 -288
  2. package/dist/index.mjs +587 -288
  3. package/dist/pptx-viewer.css +1 -1
  4. package/dist/viewer/index.js +587 -288
  5. package/dist/viewer/index.mjs +587 -288
  6. package/node_modules/emf-converter/package.json +2 -2
  7. package/node_modules/mtx-decompressor/package.json +2 -2
  8. package/node_modules/pptx-viewer-core/dist/{SvgExporter--H1PDfAY.d.ts → SvgExporter-0TxiiorD.d.ts} +1 -1
  9. package/node_modules/pptx-viewer-core/dist/{SvgExporter-Dq_2eV_r.d.mts → SvgExporter-BQ4KbRO9.d.mts} +1 -1
  10. package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +2 -2
  11. package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +2 -2
  12. package/node_modules/pptx-viewer-core/dist/cli/index.js +2875 -321
  13. package/node_modules/pptx-viewer-core/dist/cli/index.mjs +2875 -321
  14. package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +3 -3
  15. package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +3 -3
  16. package/node_modules/pptx-viewer-core/dist/index.d.mts +713 -44
  17. package/node_modules/pptx-viewer-core/dist/index.d.ts +713 -44
  18. package/node_modules/pptx-viewer-core/dist/index.js +2958 -390
  19. package/node_modules/pptx-viewer-core/dist/index.mjs +2949 -390
  20. package/node_modules/pptx-viewer-core/dist/{presentation-BozkirFp.d.mts → presentation-ArhfImJ5.d.mts} +225 -8
  21. package/node_modules/pptx-viewer-core/dist/{presentation-BozkirFp.d.ts → presentation-ArhfImJ5.d.ts} +225 -8
  22. package/node_modules/pptx-viewer-core/dist/{text-operations-B2JbPA5H.d.mts → text-operations-CLj-sJyk.d.mts} +1 -1
  23. package/node_modules/pptx-viewer-core/dist/{text-operations-zwF6i4eH.d.ts → text-operations-rhJV-A_W.d.ts} +1 -1
  24. package/node_modules/pptx-viewer-core/package.json +5 -5
  25. package/package.json +20 -20
@@ -4400,12 +4400,25 @@ function svgToCustomGeometryPaths(pathData, width, height) {
4400
4400
  function pointToXml(pt) {
4401
4401
  return { "@_x": String(Math.round(pt.x)), "@_y": String(Math.round(pt.y)) };
4402
4402
  }
4403
- function customGeometryPathsToXml(paths) {
4403
+ function customGeometryPathsToXml(paths, rawData) {
4404
4404
  const xmlPaths = paths.map((path2) => {
4405
4405
  const pathXml = {
4406
4406
  "@_w": String(Math.round(path2.width)),
4407
4407
  "@_h": String(Math.round(path2.height))
4408
4408
  };
4409
+ if (path2.fillMode) {
4410
+ pathXml["@_fill"] = path2.fillMode;
4411
+ }
4412
+ if (path2.stroke === false) {
4413
+ pathXml["@_stroke"] = "0";
4414
+ } else if (path2.stroke === true) {
4415
+ pathXml["@_stroke"] = "1";
4416
+ }
4417
+ if (path2.extrusionOk === true) {
4418
+ pathXml["@_extrusionOk"] = "1";
4419
+ } else if (path2.extrusionOk === false) {
4420
+ pathXml["@_extrusionOk"] = "0";
4421
+ }
4409
4422
  const moveToList = [];
4410
4423
  const lnToList = [];
4411
4424
  const cubicBezToList = [];
@@ -4463,12 +4476,12 @@ function customGeometryPathsToXml(paths) {
4463
4476
  }
4464
4477
  return pathXml;
4465
4478
  });
4466
- return {
4479
+ const result = {
4467
4480
  "a:avLst": {},
4468
- "a:gdLst": {},
4469
- "a:ahLst": {},
4470
- "a:cxnLst": {},
4471
- "a:rect": {
4481
+ "a:gdLst": rawData?.gdLstXml ?? {},
4482
+ "a:ahLst": rawData?.ahLstXml ?? {},
4483
+ "a:cxnLst": rawData?.cxnLstXml ?? {},
4484
+ "a:rect": rawData?.rectXml ?? {
4472
4485
  "@_l": "l",
4473
4486
  "@_t": "t",
4474
4487
  "@_r": "r",
@@ -4478,6 +4491,7 @@ function customGeometryPathsToXml(paths) {
4478
4491
  "a:path": xmlPaths.length === 1 ? xmlPaths[0] : xmlPaths
4479
4492
  }
4480
4493
  };
4494
+ return result;
4481
4495
  }
4482
4496
 
4483
4497
  // src/core/builders/sdk/ElementFactory.ts
@@ -5162,7 +5176,11 @@ function isoNow() {
5162
5176
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z");
5163
5177
  }
5164
5178
  var STANDARD_LAYOUTS = [
5165
- { name: "Title Slide", type: "ctrTitle" },
5179
+ // `type` is ST_SlideLayoutType per ECMA-376 §19.7.15. The Title Slide
5180
+ // layout uses `title`; `ctrTitle` is a placeholder type (ST_PlaceholderType)
5181
+ // and is not valid here — PowerPoint's OPC loader rejects the package
5182
+ // with ERROR_FILE_CORRUPT (0x80070570) when it sees it.
5183
+ { name: "Title Slide", type: "title" },
5166
5184
  { name: "Title and Content", type: "obj" },
5167
5185
  { name: "Section Header", type: "secHead" },
5168
5186
  { name: "Two Content", type: "twoObj" },
@@ -5196,7 +5214,7 @@ ${slideOverrides}
5196
5214
  <Override PartName="/ppt/viewProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml"/>
5197
5215
  <Override PartName="/ppt/tableStyles.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"/>
5198
5216
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
5199
- <Override PartName="/docProps/app.xml" ContentType="application/vnd.ms-officedocument.extended-properties+xml"/>
5217
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
5200
5218
  </Types>`;
5201
5219
  }
5202
5220
  function rootRelsXml() {
@@ -5220,6 +5238,9 @@ ${Array.from(
5220
5238
  <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
5221
5239
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
5222
5240
  xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
5241
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5242
+ xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main"
5243
+ xmlns:p15="http://schemas.microsoft.com/office/powerpoint/2012/main"
5223
5244
  saveSubsetFonts="1">
5224
5245
  <p:sldMasterIdLst>
5225
5246
  <p:sldMasterId id="2147483648" r:id="rId1"/>
@@ -6522,10 +6543,41 @@ var PptxPresentationSlidesReconciler = class {
6522
6543
  "@_r:id": slide.rId
6523
6544
  };
6524
6545
  });
6525
- presentation["p:sldIdLst"] = slideIdList;
6526
- input.presentationData["p:presentation"] = presentation;
6546
+ input.presentationData["p:presentation"] = this.insertSlideIdListInOrder(
6547
+ presentation,
6548
+ slideIdList
6549
+ );
6527
6550
  }
6528
6551
  }
6552
+ /**
6553
+ * Return a new presentation XML object whose `p:sldIdLst` child sits in the
6554
+ * schema-mandated position (after `sldMasterIdLst` / `notesMasterIdLst` /
6555
+ * `handoutMasterIdLst`, before `sldSz`). Non-sldIdLst keys keep their
6556
+ * relative order; attribute keys (`@_...`) stay at the front.
6557
+ */
6558
+ insertSlideIdListInOrder(presentation, slideIdList) {
6559
+ const before = /* @__PURE__ */ new Set([
6560
+ "p:sldMasterIdLst",
6561
+ "p:notesMasterIdLst",
6562
+ "p:handoutMasterIdLst"
6563
+ ]);
6564
+ const rebuilt = {};
6565
+ let inserted = false;
6566
+ for (const key of Object.keys(presentation)) {
6567
+ if (key === "p:sldIdLst") {
6568
+ continue;
6569
+ }
6570
+ if (!inserted && !key.startsWith("@_") && !before.has(key)) {
6571
+ rebuilt["p:sldIdLst"] = slideIdList;
6572
+ inserted = true;
6573
+ }
6574
+ rebuilt[key] = presentation[key];
6575
+ }
6576
+ if (!inserted) {
6577
+ rebuilt["p:sldIdLst"] = slideIdList;
6578
+ }
6579
+ return rebuilt;
6580
+ }
6529
6581
  async attachNewSlide(init) {
6530
6582
  const sourceSlidePath = init.input.findSourceSlidePath(init.slide.sourceSlideId);
6531
6583
  const sourceSlideXml = sourceSlidePath ? init.input.deepCloneXml(init.input.slideMap.get(sourceSlidePath)) : void 0;
@@ -6610,6 +6662,22 @@ var PptxPresentationSlidesReconciler = class {
6610
6662
  };
6611
6663
 
6612
6664
  // src/core/core/builders/PptxSlideRelationshipRegistry.ts
6665
+ function isExternalTarget(target) {
6666
+ const normalized = target.trim();
6667
+ if (normalized.length === 0) {
6668
+ return false;
6669
+ }
6670
+ const colonIdx = normalized.indexOf(":");
6671
+ if (colonIdx <= 0) {
6672
+ return false;
6673
+ }
6674
+ const slashIdx = normalized.indexOf("/");
6675
+ if (slashIdx !== -1 && slashIdx < colonIdx) {
6676
+ return false;
6677
+ }
6678
+ const scheme = normalized.slice(0, colonIdx);
6679
+ return /^[A-Za-z][A-Za-z0-9+\-.]*$/.test(scheme);
6680
+ }
6613
6681
  var PptxSlideRelationshipRegistry = class {
6614
6682
  relationships;
6615
6683
  usedRelationshipIds = /* @__PURE__ */ new Set();
@@ -6680,7 +6748,7 @@ var PptxSlideRelationshipRegistry = class {
6680
6748
  return existingRelationshipId;
6681
6749
  }
6682
6750
  const relationshipId = this.nextRelationshipId();
6683
- const targetMode = /^(https?:|mailto:|ftp:|file:)/i.test(normalizedTarget) ? "External" : void 0;
6751
+ const targetMode = isExternalTarget(normalizedTarget) ? "External" : void 0;
6684
6752
  this.upsertRelationship(
6685
6753
  relationshipId,
6686
6754
  this.hyperlinkRelationshipType,
@@ -6930,6 +6998,82 @@ var PptxSlideNotesPartUpdater = class {
6930
6998
  }
6931
6999
  };
6932
7000
 
7001
+ // src/core/utils/xml-reorder.ts
7002
+ function reorderObjectKeys(obj, schemaOrder) {
7003
+ const result = {};
7004
+ const consumed = /* @__PURE__ */ new Set();
7005
+ for (const key of schemaOrder) {
7006
+ if (Object.hasOwn(obj, key)) {
7007
+ const value = obj[key];
7008
+ if (value !== void 0) {
7009
+ result[key] = value;
7010
+ }
7011
+ consumed.add(key);
7012
+ }
7013
+ }
7014
+ for (const key of Object.keys(obj)) {
7015
+ if (consumed.has(key)) {
7016
+ continue;
7017
+ }
7018
+ const value = obj[key];
7019
+ if (value !== void 0) {
7020
+ result[key] = value;
7021
+ }
7022
+ }
7023
+ return result;
7024
+ }
7025
+ var EFFECT_LST_ORDER = [
7026
+ "a:blur",
7027
+ "a:fillOverlay",
7028
+ "a:glow",
7029
+ "a:innerShdw",
7030
+ "a:outerShdw",
7031
+ "a:prstShdw",
7032
+ "a:reflection",
7033
+ "a:softEdge"
7034
+ ];
7035
+ var SP_PR_ORDER = [
7036
+ "a:xfrm",
7037
+ "a:custGeom",
7038
+ "a:prstGeom",
7039
+ "a:noFill",
7040
+ "a:solidFill",
7041
+ "a:gradFill",
7042
+ "a:blipFill",
7043
+ "a:pattFill",
7044
+ "a:grpFill",
7045
+ "a:ln",
7046
+ "a:effectLst",
7047
+ "a:effectDag",
7048
+ "a:scene3d",
7049
+ "a:sp3d",
7050
+ "a:extLst"
7051
+ ];
7052
+ var TC_PR_BORDERS_ORDER = [
7053
+ "a:lnL",
7054
+ "a:lnR",
7055
+ "a:lnT",
7056
+ "a:lnB",
7057
+ "a:lnTlToBr",
7058
+ "a:lnBlToTr",
7059
+ "a:cell3D",
7060
+ "a:noFill",
7061
+ "a:solidFill",
7062
+ "a:gradFill",
7063
+ "a:blipFill",
7064
+ "a:pattFill",
7065
+ "a:grpFill",
7066
+ "a:headers",
7067
+ "a:extLst"
7068
+ ];
7069
+ var BLIP_FILL_ORDER = ["a:blip", "a:srcRect", "a:tile", "a:stretch"];
7070
+ var SHAPE_STYLE_ORDER = [
7071
+ "a:lnRef",
7072
+ "a:fillRef",
7073
+ "a:effectRef",
7074
+ "a:fontRef"
7075
+ ];
7076
+
6933
7077
  // src/core/core/builders/PptxSlideBackgroundBuilder.ts
6934
7078
  var PptxSlideBackgroundBuilder = class {
6935
7079
  applyBackground(init) {
@@ -6965,10 +7109,13 @@ var PptxSlideBackgroundBuilder = class {
6965
7109
  init.slideImageRelationshipType,
6966
7110
  relativeBackgroundImagePath
6967
7111
  );
6968
- backgroundProperties["a:blipFill"] = {
6969
- "a:blip": { "@_r:embed": backgroundRelationshipId },
6970
- "a:stretch": { "a:fillRect": {} }
6971
- };
7112
+ backgroundProperties["a:blipFill"] = reorderObjectKeys(
7113
+ {
7114
+ "a:blip": { "@_r:embed": backgroundRelationshipId },
7115
+ "a:stretch": { "a:fillRect": {} }
7116
+ },
7117
+ BLIP_FILL_ORDER
7118
+ );
6972
7119
  }
6973
7120
  } else if (hasBackgroundColor && init.slide.backgroundColor) {
6974
7121
  backgroundProperties["a:solidFill"] = {
@@ -7712,6 +7859,45 @@ var PptxGradientStyleCodec = class {
7712
7859
  b: Number.isFinite(b) ? this.context.clampUnitInterval(b / 1e5) : 0
7713
7860
  };
7714
7861
  }
7862
+ extractGradientFlip(gradFill) {
7863
+ const flipRaw = String(gradFill["@_flip"] || "").trim().toLowerCase();
7864
+ if (flipRaw === "x" || flipRaw === "y" || flipRaw === "xy" || flipRaw === "none") {
7865
+ return flipRaw;
7866
+ }
7867
+ return void 0;
7868
+ }
7869
+ extractGradientRotWithShape(gradFill) {
7870
+ const rot = gradFill["@_rotWithShape"];
7871
+ if (rot === void 0 || rot === null) {
7872
+ return void 0;
7873
+ }
7874
+ const token = String(rot).trim().toLowerCase();
7875
+ if (token === "1" || token === "true") {
7876
+ return true;
7877
+ }
7878
+ if (token === "0" || token === "false") {
7879
+ return false;
7880
+ }
7881
+ return void 0;
7882
+ }
7883
+ extractGradientScaled(gradFill) {
7884
+ const lin = gradFill["a:lin"];
7885
+ if (!lin) {
7886
+ return void 0;
7887
+ }
7888
+ const scaled = lin["@_scaled"];
7889
+ if (scaled === void 0 || scaled === null) {
7890
+ return void 0;
7891
+ }
7892
+ const token = String(scaled).trim().toLowerCase();
7893
+ if (token === "1" || token === "true") {
7894
+ return true;
7895
+ }
7896
+ if (token === "0" || token === "false") {
7897
+ return false;
7898
+ }
7899
+ return void 0;
7900
+ }
7715
7901
  extractGradientAngle(gradFill) {
7716
7902
  const angleRaw = Number.parseInt(
7717
7903
  String(gradFill["a:lin"]?.["@_ang"] || ""),
@@ -7798,11 +7984,14 @@ var PptxGradientStyleCodec = class {
7798
7984
  return void 0;
7799
7985
  }
7800
7986
  const gradientType = shapeStyle.fillGradientType || "linear";
7801
- const gradientXml = {
7802
- "a:gsLst": {
7803
- "a:gs": stops
7804
- }
7805
- };
7987
+ const gradientXml = {};
7988
+ if (shapeStyle.fillGradientFlip && shapeStyle.fillGradientFlip !== "none") {
7989
+ gradientXml["@_flip"] = shapeStyle.fillGradientFlip;
7990
+ }
7991
+ if (shapeStyle.fillGradientRotWithShape !== void 0) {
7992
+ gradientXml["@_rotWithShape"] = shapeStyle.fillGradientRotWithShape ? "1" : "0";
7993
+ }
7994
+ gradientXml["a:gsLst"] = { "a:gs": stops };
7806
7995
  if (gradientType === "radial") {
7807
7996
  const pathType = shapeStyle.fillGradientPathType || "circle";
7808
7997
  const pathXml = {
@@ -7832,10 +8021,15 @@ var PptxGradientStyleCodec = class {
7832
8021
  gradientXml["a:path"] = pathXml;
7833
8022
  } else {
7834
8023
  const normalizedAngle = typeof shapeStyle.fillGradientAngle === "number" && Number.isFinite(shapeStyle.fillGradientAngle) ? shapeStyle.fillGradientAngle : 90;
7835
- gradientXml["a:lin"] = {
7836
- "@_ang": String(Math.round(normalizedAngle * 6e4)),
7837
- "@_scaled": "1"
8024
+ const linNode = {
8025
+ "@_ang": String(Math.round(normalizedAngle * 6e4))
7838
8026
  };
8027
+ if (shapeStyle.fillGradientScaled !== void 0) {
8028
+ linNode["@_scaled"] = shapeStyle.fillGradientScaled ? "1" : "0";
8029
+ } else {
8030
+ linNode["@_scaled"] = "1";
8031
+ }
8032
+ gradientXml["a:lin"] = linNode;
7839
8033
  }
7840
8034
  return gradientXml;
7841
8035
  }
@@ -8232,6 +8426,30 @@ var PRESET_SHADOW_OPACITY_MAP = {
8232
8426
  };
8233
8427
 
8234
8428
  // src/core/core/builders/PptxShapeEffectStyleExtractor.ts
8429
+ var VALID_ALIGNMENTS = /* @__PURE__ */ new Set(["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"]);
8430
+ function parseIntAttr(value) {
8431
+ if (value === void 0 || value === null || value === "") {
8432
+ return void 0;
8433
+ }
8434
+ const parsed = Number.parseInt(String(value), 10);
8435
+ return Number.isFinite(parsed) ? parsed : void 0;
8436
+ }
8437
+ function parseAlignmentAttr(value) {
8438
+ const v = String(value ?? "").trim();
8439
+ return VALID_ALIGNMENTS.has(v) ? v : void 0;
8440
+ }
8441
+ function parseBoolAttr(value) {
8442
+ if (typeof value === "boolean") {
8443
+ return value;
8444
+ }
8445
+ if (value === "1" || value === "true") {
8446
+ return true;
8447
+ }
8448
+ if (value === "0" || value === "false") {
8449
+ return false;
8450
+ }
8451
+ return void 0;
8452
+ }
8235
8453
  var PptxShapeEffectStyleExtractor = class {
8236
8454
  context;
8237
8455
  constructor(context) {
@@ -8255,7 +8473,12 @@ var PptxShapeEffectStyleExtractor = class {
8255
8473
  const shadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8256
8474
  const shadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8257
8475
  const rotateWithShape = outerShadow["@_rotWithShape"];
8258
- const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : void 0;
8476
+ const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : rotateWithShape === "0" || rotateWithShape === "false" ? false : void 0;
8477
+ const shadowScaleX = parseIntAttr(outerShadow["@_sx"]);
8478
+ const shadowScaleY = parseIntAttr(outerShadow["@_sy"]);
8479
+ const shadowSkewX = parseIntAttr(outerShadow["@_kx"]);
8480
+ const shadowSkewY = parseIntAttr(outerShadow["@_ky"]);
8481
+ const shadowAlignment = parseAlignmentAttr(outerShadow["@_algn"]);
8259
8482
  return {
8260
8483
  shadowColor,
8261
8484
  shadowOpacity,
@@ -8264,7 +8487,12 @@ var PptxShapeEffectStyleExtractor = class {
8264
8487
  shadowOffsetY,
8265
8488
  shadowAngle: directionDegrees,
8266
8489
  shadowDistance: distance,
8267
- shadowRotateWithShape
8490
+ shadowRotateWithShape,
8491
+ shadowScaleX,
8492
+ shadowScaleY,
8493
+ shadowSkewX,
8494
+ shadowSkewY,
8495
+ shadowAlignment
8268
8496
  };
8269
8497
  }
8270
8498
  extractPresetShadowStyle(effectLstParent) {
@@ -8310,12 +8538,14 @@ var PptxShapeEffectStyleExtractor = class {
8310
8538
  const directionRadians = directionDegrees * Math.PI / 180;
8311
8539
  const innerShadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8312
8540
  const innerShadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8541
+ const innerShadowRotateWithShape = parseBoolAttr(innerShadow["@_rotWithShape"]);
8313
8542
  return {
8314
8543
  innerShadowColor,
8315
8544
  innerShadowOpacity,
8316
8545
  innerShadowBlur,
8317
8546
  innerShadowOffsetX,
8318
- innerShadowOffsetY
8547
+ innerShadowOffsetY,
8548
+ innerShadowRotateWithShape
8319
8549
  };
8320
8550
  }
8321
8551
  extractGlowStyle(shapeProps) {
@@ -8364,6 +8594,16 @@ var PptxShapeEffectStyleExtractor = class {
8364
8594
  const reflectionRotation = Number.isFinite(rotationRaw) ? rotationRaw / 6e4 : void 0;
8365
8595
  const distanceRaw = Number.parseInt(String(reflectionNode["@_dist"] || ""), 10);
8366
8596
  const reflectionDistance = Number.isFinite(distanceRaw) && distanceRaw >= 0 ? distanceRaw / this.context.emuPerPx : void 0;
8597
+ const fadeDirRaw = parseIntAttr(reflectionNode["@_fadeDir"]);
8598
+ const reflectionFadeDirection = fadeDirRaw !== void 0 ? fadeDirRaw / 6e4 : void 0;
8599
+ const reflectionScaleX = parseIntAttr(reflectionNode["@_sx"]);
8600
+ const reflectionScaleY = parseIntAttr(reflectionNode["@_sy"]);
8601
+ const reflectionSkewX = parseIntAttr(reflectionNode["@_kx"]);
8602
+ const reflectionSkewY = parseIntAttr(reflectionNode["@_ky"]);
8603
+ const reflectionAlignment = parseAlignmentAttr(reflectionNode["@_algn"]);
8604
+ const reflectionRotateWithShape = parseBoolAttr(reflectionNode["@_rotWithShape"]);
8605
+ const stPosRaw = parseIntAttr(reflectionNode["@_stPos"]);
8606
+ const reflectionStartPosition = stPosRaw !== void 0 ? stPosRaw / 1e5 : void 0;
8367
8607
  return {
8368
8608
  reflectionBlurRadius,
8369
8609
  reflectionStartOpacity,
@@ -8371,7 +8611,15 @@ var PptxShapeEffectStyleExtractor = class {
8371
8611
  reflectionEndPosition,
8372
8612
  reflectionDirection,
8373
8613
  reflectionRotation,
8374
- reflectionDistance
8614
+ reflectionDistance,
8615
+ reflectionFadeDirection,
8616
+ reflectionScaleX,
8617
+ reflectionScaleY,
8618
+ reflectionSkewX,
8619
+ reflectionSkewY,
8620
+ reflectionAlignment,
8621
+ reflectionRotateWithShape,
8622
+ reflectionStartPosition
8375
8623
  };
8376
8624
  }
8377
8625
  extractBlurStyle(shapeProps) {
@@ -8423,11 +8671,56 @@ var PptxShapeEffectXmlBuilder = class {
8423
8671
  }
8424
8672
  }
8425
8673
  };
8674
+ if (typeof shapeStyle.shadowScaleX === "number") {
8675
+ xmlObj["@_sx"] = String(Math.round(shapeStyle.shadowScaleX));
8676
+ }
8677
+ if (typeof shapeStyle.shadowScaleY === "number") {
8678
+ xmlObj["@_sy"] = String(Math.round(shapeStyle.shadowScaleY));
8679
+ }
8680
+ if (typeof shapeStyle.shadowSkewX === "number") {
8681
+ xmlObj["@_kx"] = String(Math.round(shapeStyle.shadowSkewX));
8682
+ }
8683
+ if (typeof shapeStyle.shadowSkewY === "number") {
8684
+ xmlObj["@_ky"] = String(Math.round(shapeStyle.shadowSkewY));
8685
+ }
8686
+ if (shapeStyle.shadowAlignment) {
8687
+ xmlObj["@_algn"] = shapeStyle.shadowAlignment;
8688
+ }
8426
8689
  if (typeof shapeStyle.shadowRotateWithShape === "boolean") {
8427
8690
  xmlObj["@_rotWithShape"] = shapeStyle.shadowRotateWithShape ? "1" : "0";
8428
8691
  }
8429
8692
  return xmlObj;
8430
8693
  }
8694
+ buildPresetShadowXml(shapeStyle) {
8695
+ const preset = shapeStyle.presetShadowName;
8696
+ if (!preset || preset.length === 0) {
8697
+ return void 0;
8698
+ }
8699
+ const shadowColor = String(shapeStyle.shadowColor || "#000000").trim();
8700
+ const shadowOpacity = typeof shapeStyle.shadowOpacity === "number" && Number.isFinite(shapeStyle.shadowOpacity) ? this.context.clampUnitInterval(shapeStyle.shadowOpacity) : 0.5;
8701
+ let distance;
8702
+ let directionDegrees;
8703
+ if (typeof shapeStyle.shadowAngle === "number" && typeof shapeStyle.shadowDistance === "number") {
8704
+ directionDegrees = shapeStyle.shadowAngle;
8705
+ distance = shapeStyle.shadowDistance;
8706
+ } else {
8707
+ const ox = typeof shapeStyle.shadowOffsetX === "number" ? shapeStyle.shadowOffsetX : 0;
8708
+ const oy = typeof shapeStyle.shadowOffsetY === "number" ? shapeStyle.shadowOffsetY : 0;
8709
+ distance = Math.sqrt(ox * ox + oy * oy);
8710
+ directionDegrees = (Math.atan2(oy, ox) * 180 / Math.PI + 360) % 360;
8711
+ }
8712
+ return {
8713
+ "@_prst": preset,
8714
+ "@_dist": String(Math.round(distance * this.context.emuPerPx)),
8715
+ "@_dir": String(Math.round(directionDegrees * 6e4)),
8716
+ "a:srgbClr": {
8717
+ "@_val": shadowColor.replace("#", ""),
8718
+ "a:alpha": {
8719
+ "@_val": String(Math.round(shadowOpacity * 1e5))
8720
+ }
8721
+ }
8722
+ };
8723
+ }
8431
8724
  buildInnerShadowXml(shapeStyle) {
8432
8725
  const innerColor = String(shapeStyle.innerShadowColor || "").trim();
8433
8726
  if (innerColor.length === 0 || innerColor === "transparent") {
@@ -8439,7 +8732,7 @@ var PptxShapeEffectXmlBuilder = class {
8439
8732
  const opacity = typeof shapeStyle.innerShadowOpacity === "number" && Number.isFinite(shapeStyle.innerShadowOpacity) ? this.context.clampUnitInterval(shapeStyle.innerShadowOpacity) : 0.5;
8440
8733
  const distance = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
8441
8734
  const directionDegrees = (Math.atan2(offsetY, offsetX) * 180 / Math.PI + 360) % 360;
8442
- return {
8735
+ const xmlObj = {
8443
8736
  "@_blurRad": String(Math.round(blurValue * this.context.emuPerPx)),
8444
8737
  "@_dist": String(Math.round(distance * this.context.emuPerPx)),
8445
8738
  "@_dir": String(Math.round(directionDegrees * 6e4)),
@@ -8450,6 +8743,10 @@ var PptxShapeEffectXmlBuilder = class {
8450
8743
  }
8451
8744
  }
8452
8745
  };
8746
+ if (typeof shapeStyle.innerShadowRotateWithShape === "boolean") {
8747
+ xmlObj["@_rotWithShape"] = shapeStyle.innerShadowRotateWithShape ? "1" : "0";
8748
+ }
8749
+ return xmlObj;
8453
8750
  }
8454
8751
  buildGlowXml(shapeStyle) {
8455
8752
  const glowColor = String(shapeStyle.glowColor || "").trim();
@@ -8511,6 +8808,30 @@ var PptxShapeEffectXmlBuilder = class {
8511
8808
  Math.round(shapeStyle.reflectionDistance * this.context.emuPerPx)
8512
8809
  );
8513
8810
  }
8811
+ if (typeof shapeStyle.reflectionFadeDirection === "number") {
8812
+ reflectionXml["@_fadeDir"] = String(Math.round(shapeStyle.reflectionFadeDirection * 6e4));
8813
+ }
8814
+ if (typeof shapeStyle.reflectionScaleX === "number") {
8815
+ reflectionXml["@_sx"] = String(Math.round(shapeStyle.reflectionScaleX));
8816
+ }
8817
+ if (typeof shapeStyle.reflectionScaleY === "number") {
8818
+ reflectionXml["@_sy"] = String(Math.round(shapeStyle.reflectionScaleY));
8819
+ }
8820
+ if (typeof shapeStyle.reflectionSkewX === "number") {
8821
+ reflectionXml["@_kx"] = String(Math.round(shapeStyle.reflectionSkewX));
8822
+ }
8823
+ if (typeof shapeStyle.reflectionSkewY === "number") {
8824
+ reflectionXml["@_ky"] = String(Math.round(shapeStyle.reflectionSkewY));
8825
+ }
8826
+ if (shapeStyle.reflectionAlignment) {
8827
+ reflectionXml["@_algn"] = shapeStyle.reflectionAlignment;
8828
+ }
8829
+ if (typeof shapeStyle.reflectionRotateWithShape === "boolean") {
8830
+ reflectionXml["@_rotWithShape"] = shapeStyle.reflectionRotateWithShape ? "1" : "0";
8831
+ }
8832
+ if (typeof shapeStyle.reflectionStartPosition === "number") {
8833
+ reflectionXml["@_stPos"] = String(Math.round(shapeStyle.reflectionStartPosition * 1e5));
8834
+ }
8514
8835
  return reflectionXml;
8515
8836
  }
8516
8837
  buildBlurXml(shapeStyle) {
@@ -8618,6 +8939,9 @@ var PptxShapeEffectXmlCodec = class {
8618
8939
  buildOuterShadowXml(shapeStyle) {
8619
8940
  return this.builder.buildOuterShadowXml(shapeStyle);
8620
8941
  }
8942
+ buildPresetShadowXml(shapeStyle) {
8943
+ return this.builder.buildPresetShadowXml(shapeStyle);
8944
+ }
8621
8945
  buildInnerShadowXml(shapeStyle) {
8622
8946
  return this.builder.buildInnerShadowXml(shapeStyle);
8623
8947
  }
@@ -8778,6 +9102,9 @@ var PptxColorStyleCodec = class {
8778
9102
  buildOuterShadowXml(shapeStyle) {
8779
9103
  return this.shapeEffectXmlCodec.buildOuterShadowXml(shapeStyle);
8780
9104
  }
9105
+ buildPresetShadowXml(shapeStyle) {
9106
+ return this.shapeEffectXmlCodec.buildPresetShadowXml(shapeStyle);
9107
+ }
8781
9108
  buildInnerShadowXml(shapeStyle) {
8782
9109
  return this.shapeEffectXmlCodec.buildInnerShadowXml(shapeStyle);
8783
9110
  }
@@ -8799,6 +9126,15 @@ var PptxColorStyleCodec = class {
8799
9126
  extractGradientFillColor(gradFill) {
8800
9127
  return this.gradientStyleCodec.extractGradientFillColor(gradFill);
8801
9128
  }
9129
+ extractGradientFlip(gradFill) {
9130
+ return this.gradientStyleCodec.extractGradientFlip(gradFill);
9131
+ }
9132
+ extractGradientRotWithShape(gradFill) {
9133
+ return this.gradientStyleCodec.extractGradientRotWithShape(gradFill);
9134
+ }
9135
+ extractGradientScaled(gradFill) {
9136
+ return this.gradientStyleCodec.extractGradientScaled(gradFill);
9137
+ }
8802
9138
  extractGradientPathType(gradFill) {
8803
9139
  return this.gradientStyleCodec.extractGradientPathType(gradFill);
8804
9140
  }
@@ -8810,6 +9146,61 @@ var PptxColorStyleCodec = class {
8810
9146
  }
8811
9147
  };
8812
9148
 
9149
+ // src/core/utils/color-xml-preservation.ts
9150
+ var COLOR_CHOICE_KEYS = [
9151
+ "a:srgbClr",
9152
+ "a:schemeClr",
9153
+ "a:sysClr",
9154
+ "a:prstClr",
9155
+ "a:scrgbClr",
9156
+ "a:hslClr"
9157
+ ];
9158
+ function extractColorChoiceXml(parent) {
9159
+ if (!parent) {
9160
+ return void 0;
9161
+ }
9162
+ for (const key of COLOR_CHOICE_KEYS) {
9163
+ if (parent[key] !== void 0) {
9164
+ return { [key]: parent[key] };
9165
+ }
9166
+ }
9167
+ return void 0;
9168
+ }
9169
+ function normalizeHex(value) {
9170
+ const raw = String(value ?? "").trim();
9171
+ if (raw.length === 0) {
9172
+ return "";
9173
+ }
9174
+ const hex = raw.replace(/^#/, "");
9175
+ if (/^[0-9a-fA-F]{6}$/.test(hex)) {
9176
+ return hex.toUpperCase();
9177
+ }
9178
+ return raw.toLowerCase();
9179
+ }
9180
+ function colorsEqual(left, right) {
9181
+ if (left === void 0 || right === void 0) {
9182
+ return false;
9183
+ }
9184
+ return normalizeHex(left) === normalizeHex(right);
9185
+ }
9186
+ function buildSrgbColorChoice(hex, opacity) {
9187
+ const normalized = String(hex || "").replace(/^#/, "");
9188
+ const srgb = { "@_val": normalized };
9189
+ if (typeof opacity === "number" && Number.isFinite(opacity) && opacity >= 0 && opacity < 1) {
9190
+ const alphaPct = Math.round(Math.max(0, Math.min(1, opacity)) * 1e5);
9191
+ srgb["a:alpha"] = { "@_val": String(alphaPct) };
9192
+ }
9193
+ return { "a:srgbClr": srgb };
9194
+ }
9195
+ function serializeColorChoice(originalColorXml, currentResolvedHex, fallbackHex, opacity, options = {}) {
9196
+ if (originalColorXml && colorsEqual(currentResolvedHex, fallbackHex)) {
9197
+ if (options.preserveAlphaFromOriginal !== false) {
9198
+ return originalColorXml;
9199
+ }
9200
+ }
9201
+ return buildSrgbColorChoice(fallbackHex, opacity);
9202
+ }
9203
+
8813
9204
  // src/core/core/builders/shape-style-3d-helpers.ts
8814
9205
  function applyScene3dStyle(shapeProps, style) {
8815
9206
  const scene3dNode = shapeProps["a:scene3d"];
@@ -8895,6 +9286,10 @@ function applyStrokeColor(lineNode, style, context) {
8895
9286
  const lineFill = lineNode["a:solidFill"];
8896
9287
  style.strokeColor = context.parseColor(lineFill);
8897
9288
  style.strokeOpacity = context.extractColorOpacity(lineFill);
9289
+ const strokeColorXml = extractColorChoiceXml(lineFill);
9290
+ if (strokeColorXml) {
9291
+ style.strokeColorXml = strokeColorXml;
9292
+ }
8898
9293
  } else if (lineNode["a:gradFill"]) {
8899
9294
  style.strokeColor = context.extractGradientFillColor(lineNode["a:gradFill"]);
8900
9295
  style.strokeOpacity = context.extractGradientOpacity(lineNode["a:gradFill"]);
@@ -8962,6 +9357,14 @@ function applyJoinCapCompound(lineNode, style) {
8962
9357
  style.lineJoin = "bevel";
8963
9358
  } else if ("a:miter" in lineNode) {
8964
9359
  style.lineJoin = "miter";
9360
+ const miterNode = lineNode["a:miter"];
9361
+ const limRaw = miterNode?.["@_lim"];
9362
+ if (limRaw !== void 0 && limRaw !== "") {
9363
+ const parsed = parseInt(String(limRaw), 10);
9364
+ if (Number.isFinite(parsed)) {
9365
+ style.miterLimit = parsed;
9366
+ }
9367
+ }
8965
9368
  }
8966
9369
  const capValue = String(lineNode["@_cap"] || "").trim().toLowerCase();
8967
9370
  if (capValue === "rnd" || capValue === "sq" || capValue === "flat") {
@@ -9018,6 +9421,10 @@ var PptxShapeStyleExtractor = class {
9018
9421
  style.fillMode = "solid";
9019
9422
  style.fillColor = this.context.parseColor(solidFill);
9020
9423
  style.fillOpacity = this.context.extractColorOpacity(solidFill);
9424
+ const solidFillColorXml = extractColorChoiceXml(solidFill);
9425
+ if (solidFillColorXml) {
9426
+ style.fillColorXml = solidFillColorXml;
9427
+ }
9021
9428
  } else if (gradFill) {
9022
9429
  style.fillMode = "gradient";
9023
9430
  style.fillColor = this.context.extractGradientFillColor(gradFill);
@@ -9029,6 +9436,18 @@ var PptxShapeStyleExtractor = class {
9029
9436
  style.fillGradientPathType = this.context.extractGradientPathType(gradFill);
9030
9437
  style.fillGradientFocalPoint = this.context.extractGradientFocalPoint(gradFill);
9031
9438
  style.fillGradientFillToRect = this.context.extractGradientFillToRect(gradFill);
9439
+ const gradFlip = this.context.extractGradientFlip(gradFill);
9440
+ if (gradFlip) {
9441
+ style.fillGradientFlip = gradFlip;
9442
+ }
9443
+ const gradRot = this.context.extractGradientRotWithShape(gradFill);
9444
+ if (gradRot !== void 0) {
9445
+ style.fillGradientRotWithShape = gradRot;
9446
+ }
9447
+ const gradScaled = this.context.extractGradientScaled(gradFill);
9448
+ if (gradScaled !== void 0) {
9449
+ style.fillGradientScaled = gradScaled;
9450
+ }
9032
9451
  } else if (pattFill) {
9033
9452
  style.fillMode = "pattern";
9034
9453
  style.fillColor = this.context.parseColor(pattFill["a:fgClr"]) || this.context.parseColor(pattFill["a:bgClr"]);
@@ -9110,10 +9529,45 @@ var PptxShapeStyleExtractor = class {
9110
9529
  if (styleNode?.["a:effectRef"]) {
9111
9530
  this.context.resolveThemeEffectRef(styleNode["a:effectRef"], style);
9112
9531
  }
9532
+ const fontRef = styleNode?.["a:fontRef"];
9533
+ if (fontRef) {
9534
+ const idxAttr = String(fontRef["@_idx"] || "").trim();
9535
+ if (idxAttr.length > 0) {
9536
+ style.fontRefIdx = idxAttr;
9537
+ }
9538
+ const overrideColorXml = this.extractFontRefColorXml(fontRef);
9539
+ if (overrideColorXml) {
9540
+ style.fontRefColorXml = overrideColorXml;
9541
+ }
9542
+ }
9113
9543
  applyScene3dStyle(shapeProps, style);
9114
9544
  applyShape3dStyle(shapeProps, style, this.context);
9115
9545
  return style;
9116
9546
  }
9547
+ /**
9548
+ * Pull the verbatim colour-choice child out of an `a:fontRef` element,
9549
+ * preserving any contained colour transforms for round-trip.
9550
+ */
9551
+ extractFontRefColorXml(refNode) {
9552
+ if (!refNode) {
9553
+ return void 0;
9554
+ }
9555
+ const keys = [
9556
+ "a:scrgbClr",
9557
+ "a:srgbClr",
9558
+ "a:hslClr",
9559
+ "a:sysClr",
9560
+ "a:schemeClr",
9561
+ "a:prstClr"
9562
+ ];
9563
+ for (const key of keys) {
9564
+ const child = refNode[key];
9565
+ if (child !== void 0) {
9566
+ return { [key]: child };
9567
+ }
9568
+ }
9569
+ return void 0;
9570
+ }
9117
9571
  /**
9118
9572
  * Extract p14:hiddenFill from the shape properties extension list.
9119
9573
  * URI: {AF507438-7753-43E0-B8FC-AC1667EBCBE1}
@@ -9164,10 +9618,15 @@ var PptxShapeStyleExtractor = class {
9164
9618
  function applyCellFillStyle(cellProperties, style, context) {
9165
9619
  let hasStyle = false;
9166
9620
  if (cellProperties?.["a:solidFill"]) {
9167
- const fillColor = context.parseColor(cellProperties["a:solidFill"]);
9621
+ const solidFillNode = cellProperties["a:solidFill"];
9622
+ const fillColor = context.parseColor(solidFillNode);
9168
9623
  if (fillColor) {
9169
9624
  style.fillMode = "solid";
9170
9625
  style.backgroundColor = fillColor;
9626
+ const bgColorXml = extractColorChoiceXml(solidFillNode);
9627
+ if (bgColorXml) {
9628
+ style.backgroundColorXml = bgColorXml;
9629
+ }
9171
9630
  hasStyle = true;
9172
9631
  }
9173
9632
  }
@@ -9204,6 +9663,10 @@ function applyCellFillStyle(cellProperties, style, context) {
9204
9663
  }
9205
9664
  }
9206
9665
  }
9666
+ if (cellProperties?.["a:noFill"] !== void 0) {
9667
+ style.fillMode = "none";
9668
+ hasStyle = true;
9669
+ }
9207
9670
  if (cellProperties?.["a:pattFill"]) {
9208
9671
  const pattFill = cellProperties["a:pattFill"];
9209
9672
  const fgColor = context.parseColor(pattFill["a:fgClr"]);
@@ -9510,8 +9973,7 @@ var PptxTableDataParser = class {
9510
9973
  return width / totalWidthEmu;
9511
9974
  }) : gridColumns.map(() => 1 / Math.max(gridColumns.length, 1));
9512
9975
  const tableProperties = tableNode["a:tblPr"] || {};
9513
- const tableStyleNode = tableProperties["a:tblStyle"];
9514
- const tableStyleId = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim() || void 0;
9976
+ const tableStyleId = this.extractTableStyleId(tableProperties);
9515
9977
  const xmlRows = this.context.ensureArray(tableNode["a:tr"]);
9516
9978
  const rows = xmlRows.map((rowNode) => {
9517
9979
  const rowHeightEmu = parseInt(String(rowNode?.["@_h"] || "0"), 10) || 0;
@@ -9546,6 +10008,28 @@ var PptxTableDataParser = class {
9546
10008
  return void 0;
9547
10009
  }
9548
10010
  }
10011
+ /**
10012
+ * Read the table style ID from `a:tblPr`.
10013
+ *
10014
+ * ECMA-376 §21.1.3.13 defines `<a:tableStyleId>{GUID}</a:tableStyleId>` as
10015
+ * a child element of `a:tblPr` carrying the GUID as element text. Older
10016
+ * inputs (and earlier versions of this library) used the legacy
10017
+ * `<a:tblStyle val="{GUID}"/>` child element or a `@_tblStyle` attribute.
10018
+ * Accept all three; the spec form takes precedence.
10019
+ */
10020
+ extractTableStyleId(tableProperties) {
10021
+ const tableStyleIdNode = tableProperties["a:tableStyleId"];
10022
+ if (tableStyleIdNode !== void 0 && tableStyleIdNode !== null) {
10023
+ const direct = typeof tableStyleIdNode === "string" || typeof tableStyleIdNode === "number" ? String(tableStyleIdNode) : String(tableStyleIdNode["#text"] ?? "");
10024
+ const trimmed = direct.trim();
10025
+ if (trimmed.length > 0) {
10026
+ return trimmed;
10027
+ }
10028
+ }
10029
+ const tableStyleNode = tableProperties["a:tblStyle"];
10030
+ const legacy = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim();
10031
+ return legacy.length > 0 ? legacy : void 0;
10032
+ }
9549
10033
  extractTableCellText(tableCell) {
9550
10034
  const paragraphs = this.context.ensureArray(tableCell?.["a:txBody"]?.["a:p"]);
9551
10035
  const lines = [];
@@ -9824,6 +10308,19 @@ var PptxGraphicFrameParser = class {
9824
10308
  if (graphicData["a:videoFile"] || graphicData["a:audioFile"] || uri.includes("/drawingml/2006/media")) {
9825
10309
  return "media";
9826
10310
  }
10311
+ if (graphicData["aink:ink"] || uri.includes("/2010/ink") || uri.includes("drawing/2010/ink")) {
10312
+ return "ink";
10313
+ }
10314
+ const alternateContent = graphicData["mc:AlternateContent"];
10315
+ if (alternateContent) {
10316
+ const choices = Array.isArray(alternateContent["mc:Choice"]) ? alternateContent["mc:Choice"] : alternateContent["mc:Choice"] ? [alternateContent["mc:Choice"]] : [];
10317
+ for (const choice of choices) {
10318
+ const requires = String(choice?.["@_Requires"] || "").toLowerCase();
10319
+ if (requires.includes("aink") || choice?.["aink:ink"]) {
10320
+ return "ink";
10321
+ }
10322
+ }
10323
+ }
9827
10324
  return "unknown";
9828
10325
  }
9829
10326
  };
@@ -10725,6 +11222,7 @@ var PptxDocumentPropertiesUpdater = class {
10725
11222
  }));
10726
11223
  if (sanitized.length === 0) {
10727
11224
  this.context.zip.remove("docProps/custom.xml");
11225
+ await this.removeCustomPropertiesPackagingArtifacts();
10728
11226
  return;
10729
11227
  }
10730
11228
  const customXml = {
@@ -10744,6 +11242,116 @@ var PptxDocumentPropertiesUpdater = class {
10744
11242
  }
10745
11243
  };
10746
11244
  this.context.zip.file("docProps/custom.xml", this.context.builder.build(customXml));
11245
+ await this.ensureCustomPropertiesPackagingArtifacts();
11246
+ }
11247
+ /**
11248
+ * Ensure `[Content_Types].xml` has an `Override` for `docProps/custom.xml`
11249
+ * and the root `_rels/.rels` references it (ECMA-376 §15.2.12.2 +
11250
+ * Part 2 §10.1.2.5). Without these, the package fails OPC validation
11251
+ * and Office strips the custom properties on next save.
11252
+ */
11253
+ async ensureCustomPropertiesPackagingArtifacts() {
11254
+ const customContentType = "application/vnd.openxmlformats-officedocument.custom-properties+xml";
11255
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
11256
+ const ctFile = this.context.zip.file("[Content_Types].xml");
11257
+ if (ctFile) {
11258
+ try {
11259
+ const ctXml = await ctFile.async("string");
11260
+ const ctData = this.context.parser.parse(ctXml);
11261
+ const types = ctData["Types"];
11262
+ if (types) {
11263
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
11264
+ const hasCustomOverride = overrides.some(
11265
+ (o) => String(o?.["@_PartName"] || "") === "/docProps/custom.xml"
11266
+ );
11267
+ if (!hasCustomOverride) {
11268
+ overrides.push({
11269
+ "@_PartName": "/docProps/custom.xml",
11270
+ "@_ContentType": customContentType
11271
+ });
11272
+ types["Override"] = overrides.length === 1 ? overrides[0] : overrides;
11273
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
11274
+ }
11275
+ }
11276
+ } catch (error) {
11277
+ console.warn("Failed to update [Content_Types].xml for custom properties:", error);
11278
+ }
11279
+ }
11280
+ const relsFile = this.context.zip.file("_rels/.rels");
11281
+ if (relsFile) {
11282
+ try {
11283
+ const relsXml = await relsFile.async("string");
11284
+ const relsData = this.context.parser.parse(relsXml);
11285
+ const relationships = relsData["Relationships"];
11286
+ if (relationships) {
11287
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
11288
+ const hasCustomRel = rels.some((r) => String(r?.["@_Type"] || "") === customRelType);
11289
+ if (!hasCustomRel) {
11290
+ let maxId = 0;
11291
+ for (const rel of rels) {
11292
+ const id = String(rel?.["@_Id"] || "");
11293
+ const num = Number.parseInt(id.replace(/^rId/, ""), 10);
11294
+ if (Number.isFinite(num) && num > maxId) {
11295
+ maxId = num;
11296
+ }
11297
+ }
11298
+ rels.push({
11299
+ "@_Id": `rId${maxId + 1}`,
11300
+ "@_Type": customRelType,
11301
+ "@_Target": "docProps/custom.xml"
11302
+ });
11303
+ relationships["Relationship"] = rels;
11304
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
11305
+ }
11306
+ }
11307
+ } catch (error) {
11308
+ console.warn("Failed to update _rels/.rels for custom properties:", error);
11309
+ }
11310
+ }
11311
+ }
11312
+ /**
11313
+ * Remove the Override + root rel for `docProps/custom.xml` when the
11314
+ * caller has emptied custom properties so the package doesn't keep an
11315
+ * orphan content-type entry referencing a deleted part.
11316
+ */
11317
+ async removeCustomPropertiesPackagingArtifacts() {
11318
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
11319
+ const ctFile = this.context.zip.file("[Content_Types].xml");
11320
+ if (ctFile) {
11321
+ try {
11322
+ const ctXml = await ctFile.async("string");
11323
+ const ctData = this.context.parser.parse(ctXml);
11324
+ const types = ctData["Types"];
11325
+ if (types) {
11326
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
11327
+ const filtered = overrides.filter(
11328
+ (o) => String(o?.["@_PartName"] || "") !== "/docProps/custom.xml"
11329
+ );
11330
+ if (filtered.length !== overrides.length) {
11331
+ types["Override"] = filtered.length === 1 ? filtered[0] : filtered;
11332
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
11333
+ }
11334
+ }
11335
+ } catch {
11336
+ }
11337
+ }
11338
+ const relsFile = this.context.zip.file("_rels/.rels");
11339
+ if (relsFile) {
11340
+ try {
11341
+ const relsXml = await relsFile.async("string");
11342
+ const relsData = this.context.parser.parse(relsXml);
11343
+ const relationships = relsData["Relationships"];
11344
+ if (relationships) {
11345
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
11346
+ const filtered = rels.filter((r) => String(r?.["@_Type"] || "") !== customRelType);
11347
+ if (filtered.length !== rels.length) {
11348
+ relationships["Relationship"] = filtered;
11349
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
11350
+ }
11351
+ }
11352
+ } catch {
11353
+ }
11354
+ }
10747
11355
  }
10748
11356
  normalizeCustomPropertyType(type) {
10749
11357
  const supportedTypes = /* @__PURE__ */ new Set([
@@ -11087,6 +11695,7 @@ var PptxSlideLoaderService = class {
11087
11695
  await params.loadSlideRelationships(path2, slideRelsPath);
11088
11696
  const clrMapOverride = params.parseSlideClrMapOverride(slideXmlObj);
11089
11697
  params.setCurrentSlideClrMapOverride(clrMapOverride);
11698
+ await params.setActiveMasterForSlide?.(path2);
11090
11699
  let restoreThemeOverride;
11091
11700
  try {
11092
11701
  const layoutPathForOverride = params.findLayoutPathForSlide(path2);
@@ -15838,6 +16447,20 @@ var AXIS_TYPE_MAP = {
15838
16447
  dateAx: "dateAx",
15839
16448
  serAx: "serAx"
15840
16449
  };
16450
+ function upsertChartAxisChild(parent, localName, value, getLocalName) {
16451
+ const existingKey = Object.keys(parent).find((k) => getLocalName(k) === localName);
16452
+ if (value === void 0) {
16453
+ if (existingKey) {
16454
+ delete parent[existingKey];
16455
+ }
16456
+ return;
16457
+ }
16458
+ if (existingKey) {
16459
+ parent[existingKey]["@_val"] = value;
16460
+ } else {
16461
+ parent[`c:${localName}`] = { "@_val": value };
16462
+ }
16463
+ }
15841
16464
  function parseChartAxes(plotArea, xmlLookup, colorParser, getLocalName) {
15842
16465
  const result = [];
15843
16466
  for (const key of Object.keys(plotArea)) {
@@ -15958,6 +16581,27 @@ function parseSingleAxis(axisNode, axisType, xmlLookup, colorParser) {
15958
16581
  if (dispUnitsNode) {
15959
16582
  parseDisplayUnits(dispUnitsNode, xmlLookup, result);
15960
16583
  }
16584
+ const majorUnitNode = xmlLookup.getChildByLocalName(axisNode, "majorUnit");
16585
+ if (majorUnitNode) {
16586
+ const majorVal = parseFloat(String(majorUnitNode["@_val"]));
16587
+ if (Number.isFinite(majorVal)) {
16588
+ result.majorUnit = majorVal;
16589
+ }
16590
+ }
16591
+ const minorUnitNode = xmlLookup.getChildByLocalName(axisNode, "minorUnit");
16592
+ if (minorUnitNode) {
16593
+ const minorVal = parseFloat(String(minorUnitNode["@_val"]));
16594
+ if (Number.isFinite(minorVal)) {
16595
+ result.minorUnit = minorVal;
16596
+ }
16597
+ }
16598
+ const tickLblPosNode = xmlLookup.getChildByLocalName(axisNode, "tickLblPos");
16599
+ if (tickLblPosNode) {
16600
+ const v = String(tickLblPosNode["@_val"] || "").trim();
16601
+ if (v === "high" || v === "low" || v === "nextTo" || v === "none") {
16602
+ result.tickLblPos = v;
16603
+ }
16604
+ }
15961
16605
  return result;
15962
16606
  }
15963
16607
  var VALID_DISPLAY_UNITS = /* @__PURE__ */ new Set([
@@ -17269,23 +17913,55 @@ var SHAPE_TREE_ELEMENT_TAGS = /* @__PURE__ */ new Set([
17269
17913
  "p16:model3D",
17270
17914
  ...VML_SHAPE_TAGS
17271
17915
  ]);
17916
+ function diagnoseSelection(ac) {
17917
+ const choices = ensureArray2(ac["mc:Choice"]);
17918
+ for (let i = 0; i < choices.length; i++) {
17919
+ const choice = choices[i];
17920
+ const requires = String(choice?.["@_Requires"] ?? "").trim();
17921
+ if (requires.length === 0 || areNamespacesSupported(requires)) {
17922
+ const resolved = resolveNestedAlternateContent(choice);
17923
+ return { branch: "choice", choiceIndex: i, resolved };
17924
+ }
17925
+ }
17926
+ const fallback = ac["mc:Fallback"];
17927
+ if (fallback) {
17928
+ return { branch: "fallback", resolved: resolveNestedAlternateContent(fallback) };
17929
+ }
17930
+ return void 0;
17931
+ }
17272
17932
  function unwrapAlternateContent(container) {
17273
17933
  const altContents = ensureArray2(container["mc:AlternateContent"]);
17274
17934
  if (altContents.length === 0) {
17275
- return;
17935
+ return [];
17276
17936
  }
17937
+ const blocks = [];
17277
17938
  for (const ac of altContents) {
17278
- const branch = selectAlternateContentBranch(ac);
17279
- if (!branch) {
17939
+ const diagnosis = diagnoseSelection(ac);
17940
+ if (!diagnosis) {
17280
17941
  continue;
17281
17942
  }
17943
+ const block = {
17944
+ rawAc: ac,
17945
+ selectedBranch: diagnosis.branch,
17946
+ choiceIndex: diagnosis.branch === "choice" ? diagnosis.choiceIndex : void 0,
17947
+ childRefs: []
17948
+ };
17949
+ const branch = diagnosis.resolved;
17282
17950
  for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
17283
17951
  const children = ensureArray2(branch[tag]);
17284
17952
  if (children.length > 0) {
17285
17953
  container[tag] = [...ensureArray2(container[tag]), ...children];
17954
+ for (const child of children) {
17955
+ block.childRefs.push({ tag, node: child });
17956
+ }
17286
17957
  }
17287
17958
  }
17959
+ if (block.childRefs.length > 0) {
17960
+ blocks.push(block);
17961
+ }
17288
17962
  }
17963
+ delete container["mc:AlternateContent"];
17964
+ return blocks;
17289
17965
  }
17290
17966
  function ensureArray2(val) {
17291
17967
  if (!val) {
@@ -17297,7 +17973,7 @@ function ensureArray2(val) {
17297
17973
 
17298
17974
  // src/core/utils/body-properties-parser.ts
17299
17975
  function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17300
- const parseBoolAttr = (attr) => {
17976
+ const parseBoolAttr2 = (attr) => {
17301
17977
  const raw = bodyPr[attr];
17302
17978
  if (raw === void 0) {
17303
17979
  return void 0;
@@ -17305,19 +17981,19 @@ function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17305
17981
  const val = String(raw).trim().toLowerCase();
17306
17982
  return val === "1" || val === "true";
17307
17983
  };
17308
- const compatLnSpc = parseBoolAttr("@_compatLnSpc");
17984
+ const compatLnSpc = parseBoolAttr2("@_compatLnSpc");
17309
17985
  if (compatLnSpc !== void 0) {
17310
17986
  textStyle.compatibleLineSpacing = compatLnSpc;
17311
17987
  }
17312
- const forceAA = parseBoolAttr("@_forceAA");
17988
+ const forceAA = parseBoolAttr2("@_forceAA");
17313
17989
  if (forceAA !== void 0) {
17314
17990
  textStyle.forceAntiAlias = forceAA;
17315
17991
  }
17316
- const upright = parseBoolAttr("@_upright");
17992
+ const upright = parseBoolAttr2("@_upright");
17317
17993
  if (upright !== void 0) {
17318
17994
  textStyle.upright = upright;
17319
17995
  }
17320
- const fromWordArt = parseBoolAttr("@_fromWordArt");
17996
+ const fromWordArt = parseBoolAttr2("@_fromWordArt");
17321
17997
  if (fromWordArt !== void 0) {
17322
17998
  textStyle.fromWordArt = fromWordArt;
17323
17999
  }
@@ -17340,46 +18016,6 @@ function writeBodyPrBooleanAttrs(bodyPr, textStyle) {
17340
18016
  }
17341
18017
  }
17342
18018
 
17343
- // src/core/utils/theme-override-utils.ts
17344
- var COLOR_MAP_ALIAS_KEYS = [
17345
- "bg1",
17346
- "tx1",
17347
- "bg2",
17348
- "tx2",
17349
- "accent1",
17350
- "accent2",
17351
- "accent3",
17352
- "accent4",
17353
- "accent5",
17354
- "accent6",
17355
- "hlink",
17356
- "folHlink"
17357
- ];
17358
- var DEFAULT_COLOR_MAP = {
17359
- bg1: "lt1",
17360
- tx1: "dk1",
17361
- bg2: "lt2",
17362
- tx2: "dk2",
17363
- accent1: "accent1",
17364
- accent2: "accent2",
17365
- accent3: "accent3",
17366
- accent4: "accent4",
17367
- accent5: "accent5",
17368
- accent6: "accent6",
17369
- hlink: "hlink",
17370
- folHlink: "folHlink"
17371
- };
17372
- function buildClrMapOverrideXml(override) {
17373
- if (!override || Object.keys(override).length === 0) {
17374
- return { "a:masterClrMapping": {} };
17375
- }
17376
- const attrs2 = {};
17377
- for (const key of COLOR_MAP_ALIAS_KEYS) {
17378
- attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
17379
- }
17380
- return { "a:overrideClrMapping": attrs2 };
17381
- }
17382
-
17383
18019
  // src/core/utils/data-url-utils.ts
17384
18020
  function parseDataUrlToBytes(dataUrl) {
17385
18021
  const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
@@ -17477,6 +18113,46 @@ async function fetchUrlToBytes(url) {
17477
18113
  }
17478
18114
  }
17479
18115
 
18116
+ // src/core/utils/theme-override-utils.ts
18117
+ var COLOR_MAP_ALIAS_KEYS = [
18118
+ "bg1",
18119
+ "tx1",
18120
+ "bg2",
18121
+ "tx2",
18122
+ "accent1",
18123
+ "accent2",
18124
+ "accent3",
18125
+ "accent4",
18126
+ "accent5",
18127
+ "accent6",
18128
+ "hlink",
18129
+ "folHlink"
18130
+ ];
18131
+ var DEFAULT_COLOR_MAP = {
18132
+ bg1: "lt1",
18133
+ tx1: "dk1",
18134
+ bg2: "lt2",
18135
+ tx2: "dk2",
18136
+ accent1: "accent1",
18137
+ accent2: "accent2",
18138
+ accent3: "accent3",
18139
+ accent4: "accent4",
18140
+ accent5: "accent5",
18141
+ accent6: "accent6",
18142
+ hlink: "hlink",
18143
+ folHlink: "folHlink"
18144
+ };
18145
+ function buildClrMapOverrideXml(override) {
18146
+ if (!override || Object.keys(override).length === 0) {
18147
+ return { "a:masterClrMapping": {} };
18148
+ }
18149
+ const attrs2 = {};
18150
+ for (const key of COLOR_MAP_ALIAS_KEYS) {
18151
+ attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
18152
+ }
18153
+ return { "a:overrideClrMapping": attrs2 };
18154
+ }
18155
+
17480
18156
  // src/core/utils/encryption-detection.ts
17481
18157
  var OLE_MAGIC = new Uint8Array([208, 207, 17, 224, 161, 27, 26, 225]);
17482
18158
  var ZIP_MAGIC = new Uint8Array([80, 75]);
@@ -23239,7 +23915,7 @@ function parseActiveXControlsFromSlide(slideXml2) {
23239
23915
  }
23240
23916
 
23241
23917
  // src/core/utils/theme-switching.ts
23242
- function normalizeHex(hex) {
23918
+ function normalizeHex2(hex) {
23243
23919
  if (!hex) {
23244
23920
  return "";
23245
23921
  }
@@ -23249,8 +23925,8 @@ function buildColorRemapTable(oldColorMap, newColorMap) {
23249
23925
  const remap = /* @__PURE__ */ new Map();
23250
23926
  const allKeys = [...THEME_COLOR_SCHEME_KEYS, "tx1", "bg1", "tx2", "bg2"];
23251
23927
  for (const key of allKeys) {
23252
- const oldVal = normalizeHex(oldColorMap[key]);
23253
- const newVal = normalizeHex(newColorMap[key]);
23928
+ const oldVal = normalizeHex2(oldColorMap[key]);
23929
+ const newVal = normalizeHex2(newColorMap[key]);
23254
23930
  if (oldVal && newVal && oldVal !== newVal) {
23255
23931
  remap.set(oldVal, `#${newVal}`);
23256
23932
  remap.set(`#${oldVal}`, `#${newVal}`);
@@ -23262,7 +23938,7 @@ function remapColor(color, remap) {
23262
23938
  if (!color) {
23263
23939
  return color;
23264
23940
  }
23265
- const normalized = normalizeHex(color);
23941
+ const normalized = normalizeHex2(color);
23266
23942
  const remapped = remap.get(normalized) ?? remap.get(`#${normalized}`);
23267
23943
  return remapped ?? color;
23268
23944
  }
@@ -23402,7 +24078,7 @@ function remapSlideColors(slide, remap) {
23402
24078
  function buildThemeColorMap(colorScheme) {
23403
24079
  const map = {};
23404
24080
  for (const key of THEME_COLOR_SCHEME_KEYS) {
23405
- map[key] = normalizeHex(colorScheme[key]);
24081
+ map[key] = normalizeHex2(colorScheme[key]);
23406
24082
  }
23407
24083
  map.tx1 = map.dk1;
23408
24084
  map.bg1 = map.lt1;
@@ -23438,8 +24114,11 @@ function applyThemeToData(data, newColorScheme, newFontScheme, themeName) {
23438
24114
 
23439
24115
  // src/core/core/runtime/PptxHandlerRuntimeSaveParagraphHelpers.ts
23440
24116
  var EMU_PER_PX4 = 9525;
23441
- function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing) {
24117
+ function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing, level) {
23442
24118
  const paragraphProps = {};
24119
+ if (typeof level === "number" && Number.isFinite(level) && level > 0) {
24120
+ paragraphProps["@_lvl"] = String(Math.min(Math.max(Math.round(level), 0), 8));
24121
+ }
23443
24122
  if (paragraphAlign) {
23444
24123
  paragraphProps["@_algn"] = paragraphAlign;
23445
24124
  }
@@ -23511,23 +24190,28 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23511
24190
  paragraphProps["a:buNone"] = {};
23512
24191
  return;
23513
24192
  }
23514
- if (bulletInfo.color) {
24193
+ if (bulletInfo.colorInherit) {
24194
+ paragraphProps["a:buClrTx"] = {};
24195
+ } else if (bulletInfo.color) {
23515
24196
  const colorHex = bulletInfo.color.replace("#", "");
23516
24197
  paragraphProps["a:buClr"] = {
23517
24198
  "a:srgbClr": { "@_val": colorHex }
23518
24199
  };
23519
24200
  }
23520
- if (bulletInfo.sizePercent !== void 0) {
24201
+ if (bulletInfo.sizeInherit) {
24202
+ paragraphProps["a:buSzTx"] = {};
24203
+ } else if (bulletInfo.sizePercent !== void 0) {
23521
24204
  paragraphProps["a:buSzPct"] = {
23522
24205
  "@_val": String(Math.round(bulletInfo.sizePercent * 1e3))
23523
24206
  };
23524
- }
23525
- if (bulletInfo.sizePts !== void 0) {
24207
+ } else if (bulletInfo.sizePts !== void 0) {
23526
24208
  paragraphProps["a:buSzPts"] = {
23527
24209
  "@_val": String(Math.round(bulletInfo.sizePts * 100))
23528
24210
  };
23529
24211
  }
23530
- if (bulletInfo.fontFamily) {
24212
+ if (bulletInfo.fontInherit) {
24213
+ paragraphProps["a:buFontTx"] = {};
24214
+ } else if (bulletInfo.fontFamily) {
23531
24215
  paragraphProps["a:buFont"] = {
23532
24216
  "@_typeface": bulletInfo.fontFamily
23533
24217
  };
@@ -23550,7 +24234,7 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23550
24234
  };
23551
24235
  }
23552
24236
  }
23553
- function assembleParagraphXml(runs, paragraphProps) {
24237
+ function assembleParagraphXml(runs, paragraphProps, endParaRunProperties) {
23554
24238
  const paragraph = {
23555
24239
  "a:pPr": paragraphProps
23556
24240
  };
@@ -23572,7 +24256,11 @@ function assembleParagraphXml(runs, paragraphProps) {
23572
24256
  if (cleanRegularRuns.length === 0 && fieldRuns.length === 0) {
23573
24257
  paragraph["a:r"] = runs.length > 1 ? runs : runs[0];
23574
24258
  }
23575
- paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
24259
+ if (endParaRunProperties && typeof endParaRunProperties === "object") {
24260
+ paragraph["a:endParaRPr"] = endParaRunProperties;
24261
+ } else {
24262
+ paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
24263
+ }
23576
24264
  return paragraph;
23577
24265
  }
23578
24266
  function computeUniformSegmentOverrides(textStyle, textSegments) {
@@ -23876,6 +24564,140 @@ var PptxHandlerRuntime = class {
23876
24564
  * `p:clrMapOvr / a:overrideClrMapping`.
23877
24565
  */
23878
24566
  currentSlideClrMapOverride = null;
24567
+ /**
24568
+ * Per-master colour map alias dictionaries parsed from each master's
24569
+ * `<p:clrMap>` element (e.g. `bg1 → lt1`, `tx1 → dk1`, `accent1 → accent1`).
24570
+ *
24571
+ * `clrMap` is the *aliasing* layer between logical colour names used in
24572
+ * DrawingML and the raw theme scheme slots. Per ECMA-376 §19.3.1.7 it
24573
+ * lives on each `p:sldMaster`, and slide layouts/slides may further
24574
+ * override it via `p:clrMapOvr`. Resolution must happen at colour-lookup
24575
+ * time, *not* by baking it into {@link themeColorMap}.
24576
+ *
24577
+ * Phase 2 Stream B / C-H4.
24578
+ */
24579
+ masterClrMaps = /* @__PURE__ */ new Map();
24580
+ /**
24581
+ * Per-master theme color maps. Each master may reference its own theme
24582
+ * file via `_rels/slideMasterN.xml.rels`. For multi-master decks, slides
24583
+ * must resolve scheme colours against their *own* master's theme.
24584
+ *
24585
+ * Falls back to {@link themeColorMap} when a master entry is missing.
24586
+ *
24587
+ * Phase 2 Stream B / C-H4.
24588
+ */
24589
+ masterThemeColorMaps = /* @__PURE__ */ new Map();
24590
+ /**
24591
+ * Per-master theme font maps. Same rationale as
24592
+ * {@link masterThemeColorMaps}: multi-master decks may have one font
24593
+ * scheme per theme.
24594
+ */
24595
+ masterThemeFontMaps = /* @__PURE__ */ new Map();
24596
+ /**
24597
+ * Per-master format schemes (fmtScheme). For multi-master decks each
24598
+ * master's slides should resolve fill/line/effect refs against the
24599
+ * matrix from that master's theme.
24600
+ */
24601
+ masterThemeFormatSchemes = /* @__PURE__ */ new Map();
24602
+ /**
24603
+ * Per-master mapping from slide-master path to the theme path it
24604
+ * references via `_rels/slideMasterN.xml.rels`. Populated by
24605
+ * {@link loadPerMasterThemes} during load. Used by the save-side
24606
+ * theme writer to know which themeN.xml to (re)emit for each master.
24607
+ *
24608
+ * Phase 4 Stream A / C-H3.
24609
+ */
24610
+ masterThemePaths = /* @__PURE__ */ new Map();
24611
+ /**
24612
+ * Per-script font tables for major and minor fonts. Captured per master
24613
+ * theme. Keys are master paths; values map `mj`/`mn` -> script tag (e.g.
24614
+ * `Hans`, `Hant`, `Arab`, `Hebr`, `Thai`, `Beng`, …) -> typeface name.
24615
+ *
24616
+ * Phase 4 Stream A / M4.
24617
+ */
24618
+ masterThemeMajorFontScripts = /* @__PURE__ */ new Map();
24619
+ masterThemeMinorFontScripts = /* @__PURE__ */ new Map();
24620
+ /**
24621
+ * Theme name attribute (`<a:theme @name>`) per master theme path.
24622
+ * Captured for byte-stable round-trip.
24623
+ */
24624
+ masterThemeNames = /* @__PURE__ */ new Map();
24625
+ /**
24626
+ * `<a:fontScheme @name>` per master theme path.
24627
+ */
24628
+ masterThemeFontSchemeNames = /* @__PURE__ */ new Map();
24629
+ /**
24630
+ * `<a:clrScheme @name>` per master theme path.
24631
+ */
24632
+ masterThemeColorSchemeNames = /* @__PURE__ */ new Map();
24633
+ /**
24634
+ * Raw original theme XML keyed by theme path. Captured at load-time.
24635
+ * Used by the save pipeline to passthrough the full theme XML when no
24636
+ * in-memory mutation has occurred — preserving fillStyleLst /
24637
+ * lnStyleLst / effectStyleLst / bgFillStyleLst /
24638
+ * extraClrSchemeLst / objectDefaults / extLst exactly as written.
24639
+ *
24640
+ * Phase 4 Stream A / C-H3.
24641
+ */
24642
+ originalThemeXmlByPath = /* @__PURE__ */ new Map();
24643
+ /**
24644
+ * Set of theme paths whose in-memory state has been mutated since
24645
+ * load. Saving a theme path that's NOT dirty is a no-op (the original
24646
+ * file already exists in the ZIP). Saving a dirty theme path
24647
+ * regenerates the part from in-memory state.
24648
+ *
24649
+ * Phase 4 Stream A / C-H3.
24650
+ */
24651
+ dirtyThemePaths = /* @__PURE__ */ new Set();
24652
+ /**
24653
+ * Per-master parsed `<p:txStyles>` (titleStyle/bodyStyle/otherStyle).
24654
+ * Populated by {@link enrichSlideMastersWithTxStyles} during load so the
24655
+ * inheritance chain can find the master text-style cascade without
24656
+ * re-parsing master XML on every lookup. Phase 4 Stream B / P-H1.
24657
+ */
24658
+ masterTxStylesCache = /* @__PURE__ */ new Map();
24659
+ /**
24660
+ * Captured `<a:objectDefaults>` snapshot per master theme path. The
24661
+ * full ECMA-376 inheritance chain (master / layout / placeholder /
24662
+ * objectDefaults) is non-trivial; we store the raw spDef/lnDef/txDef
24663
+ * subtrees and re-emit them verbatim for round-trip.
24664
+ *
24665
+ * Phase 4 Stream A / M5.
24666
+ */
24667
+ masterThemeObjectDefaults = /* @__PURE__ */ new Map();
24668
+ /**
24669
+ * Captured `<a:extraClrSchemeLst>` raw subtree per master theme path
24670
+ * for verbatim round-trip.
24671
+ *
24672
+ * Phase 4 Stream A.
24673
+ */
24674
+ masterThemeExtraClrSchemeLst = /* @__PURE__ */ new Map();
24675
+ /**
24676
+ * Captured `<a:custClrLst>` raw subtree per master theme path
24677
+ * for verbatim round-trip.
24678
+ *
24679
+ * Phase 4 Stream A.
24680
+ */
24681
+ masterThemeCustClrLst = /* @__PURE__ */ new Map();
24682
+ /**
24683
+ * Captured theme-level `<a:extLst>` raw subtree per master theme path.
24684
+ */
24685
+ masterThemeExtLst = /* @__PURE__ */ new Map();
24686
+ /**
24687
+ * Active master's clrMap for the slide currently being parsed. Walked
24688
+ * after `currentSlideClrMapOverride` (slide and layout overrides take
24689
+ * precedence). `null` means "fall through to themeColorMap directly".
24690
+ */
24691
+ currentMasterClrMap = null;
24692
+ /**
24693
+ * Snapshot of the global theme state taken right after
24694
+ * {@link loadThemeData} completes. Used as the fallback when a slide's
24695
+ * master has no per-master theme entry, so per-slide multi-master
24696
+ * switching does not leak the previous slide's master state.
24697
+ */
24698
+ globalThemeColorMapSnapshot = {};
24699
+ globalThemeFontMapSnapshot = {};
24700
+ globalThemeFormatSchemeSnapshot;
23879
24701
  /** Thumbnail image data from `docProps/thumbnail.jpeg` preserved for round-trip. */
23880
24702
  thumbnailData = null;
23881
24703
  /** Raw VBA project binary preserved for macro-enabled (.pptm) round-trip. */
@@ -23886,6 +24708,19 @@ var PptxHandlerRuntime = class {
23886
24708
  signatureDetection = null;
23887
24709
  /** Custom XML data parts parsed from `customXml/` in the OPC package. */
23888
24710
  customXmlParts = [];
24711
+ /**
24712
+ * Maps an element's `rawXml` reference to the `mc:AlternateContent`
24713
+ * envelope that originally wrapped it (CC-4). Populated during slide
24714
+ * (and `p:grpSp`) parsing; consulted at save time to re-emit the
24715
+ * original `<mc:Choice>` / `<mc:Fallback>` shape so legacy renderers
24716
+ * keep their fallback content.
24717
+ *
24718
+ * Multiple sibling elements may share the same `AlternateContentBlock`
24719
+ * value (a single AC envelope often wraps several child shapes — e.g.
24720
+ * `p14:media` + its `p:pic` fallback nest one each). WeakMap so AC
24721
+ * envelopes are GC'd if the parsed XmlObject is dropped.
24722
+ */
24723
+ alternateContentBlockByRawXml = /* @__PURE__ */ new WeakMap();
23889
24724
  /** Embedded fonts extracted during load, preserved for automatic re-embedding on save. */
23890
24725
  loadedEmbeddedFonts = [];
23891
24726
  /** Map of comment author IDs to display names (from `ppt/commentAuthors.xml`). */
@@ -25699,7 +26534,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25699
26534
  const bgColor = this.parseBackgroundColor(bg);
25700
26535
  const spTree = master["p:cSld"]?.["p:spTree"];
25701
26536
  const placeholders = this.extractPlaceholderList(spTree);
25702
- return { path: path2, backgroundColor: bgColor, placeholders };
26537
+ const result = { path: path2, backgroundColor: bgColor, placeholders };
26538
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
26539
+ if (hf) {
26540
+ result.headerFooter = hf;
26541
+ }
26542
+ return result;
25703
26543
  } catch (e) {
25704
26544
  console.warn("Failed to parse handout master:", e);
25705
26545
  return void 0;
@@ -25725,7 +26565,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25725
26565
  const bgColor = this.parseBackgroundColor(bg);
25726
26566
  const spTree = master["p:cSld"]?.["p:spTree"];
25727
26567
  const placeholders = this.extractPlaceholderList(spTree);
25728
- return { path: path2, backgroundColor: bgColor, placeholders };
26568
+ const result = { path: path2, backgroundColor: bgColor, placeholders };
26569
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
26570
+ if (hf) {
26571
+ result.headerFooter = hf;
26572
+ }
26573
+ return result;
25729
26574
  } catch (e) {
25730
26575
  console.warn("Failed to parse notes master:", e);
25731
26576
  return void 0;
@@ -25770,6 +26615,10 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25770
26615
  const uVal = String(userDrawn).trim().toLowerCase();
25771
26616
  layout.userDrawn = uVal === "1" || uVal === "true";
25772
26617
  }
26618
+ const hf = parseHeaderFooterFlags(sldLayout["p:hf"]);
26619
+ if (hf) {
26620
+ layout.headerFooter = hf;
26621
+ }
25773
26622
  const clrMapOvr = sldLayout["p:clrMapOvr"];
25774
26623
  if (clrMapOvr && clrMapOvr["a:masterClrMapping"] === void 0) {
25775
26624
  const overrideNode = clrMapOvr["a:overrideClrMapping"];
@@ -26375,6 +27224,18 @@ function buildClrChangeNode(style) {
26375
27224
  }
26376
27225
 
26377
27226
  // src/core/core/runtime/PptxHandlerRuntimeSaveRunProperties.ts
27227
+ function applyFontMetadata(fontNode, panose, pitchFamily, charset) {
27228
+ if (panose && panose.length > 0) {
27229
+ fontNode["@_panose"] = panose;
27230
+ }
27231
+ if (typeof pitchFamily === "number" && Number.isFinite(pitchFamily)) {
27232
+ fontNode["@_pitchFamily"] = String(pitchFamily);
27233
+ }
27234
+ if (typeof charset === "number" && Number.isFinite(charset)) {
27235
+ fontNode["@_charset"] = String(charset);
27236
+ }
27237
+ return fontNode;
27238
+ }
26378
27239
  var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime11 {
26379
27240
  createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId) {
26380
27241
  const runProps = {
@@ -26432,6 +27293,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26432
27293
  if (style.bookmark) {
26433
27294
  runProps["@_bmk"] = style.bookmark;
26434
27295
  }
27296
+ if (style.altLanguage) {
27297
+ runProps["@_altLang"] = style.altLanguage;
27298
+ }
27299
+ if (typeof style.smartTagId === "number" && Number.isFinite(style.smartTagId)) {
27300
+ runProps["@_smtId"] = String(style.smartTagId);
27301
+ }
26435
27302
  if (style.textOutlineWidth || style.textOutlineColor) {
26436
27303
  const lnObj = {};
26437
27304
  if (typeof style.textOutlineWidth === "number" && style.textOutlineWidth > 0) {
@@ -26447,11 +27314,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26447
27314
  runProps["a:ln"] = lnObj;
26448
27315
  }
26449
27316
  if (style.color) {
26450
- runProps["a:solidFill"] = {
26451
- "a:srgbClr": {
26452
- "@_val": style.color.replace("#", "")
26453
- }
26454
- };
27317
+ const resolvedOriginalColor = style.colorXml ? this.parseColor(style.colorXml) : void 0;
27318
+ runProps["a:solidFill"] = serializeColorChoice(
27319
+ style.colorXml,
27320
+ resolvedOriginalColor,
27321
+ style.color
27322
+ );
26455
27323
  } else if (style.textFillGradientStops && style.textFillGradientStops.length > 0) {
26456
27324
  const gradStops = style.textFillGradientStops.filter((stop) => Boolean(stop?.color)).map((stop) => {
26457
27325
  const rawPos = (stop.position ?? 0) / 100;
@@ -26524,16 +27392,32 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26524
27392
  };
26525
27393
  }
26526
27394
  if (style.fontFamily) {
26527
- runProps["a:latin"] = { "@_typeface": style.fontFamily };
26528
- runProps["a:ea"] = {
26529
- "@_typeface": style.eastAsiaFont || style.fontFamily
26530
- };
26531
- runProps["a:cs"] = {
26532
- "@_typeface": style.complexScriptFont || style.fontFamily
26533
- };
27395
+ runProps["a:latin"] = applyFontMetadata(
27396
+ { "@_typeface": style.fontFamily },
27397
+ style.latinFontPanose,
27398
+ style.latinFontPitchFamily,
27399
+ style.latinFontCharset
27400
+ );
27401
+ runProps["a:ea"] = applyFontMetadata(
27402
+ { "@_typeface": style.eastAsiaFont || style.fontFamily },
27403
+ style.eastAsiaFontPanose,
27404
+ style.eastAsiaFontPitchFamily,
27405
+ style.eastAsiaFontCharset
27406
+ );
27407
+ runProps["a:cs"] = applyFontMetadata(
27408
+ { "@_typeface": style.complexScriptFont || style.fontFamily },
27409
+ style.complexScriptFontPanose,
27410
+ style.complexScriptFontPitchFamily,
27411
+ style.complexScriptFontCharset
27412
+ );
26534
27413
  }
26535
27414
  if (style.symbolFont) {
26536
- runProps["a:sym"] = { "@_typeface": style.symbolFont };
27415
+ runProps["a:sym"] = applyFontMetadata(
27416
+ { "@_typeface": style.symbolFont },
27417
+ style.symbolFontPanose,
27418
+ style.symbolFontPitchFamily,
27419
+ style.symbolFontCharset
27420
+ );
26537
27421
  }
26538
27422
  if (style.hyperlink && resolveHyperlinkRelationshipId) {
26539
27423
  const hyperlinkTarget = String(style.hyperlink).trim();
@@ -26608,14 +27492,15 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26608
27492
  lineSpacing: this.createLineSpacingXmlFromMultiplier(textStyle?.lineSpacing),
26609
27493
  lineSpacingExactPt: textStyle?.lineSpacingExactPt
26610
27494
  };
26611
- const createParagraph = (runs, bulletInfo) => {
27495
+ const createParagraph = (runs, bulletInfo, level, endParaRunProperties) => {
26612
27496
  const paragraphProps = buildParagraphPropertiesXml(
26613
27497
  textStyle,
26614
27498
  paragraphAlign,
26615
27499
  bulletInfo,
26616
- spacing
27500
+ spacing,
27501
+ level
26617
27502
  );
26618
- return assembleParagraphXml(runs, paragraphProps);
27503
+ return assembleParagraphXml(runs, paragraphProps, endParaRunProperties);
26619
27504
  };
26620
27505
  const createRun = (runText, style) => ({
26621
27506
  "a:rPr": this.createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId),
@@ -26663,13 +27548,19 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26663
27548
  const paragraphs = [];
26664
27549
  let currentRuns = [];
26665
27550
  let currentBulletInfo;
27551
+ let currentLevel;
27552
+ let currentEndParaRunProperties;
26666
27553
  const pushParagraph = () => {
26667
27554
  if (currentRuns.length === 0) {
26668
27555
  currentRuns.push(createRun("", textStyle));
26669
27556
  }
26670
- paragraphs.push(createParagraph(currentRuns, currentBulletInfo));
27557
+ paragraphs.push(
27558
+ createParagraph(currentRuns, currentBulletInfo, currentLevel, currentEndParaRunProperties)
27559
+ );
26671
27560
  currentRuns = [];
26672
27561
  currentBulletInfo = void 0;
27562
+ currentLevel = void 0;
27563
+ currentEndParaRunProperties = void 0;
26673
27564
  };
26674
27565
  if (textSegments && textSegments.length > 0) {
26675
27566
  const uniformSegmentOverrides = computeUniformSegmentOverrides(textStyle, textSegments);
@@ -26681,8 +27572,16 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26681
27572
  };
26682
27573
  const segmentText = String(segment.text ?? "");
26683
27574
  const lineParts = segmentText.split("\n");
26684
- if (currentRuns.length === 0 && segment.bulletInfo) {
26685
- currentBulletInfo = segment.bulletInfo;
27575
+ if (currentRuns.length === 0) {
27576
+ if (segment.bulletInfo) {
27577
+ currentBulletInfo = segment.bulletInfo;
27578
+ }
27579
+ if (segment.paragraphLevel !== void 0) {
27580
+ currentLevel = segment.paragraphLevel;
27581
+ }
27582
+ if (segment.endParaRunProperties) {
27583
+ currentEndParaRunProperties = segment.endParaRunProperties;
27584
+ }
26686
27585
  }
26687
27586
  lineParts.forEach((linePart, lineIndex) => {
26688
27587
  if (segment.rubyText !== void 0) {
@@ -26780,7 +27679,201 @@ var PptxHandlerRuntime14 = class extends PptxHandlerRuntime13 {
26780
27679
  };
26781
27680
 
26782
27681
  // src/core/core/runtime/PptxHandlerRuntimeSaveShapeXml.ts
27682
+ var OLE_OBJECT_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject";
27683
+ var IMAGE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
27684
+ var OLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/presentationml/2006/ole";
26783
27685
  var PptxHandlerRuntime15 = class _PptxHandlerRuntime extends PptxHandlerRuntime14 {
27686
+ /**
27687
+ * Build a `p:graphicFrame` XML skeleton for an SDK-created table.
27688
+ *
27689
+ * Tables round-trip as `<p:graphicFrame>/<a:graphic>/<a:graphicData
27690
+ * uri=".../drawingml/2006/table">/<a:tbl>` inside `p:spTree`. When the
27691
+ * element was loaded from an existing file, `el.rawXml` already contains
27692
+ * this envelope and the downstream `serializeTableDataToXml` path
27693
+ * populates cells in place. When the element was created via the SDK
27694
+ * (`SlideBuilder.addTable`), there is no `rawXml`, so this method
27695
+ * fabricates a minimal envelope with an empty `a:tbl`. The element
27696
+ * writer then calls `serializeTableDataToXml`, which triggers
27697
+ * `rebuildTableXmlFromData` and fills in `a:tblGrid` / `a:tr` children.
27698
+ */
27699
+ createTableGraphicFrameXml(el) {
27700
+ const EMU = _PptxHandlerRuntime.EMU_PER_PX;
27701
+ const offX = String(Math.round(el.x * EMU));
27702
+ const offY = String(Math.round(el.y * EMU));
27703
+ const extCx = String(Math.round(Math.max(el.width, 1) * EMU));
27704
+ const extCy = String(Math.round(Math.max(el.height, 1) * EMU));
27705
+ const tblPr = {
27706
+ "@_firstRow": el.tableData?.firstRowHeader ? "1" : "0",
27707
+ "@_bandRow": el.tableData?.bandedRows ? "1" : "0"
27708
+ };
27709
+ if (el.tableData?.tableStyleId) {
27710
+ tblPr["a:tableStyleId"] = el.tableData.tableStyleId;
27711
+ }
27712
+ return {
27713
+ "p:nvGraphicFramePr": {
27714
+ "p:cNvPr": { "@_id": "0", "@_name": el.name || "Table" },
27715
+ "p:cNvGraphicFramePr": {
27716
+ "a:graphicFrameLocks": { "@_noGrp": "1" }
27717
+ },
27718
+ "p:nvPr": {}
27719
+ },
27720
+ "p:xfrm": {
27721
+ "a:off": { "@_x": offX, "@_y": offY },
27722
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27723
+ },
27724
+ "a:graphic": {
27725
+ "a:graphicData": {
27726
+ "@_uri": "http://schemas.openxmlformats.org/drawingml/2006/table",
27727
+ "a:tbl": {
27728
+ "a:tblPr": tblPr,
27729
+ "a:tblGrid": {}
27730
+ }
27731
+ }
27732
+ }
27733
+ };
27734
+ }
27735
+ /**
27736
+ * Build a `p:graphicFrame` XML skeleton for an OLE object element.
27737
+ *
27738
+ * Used both for SDK-created OLE elements (no `rawXml`) and to refresh
27739
+ * a few key attributes on a loaded element when the typed fields have
27740
+ * been mutated. The output is the canonical
27741
+ * `p:graphicFrame > a:graphic > a:graphicData uri="…/ole" > p:oleObj`
27742
+ * shape per ECMA-376 §19.3.1.34 / §13.3.4.
27743
+ *
27744
+ * The caller (`processSlideElement`) is responsible for ensuring the
27745
+ * embed / preview-image relationships referenced from `r:id` / `r:embed`
27746
+ * exist in the slide's rels file. This method does not register them
27747
+ * itself because the typed model does not currently carry the binary
27748
+ * payload — the binary part must already be in the package (loaded from
27749
+ * the original file). A fully-fabricated SDK OLE element therefore
27750
+ * still requires the consumer to attach the binary out-of-band; this
27751
+ * method simply emits a schema-valid envelope referencing the
27752
+ * specified relationship ID.
27753
+ */
27754
+ createOleGraphicFrameXml(el, embedRelationshipId) {
27755
+ const EMU = _PptxHandlerRuntime.EMU_PER_PX;
27756
+ const offX = String(Math.round(el.x * EMU));
27757
+ const offY = String(Math.round(el.y * EMU));
27758
+ const extCx = String(Math.round(Math.max(el.width, 1) * EMU));
27759
+ const extCy = String(Math.round(Math.max(el.height, 1) * EMU));
27760
+ const oleObj = {
27761
+ "@_showAsIcon": "0",
27762
+ "@_imgW": extCx,
27763
+ "@_imgH": extCy
27764
+ };
27765
+ if (el.oleProgId) {
27766
+ oleObj["@_progId"] = el.oleProgId;
27767
+ }
27768
+ if (el.oleName) {
27769
+ oleObj["@_name"] = el.oleName;
27770
+ }
27771
+ if (el.oleClsId) {
27772
+ oleObj["@_classid"] = el.oleClsId;
27773
+ }
27774
+ if (embedRelationshipId) {
27775
+ oleObj["@_r:id"] = embedRelationshipId;
27776
+ }
27777
+ if (el.isLinked) {
27778
+ oleObj["p:link"] = {
27779
+ "@_r:id": embedRelationshipId,
27780
+ "@_updateAutomatic": "1"
27781
+ };
27782
+ } else {
27783
+ oleObj["p:embed"] = {};
27784
+ }
27785
+ oleObj["p:pic"] = {
27786
+ "p:nvPicPr": {
27787
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || "OleObject" },
27788
+ "p:cNvPicPr": {},
27789
+ "p:nvPr": {}
27790
+ },
27791
+ "p:blipFill": {
27792
+ "a:blip": {},
27793
+ "a:stretch": { "a:fillRect": {} }
27794
+ },
27795
+ "p:spPr": {
27796
+ "a:xfrm": {
27797
+ "a:off": { "@_x": offX, "@_y": offY },
27798
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27799
+ },
27800
+ "a:prstGeom": { "@_prst": "rect", "a:avLst": {} }
27801
+ }
27802
+ };
27803
+ return {
27804
+ "p:nvGraphicFramePr": {
27805
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || el.fileName || "OleObject" },
27806
+ "p:cNvGraphicFramePr": {
27807
+ "a:graphicFrameLocks": { "@_noChangeAspect": "1" }
27808
+ },
27809
+ "p:nvPr": {}
27810
+ },
27811
+ "p:xfrm": {
27812
+ "a:off": { "@_x": offX, "@_y": offY },
27813
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27814
+ },
27815
+ "a:graphic": {
27816
+ "a:graphicData": {
27817
+ "@_uri": OLE_GRAPHIC_DATA_URI,
27818
+ "p:oleObj": oleObj
27819
+ }
27820
+ }
27821
+ };
27822
+ }
27823
+ /**
27824
+ * Refresh editable typed-field attributes on a loaded OLE graphicFrame's
27825
+ * raw XML. Only attributes that round-trip through the typed model
27826
+ * (`progId`, `name`, `classid`) are touched so unknown extension data
27827
+ * passes through verbatim.
27828
+ */
27829
+ applyOleTypedFieldUpdates(shape, el) {
27830
+ const oleObj = shape["a:graphic"]?.["a:graphicData"]?.["p:oleObj"];
27831
+ if (!oleObj) {
27832
+ return;
27833
+ }
27834
+ if (el.oleProgId) {
27835
+ oleObj["@_progId"] = el.oleProgId;
27836
+ }
27837
+ if (el.oleName !== void 0) {
27838
+ if (el.oleName.length > 0) {
27839
+ oleObj["@_name"] = el.oleName;
27840
+ } else {
27841
+ delete oleObj["@_name"];
27842
+ }
27843
+ }
27844
+ if (el.oleClsId) {
27845
+ oleObj["@_classid"] = el.oleClsId;
27846
+ }
27847
+ }
27848
+ /** Look up the existing OLE binary relationship ID for this slide, if any. */
27849
+ resolveOleEmbedRelationshipId(slideRelationships, oleTarget) {
27850
+ if (!oleTarget) {
27851
+ return void 0;
27852
+ }
27853
+ const normalisedTarget = oleTarget.replace(/^ppt\//, "../").replace(/^\/+/, "");
27854
+ const lowerTarget = normalisedTarget.toLowerCase();
27855
+ for (const rel of slideRelationships) {
27856
+ const relType = String(rel?.["@_Type"] || "");
27857
+ if (relType !== OLE_OBJECT_RELATIONSHIP_TYPE) {
27858
+ continue;
27859
+ }
27860
+ const target = String(rel?.["@_Target"] || "").toLowerCase().trim();
27861
+ if (target === lowerTarget || target.endsWith(lowerTarget) || lowerTarget.endsWith(target)) {
27862
+ const relId = String(rel?.["@_Id"] || "").trim();
27863
+ if (relId.length > 0) {
27864
+ return relId;
27865
+ }
27866
+ }
27867
+ }
27868
+ const fallback = slideRelationships.find(
27869
+ (rel) => String(rel?.["@_Type"] || "") === OLE_OBJECT_RELATIONSHIP_TYPE
27870
+ );
27871
+ const fallbackId = String(fallback?.["@_Id"] || "").trim();
27872
+ return fallbackId.length > 0 ? fallbackId : void 0;
27873
+ }
27874
+ /** Constants are exposed so the element-writer mixin can reuse them. */
27875
+ static OLE_OBJECT_RELATIONSHIP_TYPE = OLE_OBJECT_RELATIONSHIP_TYPE;
27876
+ static OLE_IMAGE_RELATIONSHIP_TYPE = IMAGE_RELATIONSHIP_TYPE;
26784
27877
  /**
26785
27878
  * Build a p:sp XML object for an ink annotation element.
26786
27879
  * Each ink path becomes a separate a:path within a:pathLst,
@@ -27097,6 +28190,9 @@ var PptxHandlerRuntime16 = class extends PptxHandlerRuntime15 {
27097
28190
  buildOuterShadowXml(shapeStyle) {
27098
28191
  return this.colorStyleCodec.buildOuterShadowXml(shapeStyle);
27099
28192
  }
28193
+ buildPresetShadowXml(shapeStyle) {
28194
+ return this.colorStyleCodec.buildPresetShadowXml(shapeStyle);
28195
+ }
27100
28196
  buildInnerShadowXml(shapeStyle) {
27101
28197
  return this.colorStyleCodec.buildInnerShadowXml(shapeStyle);
27102
28198
  }
@@ -27647,6 +28743,10 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27647
28743
  const solidFill = runProperties["a:solidFill"];
27648
28744
  if (solidFill) {
27649
28745
  style.color = this.parseColor(solidFill);
28746
+ const colorXml = extractColorChoiceXml(solidFill);
28747
+ if (colorXml) {
28748
+ style.colorXml = colorXml;
28749
+ }
27650
28750
  }
27651
28751
  this.applyHyperlinkStyle(style, runProperties, relationshipMap);
27652
28752
  const capAttr = String(runProperties["@_cap"] || "").trim().toLowerCase();
@@ -27694,12 +28794,86 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27694
28794
  if (bmk) {
27695
28795
  style.bookmark = bmk;
27696
28796
  }
28797
+ const altLang = String(runProperties["@_altLang"] || "").trim();
28798
+ if (altLang) {
28799
+ style.altLanguage = altLang;
28800
+ }
28801
+ if (runProperties["@_smtId"] !== void 0) {
28802
+ const smtIdRaw = Number.parseInt(String(runProperties["@_smtId"]), 10);
28803
+ if (Number.isFinite(smtIdRaw)) {
28804
+ style.smartTagId = smtIdRaw;
28805
+ }
28806
+ }
28807
+ this.applyTextFontMetadata(style, latin, "latin");
28808
+ this.applyTextFontMetadata(style, eastAsian, "eastAsia");
28809
+ this.applyTextFontMetadata(style, complexScript, "complexScript");
28810
+ this.applyTextFontMetadata(style, runProperties["a:sym"], "symbol");
27697
28811
  const runEffectList = runProperties["a:effectLst"];
27698
28812
  if (runEffectList) {
27699
28813
  this.applyTextRunEffects(style, runEffectList);
27700
28814
  }
27701
28815
  return style;
27702
28816
  }
28817
+ /**
28818
+ * Copy `@panose` / `@pitchFamily` / `@charset` from a font child node
28819
+ * (`a:latin`, `a:ea`, `a:cs`, `a:sym`) onto the matching `*Font*`
28820
+ * fields of `style`.
28821
+ */
28822
+ applyTextFontMetadata(style, fontNode, kind) {
28823
+ if (!fontNode) {
28824
+ return;
28825
+ }
28826
+ const panose = String(fontNode["@_panose"] || "").trim();
28827
+ const pitchRaw = fontNode["@_pitchFamily"];
28828
+ const charsetRaw = fontNode["@_charset"];
28829
+ const pitch = pitchRaw !== void 0 && pitchRaw !== null ? Number.parseInt(String(pitchRaw), 10) : void 0;
28830
+ const charset = charsetRaw !== void 0 && charsetRaw !== null ? Number.parseInt(String(charsetRaw), 10) : void 0;
28831
+ if (kind === "latin") {
28832
+ if (panose) {
28833
+ style.latinFontPanose = panose;
28834
+ }
28835
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28836
+ style.latinFontPitchFamily = pitch;
28837
+ }
28838
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28839
+ style.latinFontCharset = charset;
28840
+ }
28841
+ return;
28842
+ }
28843
+ if (kind === "eastAsia") {
28844
+ if (panose) {
28845
+ style.eastAsiaFontPanose = panose;
28846
+ }
28847
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28848
+ style.eastAsiaFontPitchFamily = pitch;
28849
+ }
28850
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28851
+ style.eastAsiaFontCharset = charset;
28852
+ }
28853
+ return;
28854
+ }
28855
+ if (kind === "complexScript") {
28856
+ if (panose) {
28857
+ style.complexScriptFontPanose = panose;
28858
+ }
28859
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28860
+ style.complexScriptFontPitchFamily = pitch;
28861
+ }
28862
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28863
+ style.complexScriptFontCharset = charset;
28864
+ }
28865
+ return;
28866
+ }
28867
+ if (panose) {
28868
+ style.symbolFontPanose = panose;
28869
+ }
28870
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28871
+ style.symbolFontPitchFamily = pitch;
28872
+ }
28873
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28874
+ style.symbolFontCharset = charset;
28875
+ }
28876
+ }
27703
28877
  };
27704
28878
 
27705
28879
  // src/core/core/runtime/PptxHandlerRuntimeTextEditing.ts
@@ -28135,7 +29309,7 @@ var PptxHandlerRuntime21 = class extends PptxHandlerRuntime20 {
28135
29309
  };
28136
29310
 
28137
29311
  // src/core/core/runtime/table-cell-save-helpers.ts
28138
- function writeCellFill(tcPr, style) {
29312
+ function writeCellFill(tcPr, style, resolveColorXml) {
28139
29313
  if (style.fillMode === "gradient" && style.gradientFillStops && style.gradientFillStops.length > 0) {
28140
29314
  delete tcPr["a:solidFill"];
28141
29315
  delete tcPr["a:pattFill"];
@@ -28214,11 +29388,12 @@ function writeCellFill(tcPr, style) {
28214
29388
  } else if (style.backgroundColor) {
28215
29389
  delete tcPr["a:gradFill"];
28216
29390
  delete tcPr["a:pattFill"];
28217
- tcPr["a:solidFill"] = {
28218
- "a:srgbClr": {
28219
- "@_val": style.backgroundColor.replace("#", "")
28220
- }
28221
- };
29391
+ const resolvedOriginal = style.backgroundColorXml && resolveColorXml ? resolveColorXml(style.backgroundColorXml) : void 0;
29392
+ tcPr["a:solidFill"] = serializeColorChoice(
29393
+ style.backgroundColorXml,
29394
+ resolvedOriginal,
29395
+ style.backgroundColor
29396
+ );
28222
29397
  }
28223
29398
  }
28224
29399
  function writeDiagonalBorders(tcPr, style, emuPerPx) {
@@ -28262,14 +29437,15 @@ function writeCellTextFormatting(xmlCell, style, ensureArray6) {
28262
29437
  const paragraphs = ensureArray6(xmlCell["a:txBody"]?.["a:p"]);
28263
29438
  for (const paragraph of paragraphs) {
28264
29439
  const runs = ensureArray6(paragraph?.["a:r"]);
29440
+ const rebuiltRuns = [];
29441
+ let runsChanged = false;
28265
29442
  for (const run of runs) {
28266
29443
  if (!run) {
29444
+ rebuiltRuns.push(run);
28267
29445
  continue;
28268
29446
  }
28269
- if (!run["a:rPr"]) {
28270
- run["a:rPr"] = {};
28271
- }
28272
- const rPr = run["a:rPr"];
29447
+ const existingRPr = run["a:rPr"] ?? {};
29448
+ const rPr = { ...existingRPr };
28273
29449
  if (style.bold !== void 0) {
28274
29450
  rPr["@_b"] = style.bold ? "1" : "0";
28275
29451
  }
@@ -28289,6 +29465,18 @@ function writeCellTextFormatting(xmlCell, style, ensureArray6) {
28289
29465
  }
28290
29466
  };
28291
29467
  }
29468
+ const rebuilt = { "a:rPr": rPr };
29469
+ for (const key of Object.keys(run)) {
29470
+ if (key === "a:rPr") {
29471
+ continue;
29472
+ }
29473
+ rebuilt[key] = run[key];
29474
+ }
29475
+ rebuiltRuns.push(rebuilt);
29476
+ runsChanged = true;
29477
+ }
29478
+ if (runsChanged && rebuiltRuns.length > 0) {
29479
+ paragraph["a:r"] = rebuiltRuns.length === 1 ? rebuiltRuns[0] : rebuiltRuns;
28292
29480
  }
28293
29481
  }
28294
29482
  }
@@ -28310,6 +29498,9 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28310
29498
  const existingParagraphs = this.ensureArray(txBody["a:p"]);
28311
29499
  const firstRPr = this.ensureArray(existingParagraphs[0]?.["a:r"])[0]?.["a:rPr"];
28312
29500
  const firstPPr = existingParagraphs[0]?.["a:pPr"];
29501
+ const firstEndParaRPr = existingParagraphs[0]?.["a:endParaRPr"];
29502
+ const rPrForRun = firstRPr ? { ...firstRPr } : { "@_lang": "en-US", "@_dirty": "0" };
29503
+ const endParaRPr = firstEndParaRPr ? { ...firstEndParaRPr } : { "@_lang": "en-US", "@_dirty": "0" };
28313
29504
  const lines = text.split("\n");
28314
29505
  const paragraphs = lines.map((line) => {
28315
29506
  const paragraph = {};
@@ -28317,9 +29508,10 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28317
29508
  paragraph["a:pPr"] = firstPPr;
28318
29509
  }
28319
29510
  paragraph["a:r"] = {
28320
- ...firstRPr ? { "a:rPr": firstRPr } : {},
29511
+ "a:rPr": rPrForRun,
28321
29512
  "a:t": line
28322
29513
  };
29514
+ paragraph["a:endParaRPr"] = endParaRPr;
28323
29515
  return paragraph;
28324
29516
  });
28325
29517
  txBody["a:p"] = paragraphs.length === 1 ? paragraphs[0] : paragraphs;
@@ -28332,7 +29524,7 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28332
29524
  xmlCell["a:tcPr"] = {};
28333
29525
  }
28334
29526
  const tcPr = xmlCell["a:tcPr"];
28335
- writeCellFill(tcPr, style);
29527
+ writeCellFill(tcPr, style, (colorXml) => this.parseColor(colorXml));
28336
29528
  if (style.vAlign) {
28337
29529
  const vAlignMap = {
28338
29530
  top: "t",
@@ -28414,35 +29606,29 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28414
29606
  }
28415
29607
  }
28416
29608
  }
28417
- if (style.marginLeft !== void 0 || style.marginRight !== void 0 || style.marginTop !== void 0 || style.marginBottom !== void 0) {
28418
- const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
28419
- if (!tcPr["a:tcMar"]) {
28420
- tcPr["a:tcMar"] = {};
28421
- }
28422
- const tcMar = tcPr["a:tcMar"];
28423
- if (style.marginLeft !== void 0) {
28424
- tcMar["a:marL"] = {
28425
- "@_w": String(Math.round(style.marginLeft * emuPerPx))
28426
- };
28427
- }
28428
- if (style.marginRight !== void 0) {
28429
- tcMar["a:marR"] = {
28430
- "@_w": String(Math.round(style.marginRight * emuPerPx))
28431
- };
28432
- }
28433
- if (style.marginTop !== void 0) {
28434
- tcMar["a:marT"] = {
28435
- "@_w": String(Math.round(style.marginTop * emuPerPx))
28436
- };
28437
- }
28438
- if (style.marginBottom !== void 0) {
28439
- tcMar["a:marB"] = {
28440
- "@_w": String(Math.round(style.marginBottom * emuPerPx))
28441
- };
28442
- }
29609
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
29610
+ if (style.marginLeft !== void 0) {
29611
+ tcPr["@_marL"] = String(Math.round(style.marginLeft * emuPerPx));
29612
+ }
29613
+ if (style.marginRight !== void 0) {
29614
+ tcPr["@_marR"] = String(Math.round(style.marginRight * emuPerPx));
29615
+ }
29616
+ if (style.marginTop !== void 0) {
29617
+ tcPr["@_marT"] = String(Math.round(style.marginTop * emuPerPx));
29618
+ }
29619
+ if (style.marginBottom !== void 0) {
29620
+ tcPr["@_marB"] = String(Math.round(style.marginBottom * emuPerPx));
28443
29621
  }
29622
+ delete tcPr["a:tcMar"];
28444
29623
  writeDiagonalBorders(tcPr, style, _PptxHandlerRuntime.EMU_PER_PX);
28445
29624
  writeCellTextFormatting(xmlCell, style, this.ensureArray.bind(this));
29625
+ const reordered = reorderObjectKeys(tcPr, TC_PR_BORDERS_ORDER);
29626
+ for (const key of Object.keys(tcPr)) {
29627
+ delete tcPr[key];
29628
+ }
29629
+ for (const key of Object.keys(reordered)) {
29630
+ tcPr[key] = reordered[key];
29631
+ }
28446
29632
  }
28447
29633
  };
28448
29634
 
@@ -28469,14 +29655,27 @@ function serializeCellMergeAttributes(xmlCell, cell) {
28469
29655
  delete xmlCell["@_vMerge"];
28470
29656
  }
28471
29657
  }
29658
+ var DEFAULT_POWERPOINT_TABLE_STYLE_ID = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
28472
29659
  function serializeTablePropertyFlags(tbl, tableData) {
28473
29660
  const tblPr = tbl["a:tblPr"] ?? {};
28474
- tblPr["@_bandRow"] = tableData.bandedRows ? "1" : "0";
28475
- tblPr["@_bandCol"] = tableData.bandedColumns ? "1" : "0";
28476
- tblPr["@_firstRow"] = tableData.firstRowHeader ? "1" : "0";
28477
- tblPr["@_lastRow"] = tableData.lastRow ? "1" : "0";
28478
- tblPr["@_firstCol"] = tableData.firstCol ? "1" : "0";
28479
- tblPr["@_lastCol"] = tableData.lastCol ? "1" : "0";
29661
+ const setOrDelete = (key, truthy) => {
29662
+ if (truthy) {
29663
+ tblPr[key] = "1";
29664
+ } else {
29665
+ delete tblPr[key];
29666
+ }
29667
+ };
29668
+ setOrDelete("@_bandRow", tableData.bandedRows);
29669
+ setOrDelete("@_bandCol", tableData.bandedColumns);
29670
+ setOrDelete("@_firstRow", tableData.firstRowHeader);
29671
+ setOrDelete("@_lastRow", tableData.lastRow);
29672
+ setOrDelete("@_firstCol", tableData.firstCol);
29673
+ setOrDelete("@_lastCol", tableData.lastCol);
29674
+ if (tableData.tableStyleId) {
29675
+ tblPr["a:tableStyleId"] = tableData.tableStyleId;
29676
+ } else if (!tblPr["a:tableStyleId"]) {
29677
+ tblPr["a:tableStyleId"] = DEFAULT_POWERPOINT_TABLE_STYLE_ID;
29678
+ }
28480
29679
  tbl["a:tblPr"] = tblPr;
28481
29680
  }
28482
29681
  function replaceFirstTextValueInTree(node, localName, newValue, getXmlLocalName) {
@@ -28521,7 +29720,9 @@ function createDefaultXmlCell() {
28521
29720
  "a:bodyPr": {},
28522
29721
  "a:lstStyle": {},
28523
29722
  "a:p": {
28524
- "a:endParaRPr": { "@_lang": "en-US" }
29723
+ // Match PowerPoint's "Insert Table" default: every paragraph-end
29724
+ // run carries `lang="en-US" dirty="0"`.
29725
+ "a:endParaRPr": { "@_lang": "en-US", "@_dirty": "0" }
28525
29726
  }
28526
29727
  },
28527
29728
  "a:tcPr": {}
@@ -28529,6 +29730,54 @@ function createDefaultXmlCell() {
28529
29730
  }
28530
29731
 
28531
29732
  // src/core/core/runtime/table-xml-rebuild.ts
29733
+ var GRID_COL_ID_EXT_URI = "{9D8B030D-6E8A-4147-A177-3AD203B41FA5}";
29734
+ var A16_NAMESPACE = "http://schemas.microsoft.com/office/drawing/2014/main";
29735
+ function ensureA16NamespaceOnSlideRoot(slideRoot) {
29736
+ if (!slideRoot["@_xmlns:a16"]) {
29737
+ slideRoot["@_xmlns:a16"] = A16_NAMESPACE;
29738
+ }
29739
+ if (!slideRoot["@_xmlns:mc"]) {
29740
+ slideRoot["@_xmlns:mc"] = "http://schemas.openxmlformats.org/markup-compatibility/2006";
29741
+ }
29742
+ const existingIgnorable = String(slideRoot["@_mc:Ignorable"] || "").trim();
29743
+ if (existingIgnorable.length === 0) {
29744
+ slideRoot["@_mc:Ignorable"] = "a16";
29745
+ return;
29746
+ }
29747
+ const tokens = existingIgnorable.split(/\s+/).filter((token) => token.length > 0);
29748
+ if (!tokens.includes("a16")) {
29749
+ tokens.push("a16");
29750
+ slideRoot["@_mc:Ignorable"] = tokens.join(" ");
29751
+ }
29752
+ }
29753
+ function slideContainsA16Element(node) {
29754
+ if (node === null || node === void 0) {
29755
+ return false;
29756
+ }
29757
+ if (Array.isArray(node)) {
29758
+ for (const entry of node) {
29759
+ if (slideContainsA16Element(entry)) {
29760
+ return true;
29761
+ }
29762
+ }
29763
+ return false;
29764
+ }
29765
+ if (typeof node !== "object") {
29766
+ return false;
29767
+ }
29768
+ for (const [key, value] of Object.entries(node)) {
29769
+ if (key.startsWith("a16:")) {
29770
+ return true;
29771
+ }
29772
+ if (slideContainsA16Element(value)) {
29773
+ return true;
29774
+ }
29775
+ }
29776
+ return false;
29777
+ }
29778
+ function randomColumnId() {
29779
+ return String(Math.floor(Math.random() * 4294967295));
29780
+ }
28532
29781
  function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
28533
29782
  const existingXmlRows = ensureArrayFn(tbl["a:tr"]);
28534
29783
  const existingGridCols = ensureArrayFn(
@@ -28537,8 +29786,33 @@ function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
28537
29786
  const totalWidthEmu = existingGridCols.reduce((sum, col) => {
28538
29787
  return sum + (parseInt(String(col?.["@_w"] || "0"), 10) || 0);
28539
29788
  }, 0) || 9144e3;
28540
- const newGridCols = tableData.columnWidths.map((w) => ({
28541
- "@_w": String(Math.round(w * totalWidthEmu))
29789
+ const existingColIds = existingGridCols.map((col) => {
29790
+ const extList = col?.["a:extLst"];
29791
+ const exts = Array.isArray(extList?.["a:ext"]) ? extList["a:ext"] : extList?.["a:ext"] ? [extList["a:ext"]] : [];
29792
+ for (const ext of exts) {
29793
+ if (ext?.["@_uri"] === GRID_COL_ID_EXT_URI) {
29794
+ const colId = ext["a16:colId"];
29795
+ const val = colId?.["@_val"];
29796
+ if (typeof val === "string" && val.length > 0) {
29797
+ return val;
29798
+ }
29799
+ }
29800
+ }
29801
+ return "";
29802
+ });
29803
+ const newGridCols = tableData.columnWidths.map((w, i) => ({
29804
+ "@_w": String(Math.round(w * totalWidthEmu)),
29805
+ "a:extLst": {
29806
+ "a:ext": {
29807
+ "@_uri": GRID_COL_ID_EXT_URI,
29808
+ // `xmlns:a16` is declared on the slide root by
29809
+ // `ensureA16NamespaceOnSlideRoot`; emitting it here too is
29810
+ // schema-redundant and PowerPoint flags it.
29811
+ "a16:colId": {
29812
+ "@_val": existingColIds[i] || randomColumnId()
29813
+ }
29814
+ }
29815
+ }
28542
29816
  }));
28543
29817
  if (!tbl["a:tblGrid"]) {
28544
29818
  tbl["a:tblGrid"] = {};
@@ -28893,26 +30167,64 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28893
30167
  continue;
28894
30168
  }
28895
30169
  const scalingNode = this.xmlLookupService.getChildByLocalName(axisNode, "scaling");
28896
- if (!scalingNode) {
28897
- continue;
30170
+ if (scalingNode) {
30171
+ if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
30172
+ const logBaseKey = Object.keys(scalingNode).find(
30173
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30174
+ );
30175
+ if (logBaseKey) {
30176
+ scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
30177
+ } else {
30178
+ scalingNode["c:logBase"] = {
30179
+ "@_val": String(matchingAxis.logBase)
30180
+ };
30181
+ }
30182
+ } else if (matchingAxis.logScale === false) {
30183
+ const logBaseKey = Object.keys(scalingNode).find(
30184
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30185
+ );
30186
+ if (logBaseKey) {
30187
+ delete scalingNode[logBaseKey];
30188
+ }
30189
+ }
30190
+ this.upsertChartAxisChild(
30191
+ scalingNode,
30192
+ "min",
30193
+ matchingAxis.min !== void 0 ? String(matchingAxis.min) : void 0
30194
+ );
30195
+ this.upsertChartAxisChild(
30196
+ scalingNode,
30197
+ "max",
30198
+ matchingAxis.max !== void 0 ? String(matchingAxis.max) : void 0
30199
+ );
28898
30200
  }
28899
- if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
28900
- const logBaseKey = Object.keys(scalingNode).find(
28901
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30201
+ if (matchingAxis.numFmt) {
30202
+ const numFmtKey = Object.keys(axisNode).find(
30203
+ (k) => this.compatibilityService.getXmlLocalName(k) === "numFmt"
28902
30204
  );
28903
- if (logBaseKey) {
28904
- scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
30205
+ const numFmtAttrs = {
30206
+ "@_formatCode": matchingAxis.numFmt.formatCode,
30207
+ "@_sourceLinked": matchingAxis.numFmt.sourceLinked ? "1" : "0"
30208
+ };
30209
+ if (numFmtKey) {
30210
+ axisNode[numFmtKey] = numFmtAttrs;
28905
30211
  } else {
28906
- scalingNode["c:logBase"] = {
28907
- "@_val": String(matchingAxis.logBase)
28908
- };
30212
+ axisNode["c:numFmt"] = numFmtAttrs;
28909
30213
  }
28910
- } else if (matchingAxis.logScale === false) {
28911
- const logBaseKey = Object.keys(scalingNode).find(
28912
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30214
+ }
30215
+ this.upsertChartAxisChild(
30216
+ axisNode,
30217
+ "majorUnit",
30218
+ matchingAxis.majorUnit !== void 0 ? String(matchingAxis.majorUnit) : void 0
30219
+ );
30220
+ if (matchingAxis.tickLblPos !== void 0) {
30221
+ const tickLblKey = Object.keys(axisNode).find(
30222
+ (k) => this.compatibilityService.getXmlLocalName(k) === "tickLblPos"
28913
30223
  );
28914
- if (logBaseKey) {
28915
- delete scalingNode[logBaseKey];
30224
+ if (tickLblKey) {
30225
+ axisNode[tickLblKey]["@_val"] = matchingAxis.tickLblPos;
30226
+ } else {
30227
+ axisNode["c:tickLblPos"] = { "@_val": matchingAxis.tickLblPos };
28916
30228
  }
28917
30229
  }
28918
30230
  }
@@ -28925,6 +30237,18 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28925
30237
  }
28926
30238
  this.pendingChartUpdates = void 0;
28927
30239
  }
30240
+ /**
30241
+ * Upsert a `c:<localName>` child with `@_val` on an axis or scaling node.
30242
+ * When `value` is undefined, removes any existing child of that local name.
30243
+ */
30244
+ upsertChartAxisChild(parent, localName, value) {
30245
+ upsertChartAxisChild(
30246
+ parent,
30247
+ localName,
30248
+ value,
30249
+ (key) => this.compatibilityService.getXmlLocalName(key)
30250
+ );
30251
+ }
28928
30252
  /**
28929
30253
  * Update the cached point values in a chart reference node
28930
30254
  * (numRef/strRef or numLit/strLit).
@@ -30204,17 +31528,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30204
31528
  delete spPr["a:noFill"];
30205
31529
  delete spPr["a:gradFill"];
30206
31530
  delete spPr["a:blipFill"];
30207
- const solidFill = {
30208
- "a:srgbClr": {
30209
- "@_val": fillColor.replace("#", "")
30210
- }
30211
- };
30212
- if (typeof shapeStyle.fillOpacity === "number" && shapeStyle.fillOpacity >= 0 && shapeStyle.fillOpacity < 1) {
30213
- solidFill["a:srgbClr"]["a:alpha"] = {
30214
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.fillOpacity) * 1e5))
30215
- };
30216
- }
30217
- spPr["a:solidFill"] = solidFill;
31531
+ const resolvedOriginal = shapeStyle.fillColorXml ? this.parseColor(shapeStyle.fillColorXml) : void 0;
31532
+ spPr["a:solidFill"] = serializeColorChoice(
31533
+ shapeStyle.fillColorXml,
31534
+ resolvedOriginal,
31535
+ fillColor,
31536
+ shapeStyle.fillOpacity
31537
+ );
30218
31538
  }
30219
31539
  }
30220
31540
  if (shapeStyle.strokeColor !== void 0) {
@@ -30229,17 +31549,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30229
31549
  delete lineNode["a:solidFill"];
30230
31550
  } else {
30231
31551
  delete lineNode["a:noFill"];
30232
- const lineFill = {
30233
- "a:srgbClr": {
30234
- "@_val": shapeStyle.strokeColor.replace("#", "")
30235
- }
30236
- };
30237
- if (typeof shapeStyle.strokeOpacity === "number" && shapeStyle.strokeOpacity >= 0 && shapeStyle.strokeOpacity < 1) {
30238
- lineFill["a:srgbClr"]["a:alpha"] = {
30239
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.strokeOpacity) * 1e5))
30240
- };
30241
- }
30242
- lineNode["a:solidFill"] = lineFill;
31552
+ const resolvedStrokeOriginal = shapeStyle.strokeColorXml ? this.parseColor(shapeStyle.strokeColorXml) : void 0;
31553
+ lineNode["a:solidFill"] = serializeColorChoice(
31554
+ shapeStyle.strokeColorXml,
31555
+ resolvedStrokeOriginal,
31556
+ shapeStyle.strokeColor,
31557
+ shapeStyle.strokeOpacity
31558
+ );
30243
31559
  }
30244
31560
  }
30245
31561
  if (shapeStyle.strokeDash !== void 0) {
@@ -30320,7 +31636,11 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30320
31636
  } else if (shapeStyle.lineJoin === "bevel") {
30321
31637
  lineNode["a:bevel"] = {};
30322
31638
  } else if (shapeStyle.lineJoin === "miter") {
30323
- lineNode["a:miter"] = { "@_lim": "800000" };
31639
+ const miterNode = {};
31640
+ if (typeof shapeStyle.miterLimit === "number" && Number.isFinite(shapeStyle.miterLimit) && shapeStyle.miterLimit !== 8e5) {
31641
+ miterNode["@_lim"] = String(Math.round(shapeStyle.miterLimit));
31642
+ }
31643
+ lineNode["a:miter"] = miterNode;
30324
31644
  }
30325
31645
  }
30326
31646
  if (shapeStyle.lineCap !== void 0) {
@@ -30340,6 +31660,82 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30340
31660
  spPr["a:ln"]["a:effectLst"] = lineEffectListXml;
30341
31661
  }
30342
31662
  }
31663
+ /**
31664
+ * Serialize the shape's `<p:style>` block (CT_ShapeStyle §20.1.2.2.36)
31665
+ * from the persisted ref indices/colour XML. Emits children in spec
31666
+ * order: `lnRef → fillRef → effectRef → fontRef`.
31667
+ *
31668
+ * When the original shape XML already contained a `<p:style>` we mutate
31669
+ * that node in place so any unmodelled attributes/children are preserved.
31670
+ * When it didn't, we create one. When the shape no longer has any ref
31671
+ * data we leave the existing `<p:style>` (if any) untouched — silently
31672
+ * dropping it would break round-tripping.
31673
+ *
31674
+ * Phase 2 Stream B / C-H2.
31675
+ */
31676
+ applyShapeStyleRefs(shape, shapeStyle) {
31677
+ const hasAnyRef = shapeStyle.lnRefIdx !== void 0 || shapeStyle.fillRefIdx !== void 0 || shapeStyle.effectRefIdx !== void 0 || shapeStyle.fontRefIdx !== void 0;
31678
+ if (!hasAnyRef) {
31679
+ return;
31680
+ }
31681
+ const existing = shape["p:style"];
31682
+ const styleNode = existing ?? {};
31683
+ if (shapeStyle.lnRefIdx !== void 0) {
31684
+ const lnRef = styleNode["a:lnRef"] ?? {};
31685
+ lnRef["@_idx"] = String(shapeStyle.lnRefIdx);
31686
+ this.replaceRefColorChoice(lnRef, shapeStyle.lnRefColorXml);
31687
+ styleNode["a:lnRef"] = lnRef;
31688
+ }
31689
+ if (shapeStyle.fillRefIdx !== void 0) {
31690
+ const fillRef = styleNode["a:fillRef"] ?? {};
31691
+ fillRef["@_idx"] = String(shapeStyle.fillRefIdx);
31692
+ this.replaceRefColorChoice(fillRef, shapeStyle.fillRefColorXml);
31693
+ styleNode["a:fillRef"] = fillRef;
31694
+ }
31695
+ if (shapeStyle.effectRefIdx !== void 0) {
31696
+ const effectRef = styleNode["a:effectRef"] ?? {};
31697
+ effectRef["@_idx"] = String(shapeStyle.effectRefIdx);
31698
+ this.replaceRefColorChoice(effectRef, shapeStyle.effectRefColorXml);
31699
+ styleNode["a:effectRef"] = effectRef;
31700
+ }
31701
+ if (shapeStyle.fontRefIdx !== void 0) {
31702
+ const fontRef = styleNode["a:fontRef"] ?? {};
31703
+ fontRef["@_idx"] = shapeStyle.fontRefIdx;
31704
+ this.replaceRefColorChoice(fontRef, shapeStyle.fontRefColorXml);
31705
+ styleNode["a:fontRef"] = fontRef;
31706
+ }
31707
+ const reordered = reorderObjectKeys(styleNode, SHAPE_STYLE_ORDER);
31708
+ for (const key of Object.keys(styleNode)) {
31709
+ delete styleNode[key];
31710
+ }
31711
+ for (const key of Object.keys(reordered)) {
31712
+ styleNode[key] = reordered[key];
31713
+ }
31714
+ shape["p:style"] = styleNode;
31715
+ }
31716
+ /**
31717
+ * Replace any existing colour-choice child on a style-matrix-reference
31718
+ * element with the given preserved XML, or strip all colour children
31719
+ * when the override is undefined.
31720
+ */
31721
+ replaceRefColorChoice(refNode, colorXml) {
31722
+ for (const key of [
31723
+ "a:scrgbClr",
31724
+ "a:srgbClr",
31725
+ "a:hslClr",
31726
+ "a:sysClr",
31727
+ "a:schemeClr",
31728
+ "a:prstClr"
31729
+ ]) {
31730
+ delete refNode[key];
31731
+ }
31732
+ if (!colorXml) {
31733
+ return;
31734
+ }
31735
+ for (const [key, value] of Object.entries(colorXml)) {
31736
+ refNode[key] = value;
31737
+ }
31738
+ }
30343
31739
  };
30344
31740
 
30345
31741
  // src/core/core/runtime/PptxHandlerRuntimeSaveEffectsWriter.ts
@@ -30349,17 +31745,22 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30349
31745
  * effectDag, 3D scene, and 3D shape properties to the given spPr XML object.
30350
31746
  */
30351
31747
  applyEffectsAndThreeD(spPr, shapeStyle) {
30352
- const outerShadowXml = this.buildOuterShadowXml(shapeStyle);
31748
+ const presetShadowXml = shapeStyle.presetShadowName ? this.buildPresetShadowXml(shapeStyle) : void 0;
31749
+ const outerShadowXml = presetShadowXml ? void 0 : this.buildOuterShadowXml(shapeStyle);
30353
31750
  const innerShadowXml = this.buildInnerShadowXml(shapeStyle);
30354
31751
  const glowXml = this.buildGlowXml(shapeStyle);
30355
31752
  const softEdgeXml = this.buildSoftEdgeXml(shapeStyle);
30356
31753
  const reflectionXml = this.buildReflectionXml(shapeStyle);
30357
31754
  const blurXml = this.buildBlurXml(shapeStyle);
30358
- const hasAnyEffect = outerShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
31755
+ const hasAnyEffect = outerShadowXml || presetShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
30359
31756
  if (hasAnyEffect) {
30360
31757
  const effectList = spPr["a:effectLst"] || {};
30361
- if (outerShadowXml) {
31758
+ if (presetShadowXml) {
31759
+ effectList["a:prstShdw"] = presetShadowXml;
31760
+ delete effectList["a:outerShdw"];
31761
+ } else if (outerShadowXml) {
30362
31762
  effectList["a:outerShdw"] = outerShadowXml;
31763
+ delete effectList["a:prstShdw"];
30363
31764
  }
30364
31765
  if (innerShadowXml) {
30365
31766
  effectList["a:innerShdw"] = innerShadowXml;
@@ -30376,12 +31777,13 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30376
31777
  if (blurXml) {
30377
31778
  effectList["a:blur"] = blurXml;
30378
31779
  }
30379
- spPr["a:effectLst"] = effectList;
31780
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30380
31781
  } else {
30381
31782
  const effectList = spPr["a:effectLst"];
30382
31783
  if (effectList) {
30383
- if (shapeStyle.shadowColor !== void 0 && !outerShadowXml) {
31784
+ if (shapeStyle.shadowColor !== void 0 && !outerShadowXml && !presetShadowXml) {
30384
31785
  delete effectList["a:outerShdw"];
31786
+ delete effectList["a:prstShdw"];
30385
31787
  }
30386
31788
  if (shapeStyle.innerShadowColor !== void 0 && !innerShadowXml) {
30387
31789
  delete effectList["a:innerShdw"];
@@ -30400,6 +31802,8 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30400
31802
  }
30401
31803
  if (Object.keys(effectList).length === 0) {
30402
31804
  delete spPr["a:effectLst"];
31805
+ } else {
31806
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30403
31807
  }
30404
31808
  }
30405
31809
  }
@@ -30560,7 +31964,8 @@ var PptxHandlerRuntime30 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30560
31964
  );
30561
31965
  }
30562
31966
  if (el.textStyle?.hOverflow) {
30563
- bodyPr["@_hOverflow"] = el.textStyle.hOverflow;
31967
+ bodyPr["@_horzOverflow"] = el.textStyle.hOverflow;
31968
+ delete bodyPr["@_hOverflow"];
30564
31969
  }
30565
31970
  if (el.textStyle?.vertOverflow) {
30566
31971
  bodyPr["@_vertOverflow"] = el.textStyle.vertOverflow;
@@ -30791,7 +32196,10 @@ var PptxHandlerRuntime31 = class extends PptxHandlerRuntime30 {
30791
32196
  const elWithPaths = el;
30792
32197
  if (elWithPaths.customGeometryPaths && elWithPaths.customGeometryPaths.length > 0) {
30793
32198
  delete spPr["a:prstGeom"];
30794
- spPr["a:custGeom"] = customGeometryPathsToXml(elWithPaths.customGeometryPaths);
32199
+ spPr["a:custGeom"] = customGeometryPathsToXml(
32200
+ elWithPaths.customGeometryPaths,
32201
+ elWithPaths.customGeometryRawData
32202
+ );
30795
32203
  } else if (spPr["a:prstGeom"]) {
30796
32204
  const presetGeometry = el.type === "connector" ? this.normalizePresetGeometry(el.shapeType || "straightConnector1") : this.normalizePresetGeometry(el.shapeType);
30797
32205
  const prstGeom = spPr["a:prstGeom"];
@@ -30916,6 +32324,42 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
30916
32324
  isGraphicFrameShape(shape) {
30917
32325
  return Boolean(shape["p:nvGraphicFramePr"] || shape["a:graphic"] && shape["p:xfrm"]);
30918
32326
  }
32327
+ /**
32328
+ * Reorder children of `p:spPr` to match CT_ShapeProperties (§20.1.2.2.35).
32329
+ * Also reorders any nested `a:blipFill` per CT_BlipFillProperties.
32330
+ * fast-xml-parser preserves insertion order; PowerPoint validates against
32331
+ * the schema's required order, so save-side mutations must be re-sorted.
32332
+ */
32333
+ finalizeSpPrSchemaOrder(shape) {
32334
+ const spPr = shape["p:spPr"];
32335
+ if (!spPr) {
32336
+ return;
32337
+ }
32338
+ const blipFill = spPr["a:blipFill"];
32339
+ if (blipFill) {
32340
+ this.reorderInPlace(blipFill, BLIP_FILL_ORDER);
32341
+ }
32342
+ this.reorderInPlace(spPr, SP_PR_ORDER);
32343
+ }
32344
+ /**
32345
+ * Reorder children of the picture-level `p:blipFill` (CT_BlipFillProperties).
32346
+ * Picture elements carry their blip data on the `p:pic` root, not under spPr.
32347
+ */
32348
+ finalizePictureBlipFillOrder(shape) {
32349
+ const pBlipFill = shape["p:blipFill"];
32350
+ if (pBlipFill) {
32351
+ this.reorderInPlace(pBlipFill, BLIP_FILL_ORDER);
32352
+ }
32353
+ }
32354
+ reorderInPlace(target, schemaOrder) {
32355
+ const reordered = reorderObjectKeys(target, schemaOrder);
32356
+ for (const key of Object.keys(target)) {
32357
+ delete target[key];
32358
+ }
32359
+ for (const key of Object.keys(reordered)) {
32360
+ target[key] = reordered[key];
32361
+ }
32362
+ }
30919
32363
  /** Whether an element ID indicates a template (layout/master) element. */
30920
32364
  isTemplateElementId(elementId) {
30921
32365
  return elementId.startsWith("layout-") || elementId.startsWith("master-");
@@ -30939,14 +32383,50 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
30939
32383
  }
30940
32384
  return;
30941
32385
  }
32386
+ if (el.type === "contentPart") {
32387
+ if (shape) {
32388
+ this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
32389
+ collectors.contentParts.push(shape);
32390
+ } else {
32391
+ this.compatibilityService.reportWarning({
32392
+ code: "SAVE_ELEMENT_SKIPPED",
32393
+ message: `Content part '${el.id}' has no rawXml and was skipped during save.`,
32394
+ scope: "save",
32395
+ slideId: ctx.slide.id,
32396
+ elementId: el.id
32397
+ });
32398
+ }
32399
+ return;
32400
+ }
30942
32401
  if (!shape && (el.type === "text" || el.type === "shape")) {
30943
32402
  shape = this.createElementXml(el);
30944
32403
  }
30945
32404
  if (!shape && el.type === "connector") {
30946
32405
  shape = this.createConnectorXml(el);
30947
32406
  }
30948
- if (!shape && el.type === "ink") {
30949
- shape = this.createInkShapeXml(el);
32407
+ if (el.type === "ink") {
32408
+ if (!shape) {
32409
+ shape = this.createInkShapeXml(el);
32410
+ this.compatibilityService.reportWarning({
32411
+ code: "SAVE_INK_ENCODED_AS_CUSTGEOM",
32412
+ message: "SDK-created ink element serialized as custGeom shape; pressure/tool metadata not represented in OOXML aink format.",
32413
+ scope: "save",
32414
+ slideId: ctx.slide.id,
32415
+ elementId: el.id
32416
+ });
32417
+ }
32418
+ }
32419
+ if (!shape && el.type === "table") {
32420
+ shape = this.createTableGraphicFrameXml(el);
32421
+ }
32422
+ if (el.type === "ole") {
32423
+ const oleEl = el;
32424
+ if (shape) {
32425
+ this.applyOleTypedFieldUpdates(shape, oleEl);
32426
+ } else {
32427
+ const embedRid = this.resolveOleEmbedRelationshipId(ctx.slideRelationships, oleEl.oleTarget) || ctx.slideRelationshipRegistry.nextRelationshipId();
32428
+ shape = this.createOleGraphicFrameXml(oleEl, embedRid);
32429
+ }
30950
32430
  }
30951
32431
  if (!shape) {
30952
32432
  this.compatibilityService.reportWarning({
@@ -30960,11 +32440,14 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
30960
32440
  }
30961
32441
  this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
30962
32442
  this.applyImageProperties(shape, el);
32443
+ this.finalizePictureBlipFillOrder(shape);
30963
32444
  this.applyGeometryUpdate(shape, el);
30964
32445
  if (hasShapeProperties(el) && el.shapeStyle && shape["p:spPr"]) {
30965
32446
  const spPr = shape["p:spPr"];
30966
32447
  this.applyFillAndStroke(spPr, el.shapeStyle);
30967
32448
  this.applyEffectsAndThreeD(spPr, el.shapeStyle);
32449
+ this.finalizeSpPrSchemaOrder(shape);
32450
+ this.applyShapeStyleRefs(shape, el.shapeStyle);
30968
32451
  }
30969
32452
  if (hasTextProperties(el)) {
30970
32453
  this.applyTextBodyContent(
@@ -31122,7 +32605,8 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31122
32605
  connectors: [],
31123
32606
  graphicFrames: [],
31124
32607
  groups: [],
31125
- model3ds: []
32608
+ model3ds: [],
32609
+ contentParts: []
31126
32610
  };
31127
32611
  const ctx = {
31128
32612
  slide,
@@ -31154,6 +32638,12 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31154
32638
  } else {
31155
32639
  delete spTree["p16:model3D"];
31156
32640
  }
32641
+ if (collectors.contentParts.length > 0) {
32642
+ spTree["p:contentPart"] = collectors.contentParts;
32643
+ } else {
32644
+ delete spTree["p:contentPart"];
32645
+ }
32646
+ this.reapplyAlternateContentEnvelopes(spTree, collectors);
31157
32647
  const reassigned = shapeIdValidator.validateAndDeduplicateIds(
31158
32648
  spTree,
31159
32649
  (v) => this.ensureArray(v)
@@ -31171,12 +32661,426 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31171
32661
  this.zip.file(slideRelsPath, this.builder.build(slideRelsData));
31172
32662
  this.applySlideDrawingGuides(slideNode, slide);
31173
32663
  this.deduplicateExtensionLists(xmlObj);
32664
+ if (slideContainsA16Element(slideNode)) {
32665
+ ensureA16NamespaceOnSlideRoot(slideNode);
32666
+ }
31174
32667
  this.zip.file(slide.id, this.builder.build(xmlObj));
31175
32668
  }
32669
+ /**
32670
+ * Re-wrap selected children with their original `<mc:AlternateContent>`
32671
+ * envelope (CC-4).
32672
+ *
32673
+ * Parsing merged the selected branch (Choice when supported, otherwise
32674
+ * Fallback) into the spTree's tag arrays. Without re-wrapping, dirty
32675
+ * save would emit flat `<p:sp>`/`<p:pic>` etc. and drop the
32676
+ * `<mc:Fallback>` branch — losing legacy rendering for files originally
32677
+ * authored with newer-namespace features.
32678
+ *
32679
+ * Strategy: for each XmlObject in `collectors.*` that traces back to a
32680
+ * known AC block, group by block and:
32681
+ * 1. Remove the node from its flat collector / spTree array.
32682
+ * 2. Clone the original AC envelope.
32683
+ * 3. Replace the selected branch's `<{tag}>` children with the
32684
+ * live (possibly edited) nodes from the collectors.
32685
+ * 4. Leave the unselected branch verbatim.
32686
+ *
32687
+ * Final envelopes are appended to `spTree['mc:AlternateContent']`.
32688
+ */
32689
+ reapplyAlternateContentEnvelopes(spTree, collectors) {
32690
+ const TAG_TO_COLLECTOR = {
32691
+ "p:sp": collectors.shapes,
32692
+ "p:pic": collectors.pics,
32693
+ "p:cxnSp": collectors.connectors,
32694
+ "p:graphicFrame": collectors.graphicFrames,
32695
+ "p:grpSp": collectors.groups,
32696
+ "p:contentPart": collectors.contentParts,
32697
+ // `model3d` does not flow through SHAPE_TREE_ELEMENT_TAGS, but the
32698
+ // AC pathway in OpenXML decks frequently uses Choice = p16:model3D
32699
+ // + Fallback = p:pic, so map it for completeness.
32700
+ "p16:model3D": collectors.model3ds
32701
+ };
32702
+ const blockGroups = /* @__PURE__ */ new Map();
32703
+ for (const tag of Object.keys(TAG_TO_COLLECTOR)) {
32704
+ const collector = TAG_TO_COLLECTOR[tag];
32705
+ if (!collector) {
32706
+ continue;
32707
+ }
32708
+ for (const node of collector) {
32709
+ const block = this.alternateContentBlockByRawXml.get(node);
32710
+ if (!block) {
32711
+ continue;
32712
+ }
32713
+ let entries = blockGroups.get(block);
32714
+ if (!entries) {
32715
+ entries = [];
32716
+ blockGroups.set(block, entries);
32717
+ }
32718
+ entries.push({ tag, node, collector });
32719
+ }
32720
+ }
32721
+ if (blockGroups.size === 0) {
32722
+ return;
32723
+ }
32724
+ const envelopes = [];
32725
+ for (const [block, entries] of blockGroups) {
32726
+ for (const entry of entries) {
32727
+ const idx = entry.collector.indexOf(entry.node);
32728
+ if (idx !== -1) {
32729
+ entry.collector.splice(idx, 1);
32730
+ }
32731
+ }
32732
+ const clonedAc = { ...block.rawAc };
32733
+ const liveByTag = /* @__PURE__ */ new Map();
32734
+ for (const entry of entries) {
32735
+ let arr = liveByTag.get(entry.tag);
32736
+ if (!arr) {
32737
+ arr = [];
32738
+ liveByTag.set(entry.tag, arr);
32739
+ }
32740
+ arr.push(entry.node);
32741
+ }
32742
+ if (block.selectedBranch === "choice") {
32743
+ const choices = this.ensureArray(clonedAc["mc:Choice"]);
32744
+ const targetIdx = block.choiceIndex ?? 0;
32745
+ const original = choices[targetIdx];
32746
+ if (original) {
32747
+ const rebuilt = { ...original };
32748
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
32749
+ delete rebuilt[tag];
32750
+ }
32751
+ for (const [tag, nodes] of liveByTag) {
32752
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
32753
+ }
32754
+ choices[targetIdx] = rebuilt;
32755
+ clonedAc["mc:Choice"] = choices.length === 1 ? choices[0] : choices;
32756
+ }
32757
+ } else {
32758
+ const fallback = clonedAc["mc:Fallback"];
32759
+ if (fallback) {
32760
+ const rebuilt = { ...fallback };
32761
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
32762
+ delete rebuilt[tag];
32763
+ }
32764
+ for (const [tag, nodes] of liveByTag) {
32765
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
32766
+ }
32767
+ clonedAc["mc:Fallback"] = rebuilt;
32768
+ }
32769
+ }
32770
+ envelopes.push(clonedAc);
32771
+ }
32772
+ spTree["p:sp"] = collectors.shapes;
32773
+ spTree["p:pic"] = collectors.pics;
32774
+ spTree["p:cxnSp"] = collectors.connectors;
32775
+ spTree["p:graphicFrame"] = collectors.graphicFrames;
32776
+ if (collectors.groups.length > 0) {
32777
+ spTree["p:grpSp"] = collectors.groups;
32778
+ } else {
32779
+ delete spTree["p:grpSp"];
32780
+ }
32781
+ if (collectors.contentParts.length > 0) {
32782
+ spTree["p:contentPart"] = collectors.contentParts;
32783
+ } else {
32784
+ delete spTree["p:contentPart"];
32785
+ }
32786
+ if (collectors.model3ds.length > 0) {
32787
+ spTree["p16:model3D"] = collectors.model3ds;
32788
+ } else {
32789
+ delete spTree["p16:model3D"];
32790
+ }
32791
+ spTree["mc:AlternateContent"] = envelopes.length === 1 ? envelopes[0] : envelopes;
32792
+ }
32793
+ };
32794
+
32795
+ // src/core/core/runtime/PptxHandlerRuntimeSaveTheme.ts
32796
+ var PptxHandlerRuntime34 = class extends PptxHandlerRuntime33 {
32797
+ /**
32798
+ * Mark a theme path as dirty so the save pipeline will regenerate
32799
+ * the theme XML from in-memory state. Optional — without this the
32800
+ * original XML is preserved verbatim on save (C-H3).
32801
+ */
32802
+ markThemeDirty(themePath) {
32803
+ this.dirtyThemePaths.add(themePath);
32804
+ }
32805
+ /**
32806
+ * Mark all known theme paths dirty in one call.
32807
+ */
32808
+ markAllThemesDirty() {
32809
+ for (const themePath of this.originalThemeXmlByPath.keys()) {
32810
+ this.dirtyThemePaths.add(themePath);
32811
+ }
32812
+ for (const themePath of this.masterThemePaths.values()) {
32813
+ this.dirtyThemePaths.add(themePath);
32814
+ }
32815
+ }
32816
+ /**
32817
+ * Persist all theme parts during save. Called from the save pipeline
32818
+ * after master / layout XML have been flushed and before
32819
+ * presentation.xml is serialized.
32820
+ *
32821
+ * Order of operations per theme path:
32822
+ *
32823
+ * 1. If the path is *not* in {@link dirtyThemePaths}, the existing
32824
+ * ZIP entry is already correct — no-op. (Original XML was placed
32825
+ * into the ZIP at load time.)
32826
+ * 2. If the path is dirty, build a fresh `<a:theme>` document from
32827
+ * in-memory state and the captured raw subtrees, then overwrite
32828
+ * the ZIP entry.
32829
+ */
32830
+ async persistThemeParts() {
32831
+ const seenThemePaths = /* @__PURE__ */ new Set();
32832
+ for (const [masterPath, themePath] of this.masterThemePaths.entries()) {
32833
+ if (!themePath) {
32834
+ continue;
32835
+ }
32836
+ seenThemePaths.add(themePath);
32837
+ if (!this.dirtyThemePaths.has(themePath)) {
32838
+ continue;
32839
+ }
32840
+ const themeXml2 = this.buildThemeXml(themePath, masterPath);
32841
+ if (themeXml2) {
32842
+ this.zip.file(themePath, themeXml2);
32843
+ }
32844
+ }
32845
+ for (const [themePath] of this.originalThemeXmlByPath.entries()) {
32846
+ if (seenThemePaths.has(themePath)) {
32847
+ continue;
32848
+ }
32849
+ if (!this.dirtyThemePaths.has(themePath)) {
32850
+ continue;
32851
+ }
32852
+ const themeXml2 = this.buildThemeXml(themePath, void 0);
32853
+ if (themeXml2) {
32854
+ this.zip.file(themePath, themeXml2);
32855
+ }
32856
+ }
32857
+ }
32858
+ /**
32859
+ * Build a complete `<a:theme>` XML document from in-memory state.
32860
+ * Returns the serialized XML string (with XML prolog), or `undefined`
32861
+ * if there is no source data to emit.
32862
+ *
32863
+ * - Color scheme: built from per-master color map (or global fallback).
32864
+ * - Font scheme: built from per-master font map + per-script entries.
32865
+ * - Format scheme: re-emit the original XML subtree if available; else
32866
+ * build a minimal scheme from {@link themeFormatScheme}.
32867
+ * - objectDefaults / extraClrSchemeLst / custClrLst / extLst: re-emit
32868
+ * captured raw subtrees.
32869
+ */
32870
+ buildThemeXml(themePath, masterPath) {
32871
+ const colorMap = masterPath && this.masterThemeColorMaps.get(masterPath) || this.globalThemeColorMapSnapshot || this.themeColorMap;
32872
+ const fontMap = masterPath && this.masterThemeFontMaps.get(masterPath) || this.globalThemeFontMapSnapshot || this.themeFontMap;
32873
+ const themeName = this.masterThemeNames.get(themePath) || "Office Theme";
32874
+ const colorSchemeName = this.masterThemeColorSchemeNames.get(themePath) || themeName;
32875
+ const fontSchemeName = this.masterThemeFontSchemeNames.get(themePath) || themeName;
32876
+ const majorScripts = this.masterThemeMajorFontScripts.get(themePath) || {};
32877
+ const minorScripts = this.masterThemeMinorFontScripts.get(themePath) || {};
32878
+ const clrScheme = this.buildClrSchemeObject(colorSchemeName, colorMap);
32879
+ const fontScheme = this.buildFontSchemeObject(
32880
+ fontSchemeName,
32881
+ fontMap,
32882
+ majorScripts,
32883
+ minorScripts
32884
+ );
32885
+ const fmtScheme = this.extractRawSubtreeFromOriginal(themePath, [
32886
+ "a:theme",
32887
+ "a:themeElements",
32888
+ "a:fmtScheme"
32889
+ ]);
32890
+ const themeElements = {
32891
+ "a:clrScheme": clrScheme,
32892
+ "a:fontScheme": fontScheme
32893
+ };
32894
+ if (fmtScheme !== void 0) {
32895
+ themeElements["a:fmtScheme"] = fmtScheme;
32896
+ } else {
32897
+ themeElements["a:fmtScheme"] = this.buildMinimalFmtScheme(themeName);
32898
+ }
32899
+ const themeRoot = {
32900
+ "@_xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
32901
+ "@_name": themeName,
32902
+ "a:themeElements": themeElements
32903
+ };
32904
+ const objectDefaults = this.masterThemeObjectDefaults.get(themePath);
32905
+ if (objectDefaults && (objectDefaults.spDef || objectDefaults.lnDef || objectDefaults.txDef)) {
32906
+ const od = {};
32907
+ if (objectDefaults.spDef !== void 0) {
32908
+ od["a:spDef"] = objectDefaults.spDef;
32909
+ }
32910
+ if (objectDefaults.lnDef !== void 0) {
32911
+ od["a:lnDef"] = objectDefaults.lnDef;
32912
+ }
32913
+ if (objectDefaults.txDef !== void 0) {
32914
+ od["a:txDef"] = objectDefaults.txDef;
32915
+ }
32916
+ themeRoot["a:objectDefaults"] = od;
32917
+ } else {
32918
+ themeRoot["a:objectDefaults"] = {};
32919
+ }
32920
+ const extraClr = this.masterThemeExtraClrSchemeLst.get(themePath);
32921
+ themeRoot["a:extraClrSchemeLst"] = extraClr !== void 0 ? extraClr : {};
32922
+ const custClr = this.masterThemeCustClrLst.get(themePath);
32923
+ if (custClr !== void 0) {
32924
+ themeRoot["a:custClrLst"] = custClr;
32925
+ }
32926
+ const themeExt = this.masterThemeExtLst.get(themePath);
32927
+ if (themeExt !== void 0) {
32928
+ themeRoot["a:extLst"] = themeExt;
32929
+ }
32930
+ const doc = {
32931
+ "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
32932
+ "a:theme": themeRoot
32933
+ };
32934
+ try {
32935
+ return this.builder.build(doc);
32936
+ } catch (error) {
32937
+ console.warn(`Failed to build theme XML for ${themePath}:`, error);
32938
+ return void 0;
32939
+ }
32940
+ }
32941
+ /**
32942
+ * Build the `a:clrScheme` XmlObject from a colour map. Each slot
32943
+ * value is interpreted as either a `#RRGGBB` srgb hex or a known
32944
+ * sysClr token (currently always emitted as srgbClr — the in-memory
32945
+ * map is hex-typed; sysClr round-trip belongs to the broader C-H3
32946
+ * fix to preserve original color XML and is out of scope here).
32947
+ */
32948
+ buildClrSchemeObject(schemeName, colorMap) {
32949
+ const slot = (key) => {
32950
+ const hex = String(colorMap[key] || "").replace(/^#/, "");
32951
+ const srgb = hex.length === 6 ? hex.toUpperCase() : "000000";
32952
+ return { "a:srgbClr": { "@_val": srgb } };
32953
+ };
32954
+ return {
32955
+ "@_name": schemeName,
32956
+ "a:dk1": slot("dk1"),
32957
+ "a:lt1": slot("lt1"),
32958
+ "a:dk2": slot("dk2"),
32959
+ "a:lt2": slot("lt2"),
32960
+ "a:accent1": slot("accent1"),
32961
+ "a:accent2": slot("accent2"),
32962
+ "a:accent3": slot("accent3"),
32963
+ "a:accent4": slot("accent4"),
32964
+ "a:accent5": slot("accent5"),
32965
+ "a:accent6": slot("accent6"),
32966
+ "a:hlink": slot("hlink"),
32967
+ "a:folHlink": slot("folHlink")
32968
+ };
32969
+ }
32970
+ /**
32971
+ * Build the `a:fontScheme` XmlObject from a font map plus per-script
32972
+ * font tables.
32973
+ *
32974
+ * Phase 4 Stream A / M4.
32975
+ */
32976
+ buildFontSchemeObject(schemeName, fontMap, majorScripts, minorScripts) {
32977
+ const buildFontGroup = (latinKey, eaKey, csKey, scripts) => {
32978
+ const group = {
32979
+ "a:latin": { "@_typeface": fontMap[latinKey] || "Calibri" },
32980
+ "a:ea": { "@_typeface": fontMap[eaKey] || "" },
32981
+ "a:cs": { "@_typeface": fontMap[csKey] || "" }
32982
+ };
32983
+ const scriptKeys = Object.keys(scripts);
32984
+ if (scriptKeys.length > 0) {
32985
+ const fontEntries = scriptKeys.map((script) => ({
32986
+ "@_script": script,
32987
+ "@_typeface": scripts[script]
32988
+ }));
32989
+ group["a:font"] = fontEntries.length === 1 ? fontEntries[0] : fontEntries;
32990
+ }
32991
+ return group;
32992
+ };
32993
+ return {
32994
+ "@_name": schemeName,
32995
+ "a:majorFont": buildFontGroup("mj-lt", "mj-ea", "mj-cs", majorScripts),
32996
+ "a:minorFont": buildFontGroup("mn-lt", "mn-ea", "mn-cs", minorScripts)
32997
+ };
32998
+ }
32999
+ /**
33000
+ * Re-parse the original theme XML and pluck out a subtree by path,
33001
+ * returning the raw parser object. Returns `undefined` when the
33002
+ * original is missing or the path doesn't exist.
33003
+ *
33004
+ * Used to preserve `a:fmtScheme` byte-for-byte through a regenerate
33005
+ * round-trip, since the in-memory PptxThemeFormatScheme is lossy.
33006
+ */
33007
+ extractRawSubtreeFromOriginal(themePath, path2) {
33008
+ const original = this.originalThemeXmlByPath.get(themePath);
33009
+ if (!original) {
33010
+ return void 0;
33011
+ }
33012
+ try {
33013
+ const parsed = this.parser.parse(original);
33014
+ let cursor = parsed;
33015
+ for (const segment of path2) {
33016
+ if (cursor && typeof cursor === "object" && segment in cursor) {
33017
+ cursor = cursor[segment];
33018
+ } else {
33019
+ return void 0;
33020
+ }
33021
+ }
33022
+ return cursor;
33023
+ } catch {
33024
+ return void 0;
33025
+ }
33026
+ }
33027
+ /**
33028
+ * Last-resort minimal `<a:fmtScheme>` body. Mirrors the SDK new-deck
33029
+ * builder's output for new presentations, scaled down to the smallest
33030
+ * schema-valid form.
33031
+ */
33032
+ buildMinimalFmtScheme(name) {
33033
+ const phClrSolid = { "a:solidFill": { "a:schemeClr": { "@_val": "phClr" } } };
33034
+ return {
33035
+ "@_name": name,
33036
+ "a:fillStyleLst": {
33037
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
33038
+ "a:gradFill": []
33039
+ },
33040
+ "a:lnStyleLst": {
33041
+ "a:ln": [
33042
+ {
33043
+ "@_w": "6350",
33044
+ "@_cap": "flat",
33045
+ "@_cmpd": "sng",
33046
+ "@_algn": "ctr",
33047
+ ...phClrSolid,
33048
+ "a:prstDash": { "@_val": "solid" },
33049
+ "a:miter": { "@_lim": "800000" }
33050
+ },
33051
+ {
33052
+ "@_w": "12700",
33053
+ "@_cap": "flat",
33054
+ "@_cmpd": "sng",
33055
+ "@_algn": "ctr",
33056
+ ...phClrSolid,
33057
+ "a:prstDash": { "@_val": "solid" },
33058
+ "a:miter": { "@_lim": "800000" }
33059
+ },
33060
+ {
33061
+ "@_w": "19050",
33062
+ "@_cap": "flat",
33063
+ "@_cmpd": "sng",
33064
+ "@_algn": "ctr",
33065
+ ...phClrSolid,
33066
+ "a:prstDash": { "@_val": "solid" },
33067
+ "a:miter": { "@_lim": "800000" }
33068
+ }
33069
+ ]
33070
+ },
33071
+ "a:effectStyleLst": {
33072
+ "a:effectStyle": [{ "a:effectLst": {} }, { "a:effectLst": {} }, { "a:effectLst": {} }]
33073
+ },
33074
+ "a:bgFillStyleLst": {
33075
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
33076
+ "a:gradFill": []
33077
+ }
33078
+ };
33079
+ }
31176
33080
  };
31177
33081
 
31178
33082
  // src/core/core/runtime/PptxHandlerRuntimeSavePipeline.ts
31179
- var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime33 {
33083
+ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
31180
33084
  /**
31181
33085
  * Resolve the effective conformance class for this save operation.
31182
33086
  *
@@ -31263,6 +33167,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31263
33167
  for (const [masterPath, masterXmlObj] of this.masterXmlMap.entries()) {
31264
33168
  this.zip.file(masterPath, this.builder.build(masterXmlObj));
31265
33169
  }
33170
+ await this.persistThemeParts();
31266
33171
  await this.applyEmbeddedFontPreservation(options?.embeddedFonts);
31267
33172
  if (this.presentationData) {
31268
33173
  this.presentationSaveBuilder.applySaveOptions({
@@ -31352,7 +33257,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31352
33257
  };
31353
33258
 
31354
33259
  // src/core/core/runtime/PptxHandlerRuntimeElementParsing.ts
31355
- var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
33260
+ var PptxHandlerRuntime36 = class _PptxHandlerRuntime extends PptxHandlerRuntime35 {
31356
33261
  /**
31357
33262
  * Parse media data (video/audio path and MIME type) from graphic frame data.
31358
33263
  */
@@ -31574,7 +33479,7 @@ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31574
33479
  };
31575
33480
 
31576
33481
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderLookup.ts
31577
- var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
33482
+ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31578
33483
  findPlaceholderInShapeTree(spTree, expected) {
31579
33484
  if (!spTree) {
31580
33485
  return void 0;
@@ -31727,7 +33632,7 @@ var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
31727
33632
  };
31728
33633
 
31729
33634
  // src/core/core/runtime/PptxHandlerRuntimeGeometryParsing.ts
31730
- var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
33635
+ var PptxHandlerRuntime38 = class extends PptxHandlerRuntime37 {
31731
33636
  parseGeometryAdjustments(prstGeom) {
31732
33637
  if (!prstGeom) {
31733
33638
  return void 0;
@@ -31836,6 +33741,103 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31836
33741
  }
31837
33742
  return handles.length > 0 ? handles : void 0;
31838
33743
  }
33744
+ /**
33745
+ * Extract `a:gdLst`/`a:ahLst`/`a:cxnLst`/`a:rect` raw XML from a `a:custGeom`
33746
+ * node so they can be re-emitted on save when the geometry is edited.
33747
+ * Returns `undefined` when none of these auxiliary children carry data.
33748
+ */
33749
+ extractCustomGeometryRawData(custGeom) {
33750
+ if (!custGeom) {
33751
+ return void 0;
33752
+ }
33753
+ const isNonEmpty = (node) => {
33754
+ if (node === void 0 || node === null) {
33755
+ return false;
33756
+ }
33757
+ if (typeof node !== "object") {
33758
+ return true;
33759
+ }
33760
+ return Object.keys(node).length > 0;
33761
+ };
33762
+ const result = {};
33763
+ const gdLst = custGeom["a:gdLst"];
33764
+ if (isNonEmpty(gdLst)) {
33765
+ result.gdLstXml = gdLst;
33766
+ }
33767
+ const ahLst = custGeom["a:ahLst"];
33768
+ if (isNonEmpty(ahLst)) {
33769
+ result.ahLstXml = ahLst;
33770
+ }
33771
+ const cxnLst = custGeom["a:cxnLst"];
33772
+ if (isNonEmpty(cxnLst)) {
33773
+ result.cxnLstXml = cxnLst;
33774
+ }
33775
+ const rect = custGeom["a:rect"];
33776
+ if (isNonEmpty(rect)) {
33777
+ result.rectXml = rect;
33778
+ }
33779
+ return Object.keys(result).length > 0 ? result : void 0;
33780
+ }
33781
+ /**
33782
+ * Build structured `CustomGeometryPath[]` from a parsed `a:custGeom` node,
33783
+ * including per-path `@fill`/`@stroke`/`@extrusionOk` attributes so they
33784
+ * survive a round-trip when the path list is later regenerated.
33785
+ *
33786
+ * Falls back to SVG → structured-path conversion when no structured path
33787
+ * info is otherwise available.
33788
+ */
33789
+ buildStructuredCustomGeometryPaths(custGeom, pathData, pathWidth, pathHeight) {
33790
+ if (!custGeom) {
33791
+ return void 0;
33792
+ }
33793
+ const pathLst = custGeom["a:pathLst"];
33794
+ if (!pathLst) {
33795
+ return void 0;
33796
+ }
33797
+ const pathNodes = this.ensureArray(pathLst["a:path"]);
33798
+ if (pathNodes.length === 0) {
33799
+ return void 0;
33800
+ }
33801
+ const segments = svgToCustomGeometryPaths(pathData, pathWidth, pathHeight);
33802
+ if (segments.length === 0) {
33803
+ return void 0;
33804
+ }
33805
+ const validFillModes = /* @__PURE__ */ new Set([
33806
+ "norm",
33807
+ "lighten",
33808
+ "lightenLess",
33809
+ "darken",
33810
+ "darkenLess",
33811
+ "none"
33812
+ ]);
33813
+ const parseBoolAttr2 = (value) => {
33814
+ if (value === void 0 || value === null || value === "") {
33815
+ return void 0;
33816
+ }
33817
+ if (value === "1" || value === "true" || value === true) {
33818
+ return true;
33819
+ }
33820
+ if (value === "0" || value === "false" || value === false) {
33821
+ return false;
33822
+ }
33823
+ return void 0;
33824
+ };
33825
+ const target = segments[0];
33826
+ const firstNode = pathNodes[0];
33827
+ const fillAttr = String(firstNode["@_fill"] ?? "").trim();
33828
+ if (validFillModes.has(fillAttr)) {
33829
+ target.fillMode = fillAttr;
33830
+ }
33831
+ const strokeAttr = parseBoolAttr2(firstNode["@_stroke"]);
33832
+ if (strokeAttr !== void 0) {
33833
+ target.stroke = strokeAttr;
33834
+ }
33835
+ const extrusionAttr = parseBoolAttr2(firstNode["@_extrusionOk"]);
33836
+ if (extrusionAttr !== void 0) {
33837
+ target.extrusionOk = extrusionAttr;
33838
+ }
33839
+ return segments;
33840
+ }
31839
33841
  parseCustomGeometry(custGeom, shapeWidth, shapeHeight) {
31840
33842
  if (!custGeom || !custGeom["a:pathLst"] || !custGeom["a:pathLst"]?.["a:path"]) {
31841
33843
  return null;
@@ -31904,7 +33906,7 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31904
33906
  };
31905
33907
 
31906
33908
  // src/core/core/runtime/PptxHandlerRuntimeShapeImageFill.ts
31907
- var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime37 {
33909
+ var PptxHandlerRuntime39 = class _PptxHandlerRuntime extends PptxHandlerRuntime38 {
31908
33910
  /**
31909
33911
  * Parse a shape that has an image fill (a:blipFill inside spPr)
31910
33912
  * This handles shapes like rectangles filled with images (e.g., wood texture backgrounds)
@@ -32104,7 +34106,7 @@ var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
32104
34106
  };
32105
34107
 
32106
34108
  // src/core/core/runtime/PptxHandlerRuntimeTextDefaults.ts
32107
- var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
34109
+ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32108
34110
  /**
32109
34111
  * Apply {@link PlaceholderDefaults} body-level properties to a
32110
34112
  * {@link TextStyle} as fallback values (only sets fields that are
@@ -32228,7 +34230,7 @@ var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
32228
34230
  };
32229
34231
 
32230
34232
  // src/core/core/runtime/PptxHandlerRuntimeBulletParsing.ts
32231
- var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34233
+ var PptxHandlerRuntime41 = class extends PptxHandlerRuntime40 {
32232
34234
  resolveParagraphBulletInfo(paragraph, paragraphIndex, txBody, inheritedTxBody, isBodyPlaceholder = false, slidePath) {
32233
34235
  if (!paragraph) {
32234
34236
  return null;
@@ -32259,7 +34261,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32259
34261
  if (candidate["a:buNone"]) {
32260
34262
  return { none: true };
32261
34263
  }
32262
- if (candidate["a:buChar"] || candidate["a:buAutoNum"] || candidate["a:buBlip"]) {
34264
+ if (candidate["a:buChar"] || candidate["a:buAutoNum"] || candidate["a:buBlip"] || candidate["a:buFontTx"] !== void 0 || candidate["a:buClrTx"] !== void 0 || candidate["a:buSzTx"] !== void 0) {
32263
34265
  resolvedBulletProps = candidate;
32264
34266
  break;
32265
34267
  }
@@ -32273,6 +34275,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32273
34275
  }
32274
34276
  const buFont = resolvedBulletProps["a:buFont"];
32275
34277
  const fontFamily = buFont?.["@_typeface"] ? String(buFont["@_typeface"]) : void 0;
34278
+ const fontInherit = resolvedBulletProps["a:buFontTx"] !== void 0;
32276
34279
  const buSzPct = resolvedBulletProps["a:buSzPct"];
32277
34280
  let sizePercent;
32278
34281
  if (buSzPct?.["@_val"] !== void 0) {
@@ -32289,6 +34292,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32289
34292
  sizePts = ptsRaw / 100;
32290
34293
  }
32291
34294
  }
34295
+ const sizeInherit = resolvedBulletProps["a:buSzTx"] !== void 0;
32292
34296
  const buClr = resolvedBulletProps["a:buClr"];
32293
34297
  let color;
32294
34298
  if (buClr) {
@@ -32297,6 +34301,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32297
34301
  color = String(srgb["@_val"]);
32298
34302
  }
32299
34303
  }
34304
+ const colorInherit = resolvedBulletProps["a:buClrTx"] !== void 0;
32300
34305
  const bulletChar = String(
32301
34306
  resolvedBulletProps["a:buChar"]?.["@_char"] || ""
32302
34307
  );
@@ -32306,7 +34311,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32306
34311
  fontFamily,
32307
34312
  sizePercent,
32308
34313
  sizePts,
32309
- color
34314
+ color,
34315
+ ...fontInherit ? { fontInherit: true } : {},
34316
+ ...colorInherit ? { colorInherit: true } : {},
34317
+ ...sizeInherit ? { sizeInherit: true } : {}
32310
34318
  };
32311
34319
  }
32312
34320
  const autoNum = resolvedBulletProps["a:buAutoNum"];
@@ -32321,7 +34329,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32321
34329
  fontFamily,
32322
34330
  sizePercent,
32323
34331
  sizePts,
32324
- color
34332
+ color,
34333
+ ...fontInherit ? { fontInherit: true } : {},
34334
+ ...colorInherit ? { colorInherit: true } : {},
34335
+ ...sizeInherit ? { sizeInherit: true } : {}
32325
34336
  };
32326
34337
  }
32327
34338
  const buBlip = resolvedBulletProps["a:buBlip"];
@@ -32433,7 +34444,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32433
34444
  };
32434
34445
 
32435
34446
  // src/core/core/runtime/PptxHandlerRuntimeShapeBodyParsing.ts
32436
- var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime40 {
34447
+ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
32437
34448
  /**
32438
34449
  * Parse `a:spLocks` attributes into a structured {@link PptxShapeLocks} object.
32439
34450
  * Returns `undefined` when the node is absent or contains no lock attributes.
@@ -32528,7 +34539,8 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32528
34539
  if (Number.isFinite(spcColRaw) && spcColRaw > 0) {
32529
34540
  textStyle.columnSpacing = spcColRaw / _PptxHandlerRuntime.EMU_PER_PX;
32530
34541
  }
32531
- const hOverflow = String(bodyPr["@_hOverflow"] || "").trim();
34542
+ const hOverflowRaw = bodyPr["@_horzOverflow"] ?? bodyPr["@_hOverflow"];
34543
+ const hOverflow = String(hOverflowRaw || "").trim();
32532
34544
  if (hOverflow === "overflow" || hOverflow === "clip") {
32533
34545
  textStyle.hOverflow = hOverflow;
32534
34546
  }
@@ -32711,7 +34723,7 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32711
34723
  };
32712
34724
 
32713
34725
  // src/core/core/runtime/PptxHandlerRuntimeShapeTextParsing.ts
32714
- var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
34726
+ var PptxHandlerRuntime43 = class _PptxHandlerRuntime extends PptxHandlerRuntime42 {
32715
34727
  /**
32716
34728
  * Resolve paragraph-level styles (alignment, spacing, margins, tabs,
32717
34729
  * level styles) for a single paragraph. Modifies `textStyle` in place
@@ -32893,7 +34905,7 @@ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32893
34905
  };
32894
34906
 
32895
34907
  // src/core/core/runtime/PptxHandlerRuntimeShapeParagraphContentParsing.ts
32896
- var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
34908
+ var PptxHandlerRuntime44 = class extends PptxHandlerRuntime43 {
32897
34909
  /**
32898
34910
  * Collect text content (runs, fields, equations, bullets) for a single
32899
34911
  * paragraph and return text parts + segments. The returned `seedStyle`
@@ -33076,6 +35088,23 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33076
35088
  parts.push("\n");
33077
35089
  segments.push({ text: "\n", style: { ...mergedDefaultRunStyle } });
33078
35090
  }
35091
+ const firstSegmentIndex = segments.length === 0 ? -1 : 0;
35092
+ if (firstSegmentIndex >= 0) {
35093
+ const pPrRaw = p["a:pPr"];
35094
+ const lvlRaw = pPrRaw?.["@_lvl"];
35095
+ if (lvlRaw !== void 0) {
35096
+ const lvlParsed = Number.parseInt(String(lvlRaw), 10);
35097
+ if (Number.isFinite(lvlParsed) && lvlParsed > 0) {
35098
+ segments[firstSegmentIndex].paragraphLevel = Math.min(Math.max(lvlParsed, 0), 8);
35099
+ }
35100
+ }
35101
+ const endParaRPrRaw = p["a:endParaRPr"];
35102
+ if (endParaRPrRaw && typeof endParaRPrRaw === "object") {
35103
+ segments[firstSegmentIndex].endParaRunProperties = {
35104
+ ...endParaRPrRaw
35105
+ };
35106
+ }
35107
+ }
33079
35108
  return { parts, segments, seedStyle };
33080
35109
  }
33081
35110
  /**
@@ -33185,7 +35214,7 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33185
35214
  };
33186
35215
 
33187
35216
  // src/core/core/runtime/PptxHandlerRuntimeShapeParsing.ts
33188
- var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime43 {
35217
+ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
33189
35218
  parseShape(shape, id, slidePath) {
33190
35219
  try {
33191
35220
  const spPr = shape["p:spPr"];
@@ -33221,6 +35250,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33221
35250
  let pathData;
33222
35251
  let pathWidth;
33223
35252
  let pathHeight;
35253
+ let customGeometryRawData;
33224
35254
  const custGeom = effectiveSpPr?.["a:custGeom"];
33225
35255
  if (custGeom) {
33226
35256
  const customPath = this.parseCustomGeometry(
@@ -33233,6 +35263,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33233
35263
  pathData = customPath.pathData;
33234
35264
  pathWidth = customPath.pathWidth;
33235
35265
  pathHeight = customPath.pathHeight;
35266
+ customGeometryRawData = this.extractCustomGeometryRawData(custGeom);
33236
35267
  }
33237
35268
  }
33238
35269
  const geomNode = custGeom ?? effectiveSpPr?.["a:prstGeom"];
@@ -33390,7 +35421,8 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33390
35421
  type: "shape",
33391
35422
  pathData,
33392
35423
  pathWidth,
33393
- pathHeight
35424
+ pathHeight,
35425
+ customGeometryRawData
33394
35426
  };
33395
35427
  } catch (e) {
33396
35428
  console.warn(`[pptx] Skipping shape element (${id}):`, e);
@@ -33400,7 +35432,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33400
35432
  };
33401
35433
 
33402
35434
  // src/core/core/runtime/PptxHandlerRuntimePictureParsing.ts
33403
- var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
35435
+ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
33404
35436
  async parsePicture(pic, id, slidePath) {
33405
35437
  try {
33406
35438
  const spPr = pic["p:spPr"];
@@ -33639,7 +35671,7 @@ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33639
35671
  };
33640
35672
 
33641
35673
  // src/core/core/runtime/PptxHandlerRuntimeSpTreeParsing.ts
33642
- var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
35674
+ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
33643
35675
  /**
33644
35676
  * Known element tag names that appear as direct children of `p:spTree`
33645
35677
  * (or `p:grpSp`) and represent renderable shapes/objects.
@@ -33984,9 +36016,18 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33984
36016
  * Unwrap mc:AlternateContent elements within a shape tree (or group),
33985
36017
  * merging selected branch children into the parent element arrays.
33986
36018
  * Delegates to the standalone alternate-content utility.
36019
+ *
36020
+ * Records each consumed AC envelope in {@link alternateContentBlockByRawXml}
36021
+ * so the save layer can re-emit the original `<mc:Choice>` /
36022
+ * `<mc:Fallback>` shape on dirty save (CC-4).
33987
36023
  */
33988
36024
  unwrapAlternateContent(container) {
33989
- unwrapAlternateContent(container);
36025
+ const blocks = unwrapAlternateContent(container);
36026
+ for (const block of blocks) {
36027
+ for (const ref of block.childRefs) {
36028
+ this.alternateContentBlockByRawXml.set(ref.node, block);
36029
+ }
36030
+ }
33990
36031
  }
33991
36032
  /**
33992
36033
  * Forward declaration – implemented in PptxHandlerRuntimeGroupParsing.
@@ -33997,7 +36038,7 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33997
36038
  };
33998
36039
 
33999
36040
  // src/core/core/runtime/PptxHandlerRuntimeGroupParsing.ts
34000
- var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
36041
+ var PptxHandlerRuntime48 = class _PptxHandlerRuntime extends PptxHandlerRuntime47 {
34001
36042
  async parseGroupShape(group, baseId, slidePath, rawXmlStr) {
34002
36043
  const grpSpPr = group["p:grpSpPr"];
34003
36044
  const xfrm = grpSpPr?.["a:xfrm"];
@@ -34170,7 +36211,7 @@ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34170
36211
  };
34171
36212
 
34172
36213
  // src/core/core/runtime/PptxHandlerRuntimeSlideParsing.ts
34173
- var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
36214
+ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34174
36215
  /**
34175
36216
  * Parse text body from a connector shape (p:cxnSp > p:txBody).
34176
36217
  * Uses the same text run extraction logic as regular shapes but
@@ -34282,7 +36323,7 @@ var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
34282
36323
  };
34283
36324
 
34284
36325
  // src/core/core/runtime/PptxHandlerRuntimeColorAndEffects.ts
34285
- var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
36326
+ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34286
36327
  /**
34287
36328
  * Forward declaration – implemented in PptxHandlerRuntimeThemeProcessing.
34288
36329
  * Re-resolves gradient stops by substituting `phClr` with the given colour.
@@ -34323,8 +36364,9 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34323
36364
  return void 0;
34324
36365
  }
34325
36366
  const resolvedKey = normalized === "phclr" ? "accent1" : normalized;
34326
- if (this.currentSlideClrMapOverride) {
34327
- const remapped = this.currentSlideClrMapOverride[resolvedKey];
36367
+ const overrideMap = this.currentSlideClrMapOverride ?? this.currentMasterClrMap;
36368
+ if (overrideMap) {
36369
+ const remapped = overrideMap[resolvedKey];
34328
36370
  if (remapped) {
34329
36371
  return this.themeColorMap[remapped] || this.getDefaultSchemeColorMap()[remapped];
34330
36372
  }
@@ -34407,7 +36449,7 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34407
36449
  };
34408
36450
 
34409
36451
  // src/core/core/runtime/PptxHandlerRuntimeBackgroundParsing.ts
34410
- var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
36452
+ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34411
36453
  async extractBackgroundImage(slideXml2, slidePath, rootElement = "p:sld") {
34412
36454
  try {
34413
36455
  const bg = slideXml2[rootElement]?.["p:cSld"]?.["p:bg"];
@@ -34618,7 +36660,7 @@ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34618
36660
  };
34619
36661
 
34620
36662
  // src/core/core/runtime/PptxHandlerRuntimeSlideUtils.ts
34621
- var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
36663
+ var PptxHandlerRuntime52 = class extends PptxHandlerRuntime51 {
34622
36664
  /**
34623
36665
  * Retrieve the background gradient from a layout, falling back to master.
34624
36666
  */
@@ -34689,6 +36731,64 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34689
36731
  }
34690
36732
  return void 0;
34691
36733
  }
36734
+ /**
36735
+ * Find the master file path referenced by a layout via its relationships.
36736
+ */
36737
+ findMasterPathForLayoutBase(layoutPath) {
36738
+ const layoutRels = this.slideRelsMap.get(layoutPath);
36739
+ if (!layoutRels) {
36740
+ return void 0;
36741
+ }
36742
+ for (const [, target] of layoutRels.entries()) {
36743
+ if (target.includes("slideMaster")) {
36744
+ const layoutDir = layoutPath.substring(0, layoutPath.lastIndexOf("/") + 1);
36745
+ return target.startsWith("..") ? this.resolvePath(layoutDir, target) : `ppt/${target.replace("../", "")}`;
36746
+ }
36747
+ }
36748
+ return void 0;
36749
+ }
36750
+ /**
36751
+ * Switch the active master state (clrMap + theme color/font/format
36752
+ * scheme) so that scheme-colour resolution for the slide currently
36753
+ * being parsed walks through the correct master.
36754
+ *
36755
+ * Multi-master decks must resolve scheme colours against each slide's
36756
+ * own master rather than always against `masterFiles[0]`.
36757
+ *
36758
+ * Phase 2 Stream B / C-H4.
36759
+ */
36760
+ async setActiveMasterForSlide(slidePath) {
36761
+ const layoutPath = this.findLayoutPathForSlide(slidePath);
36762
+ if (!layoutPath) {
36763
+ this.currentMasterClrMap = null;
36764
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
36765
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
36766
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
36767
+ return;
36768
+ }
36769
+ if (!this.slideRelsMap.has(layoutPath)) {
36770
+ const layoutRelsPath = `${layoutPath.replace("slideLayouts/", "slideLayouts/_rels/")}.rels`;
36771
+ try {
36772
+ await this.loadSlideRelationships(layoutPath, layoutRelsPath);
36773
+ } catch {
36774
+ }
36775
+ }
36776
+ const masterPath = this.findMasterPathForLayoutBase(layoutPath);
36777
+ if (!masterPath) {
36778
+ this.currentMasterClrMap = null;
36779
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
36780
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
36781
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
36782
+ return;
36783
+ }
36784
+ this.currentMasterClrMap = this.masterClrMaps.get(masterPath) ?? null;
36785
+ const masterColorMap = this.masterThemeColorMaps.get(masterPath);
36786
+ this.themeColorMap = masterColorMap ? { ...masterColorMap } : { ...this.globalThemeColorMapSnapshot };
36787
+ const masterFontMap = this.masterThemeFontMaps.get(masterPath);
36788
+ this.themeFontMap = masterFontMap ? { ...masterFontMap } : { ...this.globalThemeFontMapSnapshot };
36789
+ const masterFormatScheme = this.masterThemeFormatSchemes.get(masterPath);
36790
+ this.themeFormatScheme = masterFormatScheme ?? this.globalThemeFormatSchemeSnapshot;
36791
+ }
34692
36792
  /**
34693
36793
  * Extract the `p:bg/@showAnimation` flag from a slide's XML.
34694
36794
  * Returns `true` when the background should animate, `false` when
@@ -34829,7 +36929,7 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34829
36929
  };
34830
36930
 
34831
36931
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderStyles.ts
34832
- var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime51 {
36932
+ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
34833
36933
  /**
34834
36934
  * Parse a single `a:lvlXpPr` node into a structured
34835
36935
  * {@link PlaceholderTextLevelStyle}.
@@ -34950,7 +37050,7 @@ var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
34950
37050
  };
34951
37051
 
34952
37052
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderDefaults.ts
34953
- var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
37053
+ var PptxHandlerRuntime54 = class _PptxHandlerRuntime extends PptxHandlerRuntime53 {
34954
37054
  /**
34955
37055
  * Extract structured {@link PlaceholderDefaults} from a layout or master
34956
37056
  * shape that carries a `p:ph` element.
@@ -35093,7 +37193,118 @@ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35093
37193
  };
35094
37194
 
35095
37195
  // src/core/core/runtime/PptxHandlerRuntimeMasterElements.ts
35096
- var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
37196
+ function parseHeaderFooterFlags(hf) {
37197
+ if (!hf) {
37198
+ return void 0;
37199
+ }
37200
+ const result = {};
37201
+ if (hf["@_hdr"] !== void 0) {
37202
+ result.hasHeader = String(hf["@_hdr"]) !== "0";
37203
+ }
37204
+ if (hf["@_ftr"] !== void 0) {
37205
+ result.hasFooter = String(hf["@_ftr"]) !== "0";
37206
+ }
37207
+ if (hf["@_dt"] !== void 0) {
37208
+ result.hasDateTime = String(hf["@_dt"]) !== "0";
37209
+ }
37210
+ if (hf["@_sldNum"] !== void 0) {
37211
+ result.hasSlideNumber = String(hf["@_sldNum"]) !== "0";
37212
+ }
37213
+ return Object.keys(result).length > 0 ? result : void 0;
37214
+ }
37215
+ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37216
+ /**
37217
+ * Parse a `CT_TextListStyle` node (`a:defPPr` + `a:lvl1pPr` … `a:lvl9pPr`)
37218
+ * into a level-keyed style map. Used for `<p:txStyles>` children
37219
+ * (`p:titleStyle`, `p:bodyStyle`, `p:otherStyle`) — see ECMA-376 §19.3.1.52.
37220
+ */
37221
+ parseTextListStyle(node) {
37222
+ if (!node) {
37223
+ return void 0;
37224
+ }
37225
+ const levels = {};
37226
+ const defParsed = this.parsePlaceholderLevelStyle(node["a:defPPr"]);
37227
+ if (defParsed) {
37228
+ levels[-1] = defParsed;
37229
+ }
37230
+ for (let lvl = 1; lvl <= 9; lvl++) {
37231
+ const parsed = this.parsePlaceholderLevelStyle(
37232
+ node[`a:lvl${lvl}pPr`]
37233
+ );
37234
+ if (parsed) {
37235
+ levels[lvl - 1] = parsed;
37236
+ }
37237
+ }
37238
+ return Object.keys(levels).length > 0 ? levels : void 0;
37239
+ }
37240
+ /**
37241
+ * Parse `<p:txStyles>` from a slide-master XML object into a structured
37242
+ * {@link PptxMasterTextStyles}. Used to populate `PptxSlideMaster.txStyles`
37243
+ * so the title/body/other text-style cascade (P-H1) is visible on the
37244
+ * typed model.
37245
+ */
37246
+ parseMasterTxStyles(masterXml) {
37247
+ const txStyles = masterXml?.["p:txStyles"];
37248
+ if (!txStyles) {
37249
+ return void 0;
37250
+ }
37251
+ const titleStyle = this.parseTextListStyle(txStyles["p:titleStyle"]);
37252
+ const bodyStyle = this.parseTextListStyle(txStyles["p:bodyStyle"]);
37253
+ const otherStyle = this.parseTextListStyle(txStyles["p:otherStyle"]);
37254
+ if (!titleStyle && !bodyStyle && !otherStyle) {
37255
+ return void 0;
37256
+ }
37257
+ const result = {};
37258
+ if (titleStyle) {
37259
+ result.titleStyle = titleStyle;
37260
+ }
37261
+ if (bodyStyle) {
37262
+ result.bodyStyle = bodyStyle;
37263
+ }
37264
+ if (otherStyle) {
37265
+ result.otherStyle = otherStyle;
37266
+ }
37267
+ return result;
37268
+ }
37269
+ /**
37270
+ * Enrich an array of {@link PptxSlideMaster} entries (already produced by
37271
+ * `parseSlideMasters`) with parsed `<p:txStyles>`. Loads each master's XML
37272
+ * once, parses, and caches it in `masterXmlMap` for downstream consumers.
37273
+ *
37274
+ * Also stores the parsed result on the per-master cache so that the
37275
+ * inheritance chain in `applyMasterTextStyleCascade` can find it without
37276
+ * re-parsing.
37277
+ */
37278
+ async enrichSlideMastersWithTxStyles(slideMasters) {
37279
+ for (const master of slideMasters) {
37280
+ try {
37281
+ let masterXmlObj = this.masterXmlMap.get(master.path);
37282
+ if (!masterXmlObj) {
37283
+ const xmlStr = await this.zip.file(master.path)?.async("string");
37284
+ if (!xmlStr) {
37285
+ continue;
37286
+ }
37287
+ masterXmlObj = this.parser.parse(xmlStr);
37288
+ this.masterXmlMap.set(master.path, masterXmlObj);
37289
+ }
37290
+ const sldMaster = masterXmlObj["p:sldMaster"];
37291
+ if (!sldMaster) {
37292
+ continue;
37293
+ }
37294
+ const parsed = this.parseMasterTxStyles(sldMaster);
37295
+ if (parsed) {
37296
+ master.txStyles = parsed;
37297
+ this.masterTxStylesCache.set(master.path, parsed);
37298
+ }
37299
+ const hf = parseHeaderFooterFlags(sldMaster["p:hf"]);
37300
+ if (hf) {
37301
+ master.headerFooter = hf;
37302
+ }
37303
+ } catch (e) {
37304
+ console.warn("Failed to parse master txStyles:", e);
37305
+ }
37306
+ }
37307
+ }
35097
37308
  parsePresentationDefaultTextStyle() {
35098
37309
  const presentation = this.presentationData?.["p:presentation"];
35099
37310
  const defaultTextStyle = presentation?.["p:defaultTextStyle"];
@@ -35249,7 +37460,7 @@ var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
35249
37460
  };
35250
37461
 
35251
37462
  // src/core/core/runtime/PptxHandlerRuntimeLayoutElements.ts
35252
- var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37463
+ var PptxHandlerRuntime56 = class extends PptxHandlerRuntime55 {
35253
37464
  async getLayoutElements(slidePath) {
35254
37465
  const slideRels = this.slideRelsMap.get(slidePath);
35255
37466
  if (!slideRels) {
@@ -35373,10 +37584,10 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35373
37584
  }
35374
37585
  }
35375
37586
  }
35376
- this.currentSlideClrMapOverride = prevClrMapOverride;
35377
37587
  const layoutShowMasterSp = layoutXmlObj["p:sldLayout"]?.["@_showMasterSp"];
35378
37588
  const showMasterSp = layoutShowMasterSp === void 0 || String(layoutShowMasterSp).trim().toLowerCase() !== "0" && String(layoutShowMasterSp).trim().toLowerCase() !== "false";
35379
37589
  const masterElements = showMasterSp ? await this.getMasterElements(layoutPath) : [];
37590
+ this.currentSlideClrMapOverride = prevClrMapOverride;
35380
37591
  const allElements = [...masterElements, ...elements];
35381
37592
  this.layoutCache.set(layoutPath, allElements);
35382
37593
  return allElements;
@@ -35388,7 +37599,7 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35388
37599
  };
35389
37600
 
35390
37601
  // src/core/core/runtime/PptxHandlerRuntimeThemeFormatScheme.ts
35391
- var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime55 {
37602
+ var PptxHandlerRuntime57 = class _PptxHandlerRuntime extends PptxHandlerRuntime56 {
35392
37603
  /**
35393
37604
  * Collect fill-style children from a style list node, preserving
35394
37605
  * document order. Handles `a:solidFill`, `a:gradFill`, `a:pattFill`,
@@ -35630,7 +37841,7 @@ var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35630
37841
  };
35631
37842
 
35632
37843
  // src/core/core/runtime/PptxHandlerRuntimeThemeOverrides.ts
35633
- var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
37844
+ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
35634
37845
  /**
35635
37846
  * Parse the `a:fmtScheme` element from the theme into a structured
35636
37847
  * {@link PptxThemeFormatScheme}. Each sub-list (fillStyleLst, lnStyleLst,
@@ -35827,8 +38038,10 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
35827
38038
  }
35828
38039
  const fontScheme = root["a:fontScheme"];
35829
38040
  if (fontScheme) {
35830
- const majorLatin = fontScheme["a:majorFont"]?.["a:latin"];
35831
- const minorLatin = fontScheme["a:minorFont"]?.["a:latin"];
38041
+ const majorFontNode = fontScheme["a:majorFont"];
38042
+ const minorFontNode = fontScheme["a:minorFont"];
38043
+ const majorLatin = majorFontNode?.["a:latin"];
38044
+ const minorLatin = minorFontNode?.["a:latin"];
35832
38045
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
35833
38046
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
35834
38047
  if (!result.colorOverrides) {
@@ -35851,13 +38064,46 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
35851
38064
  };
35852
38065
 
35853
38066
  // src/core/core/runtime/PptxHandlerRuntimeThemeRefResolution.ts
35854
- var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
38067
+ var COLOR_CHOICE_KEYS2 = [
38068
+ "a:scrgbClr",
38069
+ "a:srgbClr",
38070
+ "a:hslClr",
38071
+ "a:sysClr",
38072
+ "a:schemeClr",
38073
+ "a:prstClr"
38074
+ ];
38075
+ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38076
+ /**
38077
+ * Pull the verbatim colour-choice child out of a style-matrix-reference
38078
+ * element (`a:lnRef`/`a:fillRef`/`a:effectRef`/`a:fontRef`). Returns the
38079
+ * full child object so it can be round-tripped at save time, preserving
38080
+ * any contained colour transforms (`a:lumMod`, `a:tint`, etc.).
38081
+ */
38082
+ extractRefColorXml(refNode) {
38083
+ if (!refNode) {
38084
+ return void 0;
38085
+ }
38086
+ for (const key of COLOR_CHOICE_KEYS2) {
38087
+ const child = refNode[key];
38088
+ if (child !== void 0) {
38089
+ return { [key]: child };
38090
+ }
38091
+ }
38092
+ return void 0;
38093
+ }
35855
38094
  /**
35856
38095
  * Resolve a `a:effectRef` element into concrete effect properties
35857
38096
  * by looking up `@_idx` in the theme format scheme's effect style list.
35858
38097
  */
35859
38098
  resolveThemeEffectRef(refNode, style) {
35860
38099
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
38100
+ if (Number.isFinite(idx) && idx > 0) {
38101
+ style.effectRefIdx = idx;
38102
+ }
38103
+ const overrideColorXml = this.extractRefColorXml(refNode);
38104
+ if (overrideColorXml) {
38105
+ style.effectRefColorXml = overrideColorXml;
38106
+ }
35861
38107
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.effectStyles.length) {
35862
38108
  return;
35863
38109
  }
@@ -35910,6 +38156,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
35910
38156
  resolveThemeLineRef(refNode, style) {
35911
38157
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
35912
38158
  const overrideColor = this.parseColor(refNode);
38159
+ if (Number.isFinite(idx) && idx > 0) {
38160
+ style.lnRefIdx = idx;
38161
+ }
38162
+ const overrideColorXml = this.extractRefColorXml(refNode);
38163
+ if (overrideColorXml) {
38164
+ style.lnRefColorXml = overrideColorXml;
38165
+ }
35913
38166
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.lineStyles.length) {
35914
38167
  style.strokeColor = overrideColor;
35915
38168
  if (overrideColor) {
@@ -35981,6 +38234,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
35981
38234
  */
35982
38235
  resolveThemeFillRef(refNode, style) {
35983
38236
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
38237
+ if (Number.isFinite(idx) && idx > 0) {
38238
+ style.fillRefIdx = idx;
38239
+ }
38240
+ const overrideColorXml = this.extractRefColorXml(refNode);
38241
+ if (overrideColorXml) {
38242
+ style.fillRefColorXml = overrideColorXml;
38243
+ }
35984
38244
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme) {
35985
38245
  style.fillMode = "theme";
35986
38246
  style.fillColor = this.parseColor(refNode);
@@ -36046,7 +38306,7 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36046
38306
  };
36047
38307
 
36048
38308
  // src/core/core/runtime/PptxHandlerRuntimeThemeLoading.ts
36049
- var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38309
+ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36050
38310
  async resolvePrimaryThemePath() {
36051
38311
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36052
38312
  if (!masterFiles || masterFiles.length === 0) {
@@ -36156,62 +38416,97 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36156
38416
  formatScheme: this.themeFormatScheme
36157
38417
  };
36158
38418
  }
36159
- async applySlideMasterColorMap(defaultMap) {
38419
+ /**
38420
+ * Parse every slide master's `<p:clrMap>` element and store the alias
38421
+ * dictionaries on {@link masterClrMaps}. Do *not* mutate
38422
+ * {@link themeColorMap} — alias resolution happens at colour-lookup
38423
+ * time so that:
38424
+ *
38425
+ * 1. The raw theme scheme stays the source of truth (clrMap is a
38426
+ * routing layer, not a colour table).
38427
+ * 2. Multi-master decks resolve each slide against its own master's
38428
+ * clrMap rather than always against `masterFiles[0]`.
38429
+ * 3. Layout `clrMapOvr` semantics work correctly when a slide's master
38430
+ * differs from the deck's first master.
38431
+ *
38432
+ * Phase 2 Stream B / C-H4.
38433
+ */
38434
+ async applySlideMasterColorMap(_defaultMap) {
36160
38435
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36161
38436
  if (!masterFiles || masterFiles.length === 0) {
36162
38437
  return;
36163
38438
  }
36164
- try {
36165
- const masterXml = await masterFiles[0].async("string");
36166
- const masterData = this.parser.parse(masterXml);
36167
- const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
36168
- if (!clrMap) {
36169
- return;
36170
- }
36171
- const aliasKeys = [
36172
- "bg1",
36173
- "tx1",
36174
- "bg2",
36175
- "tx2",
36176
- "accent1",
36177
- "accent2",
36178
- "accent3",
36179
- "accent4",
36180
- "accent5",
36181
- "accent6",
36182
- "hlink",
36183
- "folHlink"
36184
- ];
36185
- for (const aliasKey of aliasKeys) {
36186
- const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
36187
- if (!mappedKey) {
38439
+ const aliasKeys = [
38440
+ "bg1",
38441
+ "tx1",
38442
+ "bg2",
38443
+ "tx2",
38444
+ "accent1",
38445
+ "accent2",
38446
+ "accent3",
38447
+ "accent4",
38448
+ "accent5",
38449
+ "accent6",
38450
+ "hlink",
38451
+ "folHlink"
38452
+ ];
38453
+ for (const file of masterFiles) {
38454
+ try {
38455
+ const masterXml = await file.async("string");
38456
+ const masterData = this.parser.parse(masterXml);
38457
+ const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
38458
+ if (!clrMap) {
36188
38459
  continue;
36189
38460
  }
36190
- const mappedColor = this.themeColorMap[mappedKey] || defaultMap[mappedKey];
36191
- if (mappedColor) {
36192
- this.themeColorMap[aliasKey] = mappedColor;
38461
+ const aliasMap = {};
38462
+ for (const aliasKey of aliasKeys) {
38463
+ const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
38464
+ if (mappedKey) {
38465
+ aliasMap[aliasKey] = mappedKey;
38466
+ }
38467
+ }
38468
+ if (Object.keys(aliasMap).length > 0) {
38469
+ this.masterClrMaps.set(file.name, aliasMap);
36193
38470
  }
38471
+ } catch (error) {
38472
+ console.warn(`Failed to parse slide master color map for ${file.name}:`, error);
36194
38473
  }
36195
- } catch (error) {
36196
- console.warn("Failed to parse slide master color map:", error);
36197
38474
  }
36198
38475
  }
36199
- async loadThemeData() {
36200
- const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
36201
- if (!themeFiles || themeFiles.length === 0) {
36202
- return;
38476
+ /**
38477
+ * Parse a single theme part into structured colour, font, and format
38478
+ * scheme dictionaries. Used both for the global default theme and for
38479
+ * each master's per-master theme (multi-master support).
38480
+ *
38481
+ * Phase 2 Stream B / C-H4.
38482
+ */
38483
+ async parseThemePart(themePath) {
38484
+ const themeFile = this.zip.file(themePath);
38485
+ if (!themeFile) {
38486
+ return null;
36203
38487
  }
36204
- const preferredThemePath = await this.resolvePrimaryThemePath();
36205
- const preferredThemeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) : void 0;
36206
- const themeFile = preferredThemeFile ?? themeFiles[0];
36207
38488
  const themeXml2 = await themeFile.async("string");
38489
+ this.originalThemeXmlByPath.set(themePath, themeXml2);
36208
38490
  const themeData = this.parser.parse(themeXml2);
36209
38491
  const themeRoot = themeData["a:theme"];
36210
38492
  const themeElements = themeRoot?.["a:themeElements"];
36211
38493
  const colorScheme = themeElements?.["a:clrScheme"];
36212
38494
  const fontScheme = themeElements?.["a:fontScheme"];
38495
+ const fmtScheme = themeElements?.["a:fmtScheme"];
38496
+ const themeName = String(themeRoot?.["@_name"] || "").trim();
38497
+ if (themeName) {
38498
+ this.masterThemeNames.set(themePath, themeName);
38499
+ }
38500
+ const colorSchemeName = String(colorScheme?.["@_name"] || "").trim();
38501
+ if (colorSchemeName) {
38502
+ this.masterThemeColorSchemeNames.set(themePath, colorSchemeName);
38503
+ }
38504
+ const fontSchemeName = String(fontScheme?.["@_name"] || "").trim();
38505
+ if (fontSchemeName) {
38506
+ this.masterThemeFontSchemeNames.set(themePath, fontSchemeName);
38507
+ }
36213
38508
  const defaultMap = this.getDefaultSchemeColorMap();
36214
- this.themeColorMap = { ...defaultMap };
38509
+ const colorMap = { ...defaultMap };
36215
38510
  if (colorScheme) {
36216
38511
  const schemeKeys = [
36217
38512
  "dk1",
@@ -36231,39 +38526,196 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36231
38526
  const colorNode = colorScheme[`a:${key}`];
36232
38527
  const parsed = this.parseColorChoice(colorNode);
36233
38528
  if (parsed) {
36234
- this.themeColorMap[key] = parsed;
38529
+ colorMap[key] = parsed;
36235
38530
  }
36236
38531
  }
36237
38532
  }
36238
- this.themeColorMap["tx1"] = this.themeColorMap["dk1"] || defaultMap["dk1"];
36239
- this.themeColorMap["bg1"] = this.themeColorMap["lt1"] || defaultMap["lt1"];
36240
- this.themeColorMap["tx2"] = this.themeColorMap["dk2"] || defaultMap["dk2"];
36241
- this.themeColorMap["bg2"] = this.themeColorMap["lt2"] || defaultMap["lt2"];
36242
- await this.applySlideMasterColorMap(defaultMap);
36243
- const majorLatin = fontScheme?.["a:majorFont"]?.["a:latin"];
36244
- const minorLatin = fontScheme?.["a:minorFont"]?.["a:latin"];
38533
+ colorMap["tx1"] = colorMap["dk1"] || defaultMap["dk1"];
38534
+ colorMap["bg1"] = colorMap["lt1"] || defaultMap["lt1"];
38535
+ colorMap["tx2"] = colorMap["dk2"] || defaultMap["dk2"];
38536
+ colorMap["bg2"] = colorMap["lt2"] || defaultMap["lt2"];
38537
+ const majorFontNode = fontScheme?.["a:majorFont"];
38538
+ const minorFontNode = fontScheme?.["a:minorFont"];
38539
+ const majorLatin = majorFontNode?.["a:latin"];
38540
+ const minorLatin = minorFontNode?.["a:latin"];
36245
38541
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
36246
38542
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
36247
- this.themeFontMap = {};
38543
+ const fontMap = {};
36248
38544
  if (majorFont) {
36249
- this.themeFontMap["mj-lt"] = majorFont;
36250
- this.themeFontMap["mj-ea"] = majorFont;
36251
- this.themeFontMap["mj-cs"] = majorFont;
38545
+ fontMap["mj-lt"] = majorFont;
38546
+ fontMap["mj-ea"] = majorFont;
38547
+ fontMap["mj-cs"] = majorFont;
36252
38548
  }
36253
38549
  if (minorFont) {
36254
- this.themeFontMap["mn-lt"] = minorFont;
36255
- this.themeFontMap["mn-ea"] = minorFont;
36256
- this.themeFontMap["mn-cs"] = minorFont;
38550
+ fontMap["mn-lt"] = minorFont;
38551
+ fontMap["mn-ea"] = minorFont;
38552
+ fontMap["mn-cs"] = minorFont;
36257
38553
  }
36258
- const fmtScheme = themeElements?.["a:fmtScheme"];
36259
- if (fmtScheme) {
36260
- this.themeFormatScheme = this.parseFormatScheme(fmtScheme);
38554
+ const majorEa = this.normalizeTypefaceToken(
38555
+ String(majorFontNode?.["a:ea"]?.["@_typeface"] || "")
38556
+ );
38557
+ if (majorEa) {
38558
+ fontMap["mj-ea"] = majorEa;
38559
+ }
38560
+ const majorCs = this.normalizeTypefaceToken(
38561
+ String(majorFontNode?.["a:cs"]?.["@_typeface"] || "")
38562
+ );
38563
+ if (majorCs) {
38564
+ fontMap["mj-cs"] = majorCs;
38565
+ }
38566
+ const minorEa = this.normalizeTypefaceToken(
38567
+ String(minorFontNode?.["a:ea"]?.["@_typeface"] || "")
38568
+ );
38569
+ if (minorEa) {
38570
+ fontMap["mn-ea"] = minorEa;
38571
+ }
38572
+ const minorCs = this.normalizeTypefaceToken(
38573
+ String(minorFontNode?.["a:cs"]?.["@_typeface"] || "")
38574
+ );
38575
+ if (minorCs) {
38576
+ fontMap["mn-cs"] = minorCs;
38577
+ }
38578
+ const majorScripts = this.collectFontScriptOverrides(majorFontNode);
38579
+ if (Object.keys(majorScripts).length > 0) {
38580
+ this.masterThemeMajorFontScripts.set(themePath, majorScripts);
38581
+ }
38582
+ const minorScripts = this.collectFontScriptOverrides(minorFontNode);
38583
+ if (Object.keys(minorScripts).length > 0) {
38584
+ this.masterThemeMinorFontScripts.set(themePath, minorScripts);
38585
+ }
38586
+ const objectDefaultsNode = themeRoot?.["a:objectDefaults"];
38587
+ if (objectDefaultsNode) {
38588
+ const od = {
38589
+ spDef: objectDefaultsNode["a:spDef"],
38590
+ lnDef: objectDefaultsNode["a:lnDef"],
38591
+ txDef: objectDefaultsNode["a:txDef"]
38592
+ };
38593
+ if (od.spDef !== void 0 || od.lnDef !== void 0 || od.txDef !== void 0) {
38594
+ this.masterThemeObjectDefaults.set(themePath, od);
38595
+ }
38596
+ }
38597
+ const extraClrSchemeLst = themeRoot?.["a:extraClrSchemeLst"];
38598
+ if (extraClrSchemeLst !== void 0) {
38599
+ this.masterThemeExtraClrSchemeLst.set(themePath, extraClrSchemeLst);
38600
+ }
38601
+ const custClrLst = themeRoot?.["a:custClrLst"];
38602
+ if (custClrLst !== void 0) {
38603
+ this.masterThemeCustClrLst.set(themePath, custClrLst);
38604
+ }
38605
+ const themeExtLst = themeRoot?.["a:extLst"];
38606
+ if (themeExtLst !== void 0) {
38607
+ this.masterThemeExtLst.set(themePath, themeExtLst);
38608
+ }
38609
+ const formatScheme = fmtScheme ? this.parseFormatScheme(fmtScheme) : void 0;
38610
+ return { colorMap, fontMap, formatScheme };
38611
+ }
38612
+ /**
38613
+ * Parse `<a:font script="…" typeface="…"/>` children of a major or
38614
+ * minor font node into a `script -> typeface` dictionary.
38615
+ *
38616
+ * fast-xml-parser collapses repeated tags into arrays, so iterate
38617
+ * over the array form regardless of how many siblings are present.
38618
+ *
38619
+ * Phase 4 Stream A / M4.
38620
+ */
38621
+ collectFontScriptOverrides(fontNode) {
38622
+ const overrides = {};
38623
+ if (!fontNode) {
38624
+ return overrides;
38625
+ }
38626
+ const fontEntries = this.ensureArray(fontNode["a:font"]);
38627
+ for (const entry of fontEntries) {
38628
+ const script = String(entry?.["@_script"] || "").trim();
38629
+ const typeface = this.normalizeTypefaceToken(String(entry?.["@_typeface"] || ""));
38630
+ if (script && typeface) {
38631
+ overrides[script] = typeface;
38632
+ }
38633
+ }
38634
+ return overrides;
38635
+ }
38636
+ /**
38637
+ * Resolve the theme file path referenced by a given master's `.rels`.
38638
+ * Returns `undefined` when the master has no theme relationship.
38639
+ */
38640
+ async resolveThemePathForMaster(masterPath) {
38641
+ const relsPath = masterPath.replace(
38642
+ /ppt\/slideMasters\/(slideMaster\d+)\.xml/,
38643
+ "ppt/slideMasters/_rels/$1.xml.rels"
38644
+ );
38645
+ const relsXml = this.zip.file(relsPath);
38646
+ if (!relsXml) {
38647
+ return void 0;
38648
+ }
38649
+ const relsData = this.parser.parse(await relsXml.async("string"));
38650
+ const relNodes = this.ensureArray(relsData?.Relationships?.Relationship);
38651
+ for (const rel of relNodes) {
38652
+ const target = String(rel["@_Target"] || "");
38653
+ if (!target.includes("theme")) {
38654
+ continue;
38655
+ }
38656
+ const themePath = target.startsWith("..") ? this.resolvePath(masterPath.substring(0, masterPath.lastIndexOf("/") + 1), target) : target.startsWith("/") ? target.slice(1) : `ppt/${target.replace(/^\.?\//, "")}`;
38657
+ if (themePath.startsWith("ppt/theme/")) {
38658
+ return themePath;
38659
+ }
38660
+ }
38661
+ return void 0;
38662
+ }
38663
+ /**
38664
+ * Populate {@link masterThemeColorMaps}, {@link masterThemeFontMaps},
38665
+ * and {@link masterThemeFormatSchemes} for every slide master in the
38666
+ * deck. Multi-master support — Phase 2 Stream B / C-H4.
38667
+ */
38668
+ async loadPerMasterThemes() {
38669
+ const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
38670
+ if (!masterFiles || masterFiles.length === 0) {
38671
+ return;
38672
+ }
38673
+ for (const file of masterFiles) {
38674
+ try {
38675
+ const themePath = await this.resolveThemePathForMaster(file.name);
38676
+ if (!themePath) {
38677
+ continue;
38678
+ }
38679
+ this.masterThemePaths.set(file.name, themePath);
38680
+ const parsed = await this.parseThemePart(themePath);
38681
+ if (!parsed) {
38682
+ continue;
38683
+ }
38684
+ this.masterThemeColorMaps.set(file.name, parsed.colorMap);
38685
+ this.masterThemeFontMaps.set(file.name, parsed.fontMap);
38686
+ if (parsed.formatScheme) {
38687
+ this.masterThemeFormatSchemes.set(file.name, parsed.formatScheme);
38688
+ }
38689
+ } catch (error) {
38690
+ console.warn(`Failed to load per-master theme for ${file.name}:`, error);
38691
+ }
38692
+ }
38693
+ }
38694
+ async loadThemeData() {
38695
+ const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
38696
+ if (!themeFiles || themeFiles.length === 0) {
38697
+ return;
36261
38698
  }
38699
+ const preferredThemePath = await this.resolvePrimaryThemePath();
38700
+ const themeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) ?? themeFiles[0] : themeFiles[0];
38701
+ const parsed = await this.parseThemePart(themeFile.name);
38702
+ if (parsed) {
38703
+ this.themeColorMap = parsed.colorMap;
38704
+ this.themeFontMap = parsed.fontMap;
38705
+ if (parsed.formatScheme) {
38706
+ this.themeFormatScheme = parsed.formatScheme;
38707
+ }
38708
+ }
38709
+ await this.applySlideMasterColorMap(this.getDefaultSchemeColorMap());
38710
+ await this.loadPerMasterThemes();
38711
+ this.globalThemeColorMapSnapshot = { ...this.themeColorMap };
38712
+ this.globalThemeFontMapSnapshot = { ...this.themeFontMap };
38713
+ this.globalThemeFormatSchemeSnapshot = this.themeFormatScheme;
36262
38714
  }
36263
38715
  };
36264
38716
 
36265
38717
  // src/core/core/runtime/PptxHandlerRuntimeThemeProcessing.ts
36266
- var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
38718
+ var PptxHandlerRuntime61 = class extends PptxHandlerRuntime60 {
36267
38719
  // ---------------------------------------------------------------------------
36268
38720
  // Theme editing — update colour scheme, font scheme, and name in the zip
36269
38721
  // ---------------------------------------------------------------------------
@@ -36413,7 +38865,7 @@ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36413
38865
  };
36414
38866
 
36415
38867
  // src/core/core/runtime/PptxHandlerRuntimeComments.ts
36416
- var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime60 {
38868
+ var PptxHandlerRuntime62 = class _PptxHandlerRuntime extends PptxHandlerRuntime61 {
36417
38869
  /**
36418
38870
  * Parse modern threaded comments (PowerPoint 2019+ / Office 365).
36419
38871
  * Modern comments use `p188:cmLst`/`p15:cmLst` roots within
@@ -36646,7 +39098,7 @@ var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
36646
39098
  };
36647
39099
 
36648
39100
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtXmlUtils.ts
36649
- var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
39101
+ var PptxHandlerRuntime63 = class extends PptxHandlerRuntime62 {
36650
39102
  async readXmlPartByRelationshipId(slidePath, relationshipId) {
36651
39103
  const normalizedRelationshipId = String(relationshipId || "").trim();
36652
39104
  if (normalizedRelationshipId.length === 0) {
@@ -36801,7 +39253,7 @@ var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
36801
39253
  };
36802
39254
 
36803
39255
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtParsing.ts
36804
- var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime62 {
39256
+ var PptxHandlerRuntime64 = class _PptxHandlerRuntime extends PptxHandlerRuntime63 {
36805
39257
  /**
36806
39258
  * Parse quick style from `ppt/diagrams/quickStyles*.xml`.
36807
39259
  */
@@ -36960,7 +39412,7 @@ var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
36960
39412
  };
36961
39413
 
36962
39414
  // src/core/core/runtime/PptxHandlerRuntimeSmartArt.ts
36963
- var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
39415
+ var PptxHandlerRuntime65 = class _PptxHandlerRuntime extends PptxHandlerRuntime64 {
36964
39416
  async getSmartArtDataForGraphicFrame(slidePath, graphicFrame) {
36965
39417
  const graphicData = this.xmlLookupService.getChildByLocalName(
36966
39418
  this.xmlLookupService.getChildByLocalName(graphicFrame, "graphic"),
@@ -37011,10 +39463,14 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37011
39463
  const layoutPart = layoutRelationshipId.length > 0 ? await this.readXmlPartByRelationshipId(slidePath, layoutRelationshipId) : void 0;
37012
39464
  const layoutType = layoutPart?.partPath?.split("/").pop()?.replace(/\.[^.]+$/, "") || void 0;
37013
39465
  const chrome = this.parseSmartArtChrome(dataModel);
37014
- const drawingRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37015
- const drawingShapes = await this.parseSmartArtDrawingShapes(slidePath, drawingRelationshipId);
37016
39466
  const colorsRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37017
39467
  const colorTransform = await this.parseSmartArtColorTransform(slidePath, colorsRelationshipId);
39468
+ const drawingResolution = await this.resolveSmartArtDrawingPart(
39469
+ slidePath,
39470
+ diagramDataRelationshipId
39471
+ );
39472
+ const drawingShapes = drawingResolution ? await this.parseSmartArtDrawingShapesFromPath(drawingResolution.path) : [];
39473
+ const drawingRelationshipId = drawingResolution?.relId;
37018
39474
  const styleRelationshipId = String(relationshipIds["@_r:qs"] || "").trim();
37019
39475
  const quickStyle = await this.parseSmartArtQuickStyle(slidePath, styleRelationshipId);
37020
39476
  return {
@@ -37026,11 +39482,87 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37026
39482
  colorTransform,
37027
39483
  quickStyle,
37028
39484
  dataRelId: diagramDataRelationshipId,
37029
- drawingRelId: drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
39485
+ drawingRelId: drawingRelationshipId && drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
37030
39486
  colorsRelId: colorsRelationshipId.length > 0 ? colorsRelationshipId : void 0,
37031
39487
  styleRelId: styleRelationshipId.length > 0 ? styleRelationshipId : void 0
37032
39488
  };
37033
39489
  }
39490
+ /**
39491
+ * Resolve the SmartArt drawing-shapes part path + relationship id.
39492
+ *
39493
+ * Strategy:
39494
+ * 1. Locate the data-model part's rels file
39495
+ * (`ppt/diagrams/_rels/data*.xml.rels`) via `slideRelsMap`.
39496
+ * 2. Find a relationship whose `Type` matches the `…/diagramDrawing`
39497
+ * URI. PowerPoint emits this for any deck that has had its
39498
+ * SmartArt rendered to drawing shapes.
39499
+ * 3. Return the matched part path (so the caller can load it
39500
+ * directly) and the relationship id (for round-trip preservation).
39501
+ */
39502
+ async resolveSmartArtDrawingPart(slidePath, diagramDataRelationshipId) {
39503
+ if (diagramDataRelationshipId.length === 0) {
39504
+ return void 0;
39505
+ }
39506
+ const slideRels = this.slideRelsMap.get(slidePath);
39507
+ const dataTarget = slideRels?.get(diagramDataRelationshipId);
39508
+ if (!dataTarget) {
39509
+ return void 0;
39510
+ }
39511
+ const dataPath = this.resolveImagePath(slidePath, dataTarget);
39512
+ const dataDir = dataPath.replace(/\/[^/]+$/, "");
39513
+ const dataFile = dataPath.split("/").pop() ?? "";
39514
+ const dataRelsPath = `${dataDir}/_rels/${dataFile}.rels`;
39515
+ const relsXml = await this.zip.file(dataRelsPath)?.async("string");
39516
+ if (!relsXml) {
39517
+ return void 0;
39518
+ }
39519
+ try {
39520
+ const parsed = this.parser.parse(relsXml);
39521
+ const relsRoot = parsed["Relationships"];
39522
+ if (!relsRoot) {
39523
+ return void 0;
39524
+ }
39525
+ const rels = this.ensureArray(relsRoot["Relationship"]);
39526
+ const drawingRel = rels.find(
39527
+ (rel) => String(rel?.["@_Type"] || "").endsWith("/diagramDrawing")
39528
+ );
39529
+ const id = String(drawingRel?.["@_Id"] || "").trim();
39530
+ const target = String(drawingRel?.["@_Target"] || "").trim();
39531
+ if (id.length === 0 || target.length === 0) {
39532
+ return void 0;
39533
+ }
39534
+ const drawingPath = this.resolveImagePath(dataPath, target);
39535
+ return { relId: id, path: drawingPath };
39536
+ } catch {
39537
+ return void 0;
39538
+ }
39539
+ }
39540
+ /**
39541
+ * Parse SmartArt drawing shapes given an absolute part path.
39542
+ *
39543
+ * Wraps `parseSmartArtDrawingShapes` (which expects a slide-relative
39544
+ * relationship id) with a path-based lookup so the resolution layer
39545
+ * can pull the part from anywhere in the package.
39546
+ */
39547
+ async parseSmartArtDrawingShapesFromPath(drawingPath) {
39548
+ const xmlString = await this.zip.file(drawingPath)?.async("string");
39549
+ if (!xmlString) {
39550
+ return [];
39551
+ }
39552
+ try {
39553
+ const xml = this.parser.parse(xmlString);
39554
+ const drawing = this.xmlLookupService.getChildByLocalName(xml, "drawing");
39555
+ const spTree = this.xmlLookupService.getChildByLocalName(drawing || xml, "spTree");
39556
+ if (!spTree) {
39557
+ return [];
39558
+ }
39559
+ const shapes = this.xmlLookupService.getChildrenArrayByLocalName(spTree, "sp");
39560
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
39561
+ return shapes.map((sp, index) => this.parseDrawingShape(sp, index, emuPerPx)).filter((entry) => entry !== null);
39562
+ } catch {
39563
+ return [];
39564
+ }
39565
+ }
37034
39566
  parseSmartArtConnections(dataModel) {
37035
39567
  const connectionList = this.xmlLookupService.getChildByLocalName(dataModel, "cxnLst");
37036
39568
  const rawConnections = this.xmlLookupService.getChildrenArrayByLocalName(connectionList, "cxn");
@@ -37061,7 +39593,7 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37061
39593
  };
37062
39594
 
37063
39595
  // src/core/core/runtime/PptxHandlerRuntimeChartDetection.ts
37064
- var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
39596
+ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37065
39597
  detectChartType(plotArea) {
37066
39598
  if (!plotArea) {
37067
39599
  return "unknown";
@@ -37170,7 +39702,7 @@ var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
37170
39702
  };
37171
39703
 
37172
39704
  // src/core/core/runtime/PptxHandlerRuntimeChartParsingHelpers.ts
37173
- var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
39705
+ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37174
39706
  /**
37175
39707
  * Parse `c:plotVisOnly` from the chart root element.
37176
39708
  *
@@ -37231,7 +39763,7 @@ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37231
39763
  };
37232
39764
 
37233
39765
  // src/core/core/runtime/PptxHandlerRuntimeChartExternalData.ts
37234
- var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
39766
+ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37235
39767
  /**
37236
39768
  * Parse `c:externalData` from the chart's `c:chartSpace` and resolve
37237
39769
  * the external relationship target from the chart part's .rels file.
@@ -37347,7 +39879,7 @@ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37347
39879
  };
37348
39880
 
37349
39881
  // src/core/core/runtime/PptxHandlerRuntimeChartColorStyle.ts
37350
- var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
39882
+ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37351
39883
  /**
37352
39884
  * Parse the Office 2013+ chart color style part (`chartColorStyle*.xml`)
37353
39885
  * referenced from the chart's relationships.
@@ -37454,7 +39986,7 @@ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37454
39986
  };
37455
39987
 
37456
39988
  // src/core/core/runtime/PptxHandlerRuntimeChartParsing.ts
37457
- var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
39989
+ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
37458
39990
  /**
37459
39991
  * Parse chart data from a graphic frame element on a slide.
37460
39992
  *
@@ -37701,7 +40233,7 @@ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37701
40233
  };
37702
40234
 
37703
40235
  // src/core/core/runtime/PptxHandlerRuntimePresentationStructure.ts
37704
- var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
40236
+ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
37705
40237
  parseEditorAnimations(slideXml2) {
37706
40238
  return this.editorAnimationService.parseEditorAnimations(slideXml2);
37707
40239
  }
@@ -37946,7 +40478,7 @@ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
37946
40478
  };
37947
40479
 
37948
40480
  // src/core/core/runtime/PptxHandlerRuntimeEmbeddedFonts.ts
37949
- var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
40481
+ var PptxHandlerRuntime72 = class extends PptxHandlerRuntime71 {
37950
40482
  async getEmbeddedFonts() {
37951
40483
  const embeddedFontEntries = this.ensureArray(
37952
40484
  this.presentationData?.["p:presentation"]?.["p:embeddedFontLst"]?.["p:embeddedFont"]
@@ -38118,7 +40650,7 @@ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
38118
40650
  };
38119
40651
 
38120
40652
  // src/core/core/runtime/PptxHandlerRuntimeLoadSession.ts
38121
- var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime71 {
40653
+ var PptxHandlerRuntime73 = class _PptxHandlerRuntime extends PptxHandlerRuntime72 {
38122
40654
  isZipContainer(data) {
38123
40655
  const bytes = new Uint8Array(data);
38124
40656
  if (bytes.byteLength < 4) {
@@ -38161,6 +40693,23 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38161
40693
  this.imageDataCache.clear();
38162
40694
  this.themeColorMap = {};
38163
40695
  this.themeFontMap = {};
40696
+ this.masterClrMaps.clear();
40697
+ this.masterThemeColorMaps.clear();
40698
+ this.masterThemeFontMaps.clear();
40699
+ this.masterThemeFormatSchemes.clear();
40700
+ this.masterThemePaths.clear();
40701
+ this.masterThemeMajorFontScripts.clear();
40702
+ this.masterThemeMinorFontScripts.clear();
40703
+ this.masterThemeNames.clear();
40704
+ this.masterThemeFontSchemeNames.clear();
40705
+ this.masterThemeColorSchemeNames.clear();
40706
+ this.originalThemeXmlByPath.clear();
40707
+ this.dirtyThemePaths.clear();
40708
+ this.masterThemeObjectDefaults.clear();
40709
+ this.masterThemeExtraClrSchemeLst.clear();
40710
+ this.masterThemeCustClrLst.clear();
40711
+ this.masterThemeExtLst.clear();
40712
+ this.currentMasterClrMap = null;
38164
40713
  this.presentationDefaultTextStyle = void 0;
38165
40714
  this.commentAuthorMap.clear();
38166
40715
  this.commentAuthorDetails.clear();
@@ -38315,6 +40864,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38315
40864
  setCurrentSlideClrMapOverride: (override) => {
38316
40865
  this.currentSlideClrMapOverride = override;
38317
40866
  },
40867
+ setActiveMasterForSlide: (slidePath) => this.setActiveMasterForSlide(slidePath),
38318
40868
  findLayoutPathForSlide: (slidePath) => this.findLayoutPathForSlide(slidePath),
38319
40869
  loadThemeOverride: (partBasePath) => this.loadThemeOverride(partBasePath),
38320
40870
  applyThemeOverrideState: (override) => this.applyThemeOverrideState(override),
@@ -38345,7 +40895,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38345
40895
  };
38346
40896
 
38347
40897
  // src/core/core/runtime/PptxHandlerRuntimeLoadPipeline.ts
38348
- var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
40898
+ var PptxHandlerRuntime74 = class extends PptxHandlerRuntime73 {
38349
40899
  async buildLoadData(presentationState, slidesWithWarnings) {
38350
40900
  const headerFooter = this.extractHeaderFooter();
38351
40901
  const presentationProperties = await this.parsePresentationProperties();
@@ -38357,6 +40907,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38357
40907
  const notesMaster = await this.parseNotesMaster();
38358
40908
  const handoutMaster = await this.parseHandoutMaster();
38359
40909
  const slideMasters = await this.parseSlideMasters();
40910
+ await this.enrichSlideMastersWithTxStyles(slideMasters);
38360
40911
  const tags = await this.parseTags();
38361
40912
  const customProperties = await this.parseCustomProperties();
38362
40913
  const coreProperties = await this.parseCoreProperties();
@@ -38691,7 +41242,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38691
41242
  };
38692
41243
 
38693
41244
  // src/core/core/runtime/PptxHandlerRuntimeImplementation.ts
38694
- var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime73 {
41245
+ var PptxHandlerRuntime75 = class _PptxHandlerRuntime extends PptxHandlerRuntime74 {
38695
41246
  constructor(dependencyFactory = new PptxRuntimeDependencyFactory()) {
38696
41247
  super();
38697
41248
  this.dependencyFactory = dependencyFactory;
@@ -38739,6 +41290,9 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38739
41290
  extractGradientPathType: (gradFill) => this.colorStyleCodec.extractGradientPathType(gradFill),
38740
41291
  extractGradientFocalPoint: (gradFill) => this.colorStyleCodec.extractGradientFocalPoint(gradFill),
38741
41292
  extractGradientFillToRect: (gradFill) => this.colorStyleCodec.extractGradientFillToRect(gradFill),
41293
+ extractGradientFlip: (gradFill) => this.colorStyleCodec.extractGradientFlip(gradFill),
41294
+ extractGradientRotWithShape: (gradFill) => this.colorStyleCodec.extractGradientRotWithShape(gradFill),
41295
+ extractGradientScaled: (gradFill) => this.colorStyleCodec.extractGradientScaled(gradFill),
38742
41296
  normalizeStrokeDashType: (value) => this.normalizeStrokeDashType(value),
38743
41297
  normalizeConnectorArrowType: (value) => this.normalizeConnectorArrowType(value),
38744
41298
  ensureArray: (value) => this.ensureArray(value),
@@ -38812,11 +41366,11 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38812
41366
  };
38813
41367
 
38814
41368
  // src/core/core/PptxHandlerRuntime.ts
38815
- var PptxHandlerRuntime75 = class extends PptxHandlerRuntime74 {
41369
+ var PptxHandlerRuntime76 = class extends PptxHandlerRuntime75 {
38816
41370
  };
38817
41371
 
38818
41372
  // src/core/core/PptxHandlerRuntimeFactory.ts
38819
- var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime75();
41373
+ var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime76();
38820
41374
 
38821
41375
  // src/core/PptxHandlerCore.ts
38822
41376
  var PptxHandlerCore = class {