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
@@ -4426,12 +4426,25 @@ function svgToCustomGeometryPaths(pathData, width, height) {
4426
4426
  function pointToXml(pt) {
4427
4427
  return { "@_x": String(Math.round(pt.x)), "@_y": String(Math.round(pt.y)) };
4428
4428
  }
4429
- function customGeometryPathsToXml(paths) {
4429
+ function customGeometryPathsToXml(paths, rawData) {
4430
4430
  const xmlPaths = paths.map((path2) => {
4431
4431
  const pathXml = {
4432
4432
  "@_w": String(Math.round(path2.width)),
4433
4433
  "@_h": String(Math.round(path2.height))
4434
4434
  };
4435
+ if (path2.fillMode) {
4436
+ pathXml["@_fill"] = path2.fillMode;
4437
+ }
4438
+ if (path2.stroke === false) {
4439
+ pathXml["@_stroke"] = "0";
4440
+ } else if (path2.stroke === true) {
4441
+ pathXml["@_stroke"] = "1";
4442
+ }
4443
+ if (path2.extrusionOk === true) {
4444
+ pathXml["@_extrusionOk"] = "1";
4445
+ } else if (path2.extrusionOk === false) {
4446
+ pathXml["@_extrusionOk"] = "0";
4447
+ }
4435
4448
  const moveToList = [];
4436
4449
  const lnToList = [];
4437
4450
  const cubicBezToList = [];
@@ -4489,12 +4502,12 @@ function customGeometryPathsToXml(paths) {
4489
4502
  }
4490
4503
  return pathXml;
4491
4504
  });
4492
- return {
4505
+ const result = {
4493
4506
  "a:avLst": {},
4494
- "a:gdLst": {},
4495
- "a:ahLst": {},
4496
- "a:cxnLst": {},
4497
- "a:rect": {
4507
+ "a:gdLst": rawData?.gdLstXml ?? {},
4508
+ "a:ahLst": rawData?.ahLstXml ?? {},
4509
+ "a:cxnLst": rawData?.cxnLstXml ?? {},
4510
+ "a:rect": rawData?.rectXml ?? {
4498
4511
  "@_l": "l",
4499
4512
  "@_t": "t",
4500
4513
  "@_r": "r",
@@ -4504,6 +4517,7 @@ function customGeometryPathsToXml(paths) {
4504
4517
  "a:path": xmlPaths.length === 1 ? xmlPaths[0] : xmlPaths
4505
4518
  }
4506
4519
  };
4520
+ return result;
4507
4521
  }
4508
4522
 
4509
4523
  // src/core/builders/sdk/ElementFactory.ts
@@ -5188,7 +5202,11 @@ function isoNow() {
5188
5202
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z");
5189
5203
  }
5190
5204
  var STANDARD_LAYOUTS = [
5191
- { name: "Title Slide", type: "ctrTitle" },
5205
+ // `type` is ST_SlideLayoutType per ECMA-376 §19.7.15. The Title Slide
5206
+ // layout uses `title`; `ctrTitle` is a placeholder type (ST_PlaceholderType)
5207
+ // and is not valid here — PowerPoint's OPC loader rejects the package
5208
+ // with ERROR_FILE_CORRUPT (0x80070570) when it sees it.
5209
+ { name: "Title Slide", type: "title" },
5192
5210
  { name: "Title and Content", type: "obj" },
5193
5211
  { name: "Section Header", type: "secHead" },
5194
5212
  { name: "Two Content", type: "twoObj" },
@@ -5222,7 +5240,7 @@ ${slideOverrides}
5222
5240
  <Override PartName="/ppt/viewProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml"/>
5223
5241
  <Override PartName="/ppt/tableStyles.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"/>
5224
5242
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
5225
- <Override PartName="/docProps/app.xml" ContentType="application/vnd.ms-officedocument.extended-properties+xml"/>
5243
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
5226
5244
  </Types>`;
5227
5245
  }
5228
5246
  function rootRelsXml() {
@@ -5246,6 +5264,9 @@ ${Array.from(
5246
5264
  <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
5247
5265
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
5248
5266
  xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
5267
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5268
+ xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main"
5269
+ xmlns:p15="http://schemas.microsoft.com/office/powerpoint/2012/main"
5249
5270
  saveSubsetFonts="1">
5250
5271
  <p:sldMasterIdLst>
5251
5272
  <p:sldMasterId id="2147483648" r:id="rId1"/>
@@ -6548,10 +6569,41 @@ var PptxPresentationSlidesReconciler = class {
6548
6569
  "@_r:id": slide.rId
6549
6570
  };
6550
6571
  });
6551
- presentation["p:sldIdLst"] = slideIdList;
6552
- input.presentationData["p:presentation"] = presentation;
6572
+ input.presentationData["p:presentation"] = this.insertSlideIdListInOrder(
6573
+ presentation,
6574
+ slideIdList
6575
+ );
6553
6576
  }
6554
6577
  }
6578
+ /**
6579
+ * Return a new presentation XML object whose `p:sldIdLst` child sits in the
6580
+ * schema-mandated position (after `sldMasterIdLst` / `notesMasterIdLst` /
6581
+ * `handoutMasterIdLst`, before `sldSz`). Non-sldIdLst keys keep their
6582
+ * relative order; attribute keys (`@_...`) stay at the front.
6583
+ */
6584
+ insertSlideIdListInOrder(presentation, slideIdList) {
6585
+ const before = /* @__PURE__ */ new Set([
6586
+ "p:sldMasterIdLst",
6587
+ "p:notesMasterIdLst",
6588
+ "p:handoutMasterIdLst"
6589
+ ]);
6590
+ const rebuilt = {};
6591
+ let inserted = false;
6592
+ for (const key of Object.keys(presentation)) {
6593
+ if (key === "p:sldIdLst") {
6594
+ continue;
6595
+ }
6596
+ if (!inserted && !key.startsWith("@_") && !before.has(key)) {
6597
+ rebuilt["p:sldIdLst"] = slideIdList;
6598
+ inserted = true;
6599
+ }
6600
+ rebuilt[key] = presentation[key];
6601
+ }
6602
+ if (!inserted) {
6603
+ rebuilt["p:sldIdLst"] = slideIdList;
6604
+ }
6605
+ return rebuilt;
6606
+ }
6555
6607
  async attachNewSlide(init) {
6556
6608
  const sourceSlidePath = init.input.findSourceSlidePath(init.slide.sourceSlideId);
6557
6609
  const sourceSlideXml = sourceSlidePath ? init.input.deepCloneXml(init.input.slideMap.get(sourceSlidePath)) : void 0;
@@ -6636,6 +6688,22 @@ var PptxPresentationSlidesReconciler = class {
6636
6688
  };
6637
6689
 
6638
6690
  // src/core/core/builders/PptxSlideRelationshipRegistry.ts
6691
+ function isExternalTarget(target) {
6692
+ const normalized = target.trim();
6693
+ if (normalized.length === 0) {
6694
+ return false;
6695
+ }
6696
+ const colonIdx = normalized.indexOf(":");
6697
+ if (colonIdx <= 0) {
6698
+ return false;
6699
+ }
6700
+ const slashIdx = normalized.indexOf("/");
6701
+ if (slashIdx !== -1 && slashIdx < colonIdx) {
6702
+ return false;
6703
+ }
6704
+ const scheme = normalized.slice(0, colonIdx);
6705
+ return /^[A-Za-z][A-Za-z0-9+\-.]*$/.test(scheme);
6706
+ }
6639
6707
  var PptxSlideRelationshipRegistry = class {
6640
6708
  relationships;
6641
6709
  usedRelationshipIds = /* @__PURE__ */ new Set();
@@ -6706,7 +6774,7 @@ var PptxSlideRelationshipRegistry = class {
6706
6774
  return existingRelationshipId;
6707
6775
  }
6708
6776
  const relationshipId = this.nextRelationshipId();
6709
- const targetMode = /^(https?:|mailto:|ftp:|file:)/i.test(normalizedTarget) ? "External" : void 0;
6777
+ const targetMode = isExternalTarget(normalizedTarget) ? "External" : void 0;
6710
6778
  this.upsertRelationship(
6711
6779
  relationshipId,
6712
6780
  this.hyperlinkRelationshipType,
@@ -6956,6 +7024,82 @@ var PptxSlideNotesPartUpdater = class {
6956
7024
  }
6957
7025
  };
6958
7026
 
7027
+ // src/core/utils/xml-reorder.ts
7028
+ function reorderObjectKeys(obj, schemaOrder) {
7029
+ const result = {};
7030
+ const consumed = /* @__PURE__ */ new Set();
7031
+ for (const key of schemaOrder) {
7032
+ if (Object.hasOwn(obj, key)) {
7033
+ const value = obj[key];
7034
+ if (value !== void 0) {
7035
+ result[key] = value;
7036
+ }
7037
+ consumed.add(key);
7038
+ }
7039
+ }
7040
+ for (const key of Object.keys(obj)) {
7041
+ if (consumed.has(key)) {
7042
+ continue;
7043
+ }
7044
+ const value = obj[key];
7045
+ if (value !== void 0) {
7046
+ result[key] = value;
7047
+ }
7048
+ }
7049
+ return result;
7050
+ }
7051
+ var EFFECT_LST_ORDER = [
7052
+ "a:blur",
7053
+ "a:fillOverlay",
7054
+ "a:glow",
7055
+ "a:innerShdw",
7056
+ "a:outerShdw",
7057
+ "a:prstShdw",
7058
+ "a:reflection",
7059
+ "a:softEdge"
7060
+ ];
7061
+ var SP_PR_ORDER = [
7062
+ "a:xfrm",
7063
+ "a:custGeom",
7064
+ "a:prstGeom",
7065
+ "a:noFill",
7066
+ "a:solidFill",
7067
+ "a:gradFill",
7068
+ "a:blipFill",
7069
+ "a:pattFill",
7070
+ "a:grpFill",
7071
+ "a:ln",
7072
+ "a:effectLst",
7073
+ "a:effectDag",
7074
+ "a:scene3d",
7075
+ "a:sp3d",
7076
+ "a:extLst"
7077
+ ];
7078
+ var TC_PR_BORDERS_ORDER = [
7079
+ "a:lnL",
7080
+ "a:lnR",
7081
+ "a:lnT",
7082
+ "a:lnB",
7083
+ "a:lnTlToBr",
7084
+ "a:lnBlToTr",
7085
+ "a:cell3D",
7086
+ "a:noFill",
7087
+ "a:solidFill",
7088
+ "a:gradFill",
7089
+ "a:blipFill",
7090
+ "a:pattFill",
7091
+ "a:grpFill",
7092
+ "a:headers",
7093
+ "a:extLst"
7094
+ ];
7095
+ var BLIP_FILL_ORDER = ["a:blip", "a:srcRect", "a:tile", "a:stretch"];
7096
+ var SHAPE_STYLE_ORDER = [
7097
+ "a:lnRef",
7098
+ "a:fillRef",
7099
+ "a:effectRef",
7100
+ "a:fontRef"
7101
+ ];
7102
+
6959
7103
  // src/core/core/builders/PptxSlideBackgroundBuilder.ts
6960
7104
  var PptxSlideBackgroundBuilder = class {
6961
7105
  applyBackground(init) {
@@ -6991,10 +7135,13 @@ var PptxSlideBackgroundBuilder = class {
6991
7135
  init.slideImageRelationshipType,
6992
7136
  relativeBackgroundImagePath
6993
7137
  );
6994
- backgroundProperties["a:blipFill"] = {
6995
- "a:blip": { "@_r:embed": backgroundRelationshipId },
6996
- "a:stretch": { "a:fillRect": {} }
6997
- };
7138
+ backgroundProperties["a:blipFill"] = reorderObjectKeys(
7139
+ {
7140
+ "a:blip": { "@_r:embed": backgroundRelationshipId },
7141
+ "a:stretch": { "a:fillRect": {} }
7142
+ },
7143
+ BLIP_FILL_ORDER
7144
+ );
6998
7145
  }
6999
7146
  } else if (hasBackgroundColor && init.slide.backgroundColor) {
7000
7147
  backgroundProperties["a:solidFill"] = {
@@ -7738,6 +7885,45 @@ var PptxGradientStyleCodec = class {
7738
7885
  b: Number.isFinite(b) ? this.context.clampUnitInterval(b / 1e5) : 0
7739
7886
  };
7740
7887
  }
7888
+ extractGradientFlip(gradFill) {
7889
+ const flipRaw = String(gradFill["@_flip"] || "").trim().toLowerCase();
7890
+ if (flipRaw === "x" || flipRaw === "y" || flipRaw === "xy" || flipRaw === "none") {
7891
+ return flipRaw;
7892
+ }
7893
+ return void 0;
7894
+ }
7895
+ extractGradientRotWithShape(gradFill) {
7896
+ const rot = gradFill["@_rotWithShape"];
7897
+ if (rot === void 0 || rot === null) {
7898
+ return void 0;
7899
+ }
7900
+ const token = String(rot).trim().toLowerCase();
7901
+ if (token === "1" || token === "true") {
7902
+ return true;
7903
+ }
7904
+ if (token === "0" || token === "false") {
7905
+ return false;
7906
+ }
7907
+ return void 0;
7908
+ }
7909
+ extractGradientScaled(gradFill) {
7910
+ const lin = gradFill["a:lin"];
7911
+ if (!lin) {
7912
+ return void 0;
7913
+ }
7914
+ const scaled = lin["@_scaled"];
7915
+ if (scaled === void 0 || scaled === null) {
7916
+ return void 0;
7917
+ }
7918
+ const token = String(scaled).trim().toLowerCase();
7919
+ if (token === "1" || token === "true") {
7920
+ return true;
7921
+ }
7922
+ if (token === "0" || token === "false") {
7923
+ return false;
7924
+ }
7925
+ return void 0;
7926
+ }
7741
7927
  extractGradientAngle(gradFill) {
7742
7928
  const angleRaw = Number.parseInt(
7743
7929
  String(gradFill["a:lin"]?.["@_ang"] || ""),
@@ -7824,11 +8010,14 @@ var PptxGradientStyleCodec = class {
7824
8010
  return void 0;
7825
8011
  }
7826
8012
  const gradientType = shapeStyle.fillGradientType || "linear";
7827
- const gradientXml = {
7828
- "a:gsLst": {
7829
- "a:gs": stops
7830
- }
7831
- };
8013
+ const gradientXml = {};
8014
+ if (shapeStyle.fillGradientFlip && shapeStyle.fillGradientFlip !== "none") {
8015
+ gradientXml["@_flip"] = shapeStyle.fillGradientFlip;
8016
+ }
8017
+ if (shapeStyle.fillGradientRotWithShape !== void 0) {
8018
+ gradientXml["@_rotWithShape"] = shapeStyle.fillGradientRotWithShape ? "1" : "0";
8019
+ }
8020
+ gradientXml["a:gsLst"] = { "a:gs": stops };
7832
8021
  if (gradientType === "radial") {
7833
8022
  const pathType = shapeStyle.fillGradientPathType || "circle";
7834
8023
  const pathXml = {
@@ -7858,10 +8047,15 @@ var PptxGradientStyleCodec = class {
7858
8047
  gradientXml["a:path"] = pathXml;
7859
8048
  } else {
7860
8049
  const normalizedAngle = typeof shapeStyle.fillGradientAngle === "number" && Number.isFinite(shapeStyle.fillGradientAngle) ? shapeStyle.fillGradientAngle : 90;
7861
- gradientXml["a:lin"] = {
7862
- "@_ang": String(Math.round(normalizedAngle * 6e4)),
7863
- "@_scaled": "1"
8050
+ const linNode = {
8051
+ "@_ang": String(Math.round(normalizedAngle * 6e4))
7864
8052
  };
8053
+ if (shapeStyle.fillGradientScaled !== void 0) {
8054
+ linNode["@_scaled"] = shapeStyle.fillGradientScaled ? "1" : "0";
8055
+ } else {
8056
+ linNode["@_scaled"] = "1";
8057
+ }
8058
+ gradientXml["a:lin"] = linNode;
7865
8059
  }
7866
8060
  return gradientXml;
7867
8061
  }
@@ -8258,6 +8452,30 @@ var PRESET_SHADOW_OPACITY_MAP = {
8258
8452
  };
8259
8453
 
8260
8454
  // src/core/core/builders/PptxShapeEffectStyleExtractor.ts
8455
+ var VALID_ALIGNMENTS = /* @__PURE__ */ new Set(["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"]);
8456
+ function parseIntAttr(value) {
8457
+ if (value === void 0 || value === null || value === "") {
8458
+ return void 0;
8459
+ }
8460
+ const parsed = Number.parseInt(String(value), 10);
8461
+ return Number.isFinite(parsed) ? parsed : void 0;
8462
+ }
8463
+ function parseAlignmentAttr(value) {
8464
+ const v = String(value ?? "").trim();
8465
+ return VALID_ALIGNMENTS.has(v) ? v : void 0;
8466
+ }
8467
+ function parseBoolAttr(value) {
8468
+ if (typeof value === "boolean") {
8469
+ return value;
8470
+ }
8471
+ if (value === "1" || value === "true") {
8472
+ return true;
8473
+ }
8474
+ if (value === "0" || value === "false") {
8475
+ return false;
8476
+ }
8477
+ return void 0;
8478
+ }
8261
8479
  var PptxShapeEffectStyleExtractor = class {
8262
8480
  context;
8263
8481
  constructor(context) {
@@ -8281,7 +8499,12 @@ var PptxShapeEffectStyleExtractor = class {
8281
8499
  const shadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8282
8500
  const shadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8283
8501
  const rotateWithShape = outerShadow["@_rotWithShape"];
8284
- const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : void 0;
8502
+ const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : rotateWithShape === "0" || rotateWithShape === "false" ? false : void 0;
8503
+ const shadowScaleX = parseIntAttr(outerShadow["@_sx"]);
8504
+ const shadowScaleY = parseIntAttr(outerShadow["@_sy"]);
8505
+ const shadowSkewX = parseIntAttr(outerShadow["@_kx"]);
8506
+ const shadowSkewY = parseIntAttr(outerShadow["@_ky"]);
8507
+ const shadowAlignment = parseAlignmentAttr(outerShadow["@_algn"]);
8285
8508
  return {
8286
8509
  shadowColor,
8287
8510
  shadowOpacity,
@@ -8290,7 +8513,12 @@ var PptxShapeEffectStyleExtractor = class {
8290
8513
  shadowOffsetY,
8291
8514
  shadowAngle: directionDegrees,
8292
8515
  shadowDistance: distance,
8293
- shadowRotateWithShape
8516
+ shadowRotateWithShape,
8517
+ shadowScaleX,
8518
+ shadowScaleY,
8519
+ shadowSkewX,
8520
+ shadowSkewY,
8521
+ shadowAlignment
8294
8522
  };
8295
8523
  }
8296
8524
  extractPresetShadowStyle(effectLstParent) {
@@ -8336,12 +8564,14 @@ var PptxShapeEffectStyleExtractor = class {
8336
8564
  const directionRadians = directionDegrees * Math.PI / 180;
8337
8565
  const innerShadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8338
8566
  const innerShadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8567
+ const innerShadowRotateWithShape = parseBoolAttr(innerShadow["@_rotWithShape"]);
8339
8568
  return {
8340
8569
  innerShadowColor,
8341
8570
  innerShadowOpacity,
8342
8571
  innerShadowBlur,
8343
8572
  innerShadowOffsetX,
8344
- innerShadowOffsetY
8573
+ innerShadowOffsetY,
8574
+ innerShadowRotateWithShape
8345
8575
  };
8346
8576
  }
8347
8577
  extractGlowStyle(shapeProps) {
@@ -8390,6 +8620,16 @@ var PptxShapeEffectStyleExtractor = class {
8390
8620
  const reflectionRotation = Number.isFinite(rotationRaw) ? rotationRaw / 6e4 : void 0;
8391
8621
  const distanceRaw = Number.parseInt(String(reflectionNode["@_dist"] || ""), 10);
8392
8622
  const reflectionDistance = Number.isFinite(distanceRaw) && distanceRaw >= 0 ? distanceRaw / this.context.emuPerPx : void 0;
8623
+ const fadeDirRaw = parseIntAttr(reflectionNode["@_fadeDir"]);
8624
+ const reflectionFadeDirection = fadeDirRaw !== void 0 ? fadeDirRaw / 6e4 : void 0;
8625
+ const reflectionScaleX = parseIntAttr(reflectionNode["@_sx"]);
8626
+ const reflectionScaleY = parseIntAttr(reflectionNode["@_sy"]);
8627
+ const reflectionSkewX = parseIntAttr(reflectionNode["@_kx"]);
8628
+ const reflectionSkewY = parseIntAttr(reflectionNode["@_ky"]);
8629
+ const reflectionAlignment = parseAlignmentAttr(reflectionNode["@_algn"]);
8630
+ const reflectionRotateWithShape = parseBoolAttr(reflectionNode["@_rotWithShape"]);
8631
+ const stPosRaw = parseIntAttr(reflectionNode["@_stPos"]);
8632
+ const reflectionStartPosition = stPosRaw !== void 0 ? stPosRaw / 1e5 : void 0;
8393
8633
  return {
8394
8634
  reflectionBlurRadius,
8395
8635
  reflectionStartOpacity,
@@ -8397,7 +8637,15 @@ var PptxShapeEffectStyleExtractor = class {
8397
8637
  reflectionEndPosition,
8398
8638
  reflectionDirection,
8399
8639
  reflectionRotation,
8400
- reflectionDistance
8640
+ reflectionDistance,
8641
+ reflectionFadeDirection,
8642
+ reflectionScaleX,
8643
+ reflectionScaleY,
8644
+ reflectionSkewX,
8645
+ reflectionSkewY,
8646
+ reflectionAlignment,
8647
+ reflectionRotateWithShape,
8648
+ reflectionStartPosition
8401
8649
  };
8402
8650
  }
8403
8651
  extractBlurStyle(shapeProps) {
@@ -8449,11 +8697,56 @@ var PptxShapeEffectXmlBuilder = class {
8449
8697
  }
8450
8698
  }
8451
8699
  };
8700
+ if (typeof shapeStyle.shadowScaleX === "number") {
8701
+ xmlObj["@_sx"] = String(Math.round(shapeStyle.shadowScaleX));
8702
+ }
8703
+ if (typeof shapeStyle.shadowScaleY === "number") {
8704
+ xmlObj["@_sy"] = String(Math.round(shapeStyle.shadowScaleY));
8705
+ }
8706
+ if (typeof shapeStyle.shadowSkewX === "number") {
8707
+ xmlObj["@_kx"] = String(Math.round(shapeStyle.shadowSkewX));
8708
+ }
8709
+ if (typeof shapeStyle.shadowSkewY === "number") {
8710
+ xmlObj["@_ky"] = String(Math.round(shapeStyle.shadowSkewY));
8711
+ }
8712
+ if (shapeStyle.shadowAlignment) {
8713
+ xmlObj["@_algn"] = shapeStyle.shadowAlignment;
8714
+ }
8452
8715
  if (typeof shapeStyle.shadowRotateWithShape === "boolean") {
8453
8716
  xmlObj["@_rotWithShape"] = shapeStyle.shadowRotateWithShape ? "1" : "0";
8454
8717
  }
8455
8718
  return xmlObj;
8456
8719
  }
8720
+ buildPresetShadowXml(shapeStyle) {
8721
+ const preset = shapeStyle.presetShadowName;
8722
+ if (!preset || preset.length === 0) {
8723
+ return void 0;
8724
+ }
8725
+ const shadowColor = String(shapeStyle.shadowColor || "#000000").trim();
8726
+ const shadowOpacity = typeof shapeStyle.shadowOpacity === "number" && Number.isFinite(shapeStyle.shadowOpacity) ? this.context.clampUnitInterval(shapeStyle.shadowOpacity) : 0.5;
8727
+ let distance;
8728
+ let directionDegrees;
8729
+ if (typeof shapeStyle.shadowAngle === "number" && typeof shapeStyle.shadowDistance === "number") {
8730
+ directionDegrees = shapeStyle.shadowAngle;
8731
+ distance = shapeStyle.shadowDistance;
8732
+ } else {
8733
+ const ox = typeof shapeStyle.shadowOffsetX === "number" ? shapeStyle.shadowOffsetX : 0;
8734
+ const oy = typeof shapeStyle.shadowOffsetY === "number" ? shapeStyle.shadowOffsetY : 0;
8735
+ distance = Math.sqrt(ox * ox + oy * oy);
8736
+ directionDegrees = (Math.atan2(oy, ox) * 180 / Math.PI + 360) % 360;
8737
+ }
8738
+ return {
8739
+ "@_prst": preset,
8740
+ "@_dist": String(Math.round(distance * this.context.emuPerPx)),
8741
+ "@_dir": String(Math.round(directionDegrees * 6e4)),
8742
+ "a:srgbClr": {
8743
+ "@_val": shadowColor.replace("#", ""),
8744
+ "a:alpha": {
8745
+ "@_val": String(Math.round(shadowOpacity * 1e5))
8746
+ }
8747
+ }
8748
+ };
8749
+ }
8457
8750
  buildInnerShadowXml(shapeStyle) {
8458
8751
  const innerColor = String(shapeStyle.innerShadowColor || "").trim();
8459
8752
  if (innerColor.length === 0 || innerColor === "transparent") {
@@ -8465,7 +8758,7 @@ var PptxShapeEffectXmlBuilder = class {
8465
8758
  const opacity = typeof shapeStyle.innerShadowOpacity === "number" && Number.isFinite(shapeStyle.innerShadowOpacity) ? this.context.clampUnitInterval(shapeStyle.innerShadowOpacity) : 0.5;
8466
8759
  const distance = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
8467
8760
  const directionDegrees = (Math.atan2(offsetY, offsetX) * 180 / Math.PI + 360) % 360;
8468
- return {
8761
+ const xmlObj = {
8469
8762
  "@_blurRad": String(Math.round(blurValue * this.context.emuPerPx)),
8470
8763
  "@_dist": String(Math.round(distance * this.context.emuPerPx)),
8471
8764
  "@_dir": String(Math.round(directionDegrees * 6e4)),
@@ -8476,6 +8769,10 @@ var PptxShapeEffectXmlBuilder = class {
8476
8769
  }
8477
8770
  }
8478
8771
  };
8772
+ if (typeof shapeStyle.innerShadowRotateWithShape === "boolean") {
8773
+ xmlObj["@_rotWithShape"] = shapeStyle.innerShadowRotateWithShape ? "1" : "0";
8774
+ }
8775
+ return xmlObj;
8479
8776
  }
8480
8777
  buildGlowXml(shapeStyle) {
8481
8778
  const glowColor = String(shapeStyle.glowColor || "").trim();
@@ -8537,6 +8834,30 @@ var PptxShapeEffectXmlBuilder = class {
8537
8834
  Math.round(shapeStyle.reflectionDistance * this.context.emuPerPx)
8538
8835
  );
8539
8836
  }
8837
+ if (typeof shapeStyle.reflectionFadeDirection === "number") {
8838
+ reflectionXml["@_fadeDir"] = String(Math.round(shapeStyle.reflectionFadeDirection * 6e4));
8839
+ }
8840
+ if (typeof shapeStyle.reflectionScaleX === "number") {
8841
+ reflectionXml["@_sx"] = String(Math.round(shapeStyle.reflectionScaleX));
8842
+ }
8843
+ if (typeof shapeStyle.reflectionScaleY === "number") {
8844
+ reflectionXml["@_sy"] = String(Math.round(shapeStyle.reflectionScaleY));
8845
+ }
8846
+ if (typeof shapeStyle.reflectionSkewX === "number") {
8847
+ reflectionXml["@_kx"] = String(Math.round(shapeStyle.reflectionSkewX));
8848
+ }
8849
+ if (typeof shapeStyle.reflectionSkewY === "number") {
8850
+ reflectionXml["@_ky"] = String(Math.round(shapeStyle.reflectionSkewY));
8851
+ }
8852
+ if (shapeStyle.reflectionAlignment) {
8853
+ reflectionXml["@_algn"] = shapeStyle.reflectionAlignment;
8854
+ }
8855
+ if (typeof shapeStyle.reflectionRotateWithShape === "boolean") {
8856
+ reflectionXml["@_rotWithShape"] = shapeStyle.reflectionRotateWithShape ? "1" : "0";
8857
+ }
8858
+ if (typeof shapeStyle.reflectionStartPosition === "number") {
8859
+ reflectionXml["@_stPos"] = String(Math.round(shapeStyle.reflectionStartPosition * 1e5));
8860
+ }
8540
8861
  return reflectionXml;
8541
8862
  }
8542
8863
  buildBlurXml(shapeStyle) {
@@ -8644,6 +8965,9 @@ var PptxShapeEffectXmlCodec = class {
8644
8965
  buildOuterShadowXml(shapeStyle) {
8645
8966
  return this.builder.buildOuterShadowXml(shapeStyle);
8646
8967
  }
8968
+ buildPresetShadowXml(shapeStyle) {
8969
+ return this.builder.buildPresetShadowXml(shapeStyle);
8970
+ }
8647
8971
  buildInnerShadowXml(shapeStyle) {
8648
8972
  return this.builder.buildInnerShadowXml(shapeStyle);
8649
8973
  }
@@ -8804,6 +9128,9 @@ var PptxColorStyleCodec = class {
8804
9128
  buildOuterShadowXml(shapeStyle) {
8805
9129
  return this.shapeEffectXmlCodec.buildOuterShadowXml(shapeStyle);
8806
9130
  }
9131
+ buildPresetShadowXml(shapeStyle) {
9132
+ return this.shapeEffectXmlCodec.buildPresetShadowXml(shapeStyle);
9133
+ }
8807
9134
  buildInnerShadowXml(shapeStyle) {
8808
9135
  return this.shapeEffectXmlCodec.buildInnerShadowXml(shapeStyle);
8809
9136
  }
@@ -8825,6 +9152,15 @@ var PptxColorStyleCodec = class {
8825
9152
  extractGradientFillColor(gradFill) {
8826
9153
  return this.gradientStyleCodec.extractGradientFillColor(gradFill);
8827
9154
  }
9155
+ extractGradientFlip(gradFill) {
9156
+ return this.gradientStyleCodec.extractGradientFlip(gradFill);
9157
+ }
9158
+ extractGradientRotWithShape(gradFill) {
9159
+ return this.gradientStyleCodec.extractGradientRotWithShape(gradFill);
9160
+ }
9161
+ extractGradientScaled(gradFill) {
9162
+ return this.gradientStyleCodec.extractGradientScaled(gradFill);
9163
+ }
8828
9164
  extractGradientPathType(gradFill) {
8829
9165
  return this.gradientStyleCodec.extractGradientPathType(gradFill);
8830
9166
  }
@@ -8836,6 +9172,61 @@ var PptxColorStyleCodec = class {
8836
9172
  }
8837
9173
  };
8838
9174
 
9175
+ // src/core/utils/color-xml-preservation.ts
9176
+ var COLOR_CHOICE_KEYS = [
9177
+ "a:srgbClr",
9178
+ "a:schemeClr",
9179
+ "a:sysClr",
9180
+ "a:prstClr",
9181
+ "a:scrgbClr",
9182
+ "a:hslClr"
9183
+ ];
9184
+ function extractColorChoiceXml(parent) {
9185
+ if (!parent) {
9186
+ return void 0;
9187
+ }
9188
+ for (const key of COLOR_CHOICE_KEYS) {
9189
+ if (parent[key] !== void 0) {
9190
+ return { [key]: parent[key] };
9191
+ }
9192
+ }
9193
+ return void 0;
9194
+ }
9195
+ function normalizeHex(value) {
9196
+ const raw = String(value ?? "").trim();
9197
+ if (raw.length === 0) {
9198
+ return "";
9199
+ }
9200
+ const hex = raw.replace(/^#/, "");
9201
+ if (/^[0-9a-fA-F]{6}$/.test(hex)) {
9202
+ return hex.toUpperCase();
9203
+ }
9204
+ return raw.toLowerCase();
9205
+ }
9206
+ function colorsEqual(left, right) {
9207
+ if (left === void 0 || right === void 0) {
9208
+ return false;
9209
+ }
9210
+ return normalizeHex(left) === normalizeHex(right);
9211
+ }
9212
+ function buildSrgbColorChoice(hex, opacity) {
9213
+ const normalized = String(hex || "").replace(/^#/, "");
9214
+ const srgb = { "@_val": normalized };
9215
+ if (typeof opacity === "number" && Number.isFinite(opacity) && opacity >= 0 && opacity < 1) {
9216
+ const alphaPct = Math.round(Math.max(0, Math.min(1, opacity)) * 1e5);
9217
+ srgb["a:alpha"] = { "@_val": String(alphaPct) };
9218
+ }
9219
+ return { "a:srgbClr": srgb };
9220
+ }
9221
+ function serializeColorChoice(originalColorXml, currentResolvedHex, fallbackHex, opacity, options = {}) {
9222
+ if (originalColorXml && colorsEqual(currentResolvedHex, fallbackHex)) {
9223
+ if (options.preserveAlphaFromOriginal !== false) {
9224
+ return originalColorXml;
9225
+ }
9226
+ }
9227
+ return buildSrgbColorChoice(fallbackHex, opacity);
9228
+ }
9229
+
8839
9230
  // src/core/core/builders/shape-style-3d-helpers.ts
8840
9231
  function applyScene3dStyle(shapeProps, style) {
8841
9232
  const scene3dNode = shapeProps["a:scene3d"];
@@ -8921,6 +9312,10 @@ function applyStrokeColor(lineNode, style, context) {
8921
9312
  const lineFill = lineNode["a:solidFill"];
8922
9313
  style.strokeColor = context.parseColor(lineFill);
8923
9314
  style.strokeOpacity = context.extractColorOpacity(lineFill);
9315
+ const strokeColorXml = extractColorChoiceXml(lineFill);
9316
+ if (strokeColorXml) {
9317
+ style.strokeColorXml = strokeColorXml;
9318
+ }
8924
9319
  } else if (lineNode["a:gradFill"]) {
8925
9320
  style.strokeColor = context.extractGradientFillColor(lineNode["a:gradFill"]);
8926
9321
  style.strokeOpacity = context.extractGradientOpacity(lineNode["a:gradFill"]);
@@ -8988,6 +9383,14 @@ function applyJoinCapCompound(lineNode, style) {
8988
9383
  style.lineJoin = "bevel";
8989
9384
  } else if ("a:miter" in lineNode) {
8990
9385
  style.lineJoin = "miter";
9386
+ const miterNode = lineNode["a:miter"];
9387
+ const limRaw = miterNode?.["@_lim"];
9388
+ if (limRaw !== void 0 && limRaw !== "") {
9389
+ const parsed = parseInt(String(limRaw), 10);
9390
+ if (Number.isFinite(parsed)) {
9391
+ style.miterLimit = parsed;
9392
+ }
9393
+ }
8991
9394
  }
8992
9395
  const capValue = String(lineNode["@_cap"] || "").trim().toLowerCase();
8993
9396
  if (capValue === "rnd" || capValue === "sq" || capValue === "flat") {
@@ -9044,6 +9447,10 @@ var PptxShapeStyleExtractor = class {
9044
9447
  style.fillMode = "solid";
9045
9448
  style.fillColor = this.context.parseColor(solidFill);
9046
9449
  style.fillOpacity = this.context.extractColorOpacity(solidFill);
9450
+ const solidFillColorXml = extractColorChoiceXml(solidFill);
9451
+ if (solidFillColorXml) {
9452
+ style.fillColorXml = solidFillColorXml;
9453
+ }
9047
9454
  } else if (gradFill) {
9048
9455
  style.fillMode = "gradient";
9049
9456
  style.fillColor = this.context.extractGradientFillColor(gradFill);
@@ -9055,6 +9462,18 @@ var PptxShapeStyleExtractor = class {
9055
9462
  style.fillGradientPathType = this.context.extractGradientPathType(gradFill);
9056
9463
  style.fillGradientFocalPoint = this.context.extractGradientFocalPoint(gradFill);
9057
9464
  style.fillGradientFillToRect = this.context.extractGradientFillToRect(gradFill);
9465
+ const gradFlip = this.context.extractGradientFlip(gradFill);
9466
+ if (gradFlip) {
9467
+ style.fillGradientFlip = gradFlip;
9468
+ }
9469
+ const gradRot = this.context.extractGradientRotWithShape(gradFill);
9470
+ if (gradRot !== void 0) {
9471
+ style.fillGradientRotWithShape = gradRot;
9472
+ }
9473
+ const gradScaled = this.context.extractGradientScaled(gradFill);
9474
+ if (gradScaled !== void 0) {
9475
+ style.fillGradientScaled = gradScaled;
9476
+ }
9058
9477
  } else if (pattFill) {
9059
9478
  style.fillMode = "pattern";
9060
9479
  style.fillColor = this.context.parseColor(pattFill["a:fgClr"]) || this.context.parseColor(pattFill["a:bgClr"]);
@@ -9136,10 +9555,45 @@ var PptxShapeStyleExtractor = class {
9136
9555
  if (styleNode?.["a:effectRef"]) {
9137
9556
  this.context.resolveThemeEffectRef(styleNode["a:effectRef"], style);
9138
9557
  }
9558
+ const fontRef = styleNode?.["a:fontRef"];
9559
+ if (fontRef) {
9560
+ const idxAttr = String(fontRef["@_idx"] || "").trim();
9561
+ if (idxAttr.length > 0) {
9562
+ style.fontRefIdx = idxAttr;
9563
+ }
9564
+ const overrideColorXml = this.extractFontRefColorXml(fontRef);
9565
+ if (overrideColorXml) {
9566
+ style.fontRefColorXml = overrideColorXml;
9567
+ }
9568
+ }
9139
9569
  applyScene3dStyle(shapeProps, style);
9140
9570
  applyShape3dStyle(shapeProps, style, this.context);
9141
9571
  return style;
9142
9572
  }
9573
+ /**
9574
+ * Pull the verbatim colour-choice child out of an `a:fontRef` element,
9575
+ * preserving any contained colour transforms for round-trip.
9576
+ */
9577
+ extractFontRefColorXml(refNode) {
9578
+ if (!refNode) {
9579
+ return void 0;
9580
+ }
9581
+ const keys = [
9582
+ "a:scrgbClr",
9583
+ "a:srgbClr",
9584
+ "a:hslClr",
9585
+ "a:sysClr",
9586
+ "a:schemeClr",
9587
+ "a:prstClr"
9588
+ ];
9589
+ for (const key of keys) {
9590
+ const child = refNode[key];
9591
+ if (child !== void 0) {
9592
+ return { [key]: child };
9593
+ }
9594
+ }
9595
+ return void 0;
9596
+ }
9143
9597
  /**
9144
9598
  * Extract p14:hiddenFill from the shape properties extension list.
9145
9599
  * URI: {AF507438-7753-43E0-B8FC-AC1667EBCBE1}
@@ -9190,10 +9644,15 @@ var PptxShapeStyleExtractor = class {
9190
9644
  function applyCellFillStyle(cellProperties, style, context) {
9191
9645
  let hasStyle = false;
9192
9646
  if (cellProperties?.["a:solidFill"]) {
9193
- const fillColor = context.parseColor(cellProperties["a:solidFill"]);
9647
+ const solidFillNode = cellProperties["a:solidFill"];
9648
+ const fillColor = context.parseColor(solidFillNode);
9194
9649
  if (fillColor) {
9195
9650
  style.fillMode = "solid";
9196
9651
  style.backgroundColor = fillColor;
9652
+ const bgColorXml = extractColorChoiceXml(solidFillNode);
9653
+ if (bgColorXml) {
9654
+ style.backgroundColorXml = bgColorXml;
9655
+ }
9197
9656
  hasStyle = true;
9198
9657
  }
9199
9658
  }
@@ -9230,6 +9689,10 @@ function applyCellFillStyle(cellProperties, style, context) {
9230
9689
  }
9231
9690
  }
9232
9691
  }
9692
+ if (cellProperties?.["a:noFill"] !== void 0) {
9693
+ style.fillMode = "none";
9694
+ hasStyle = true;
9695
+ }
9233
9696
  if (cellProperties?.["a:pattFill"]) {
9234
9697
  const pattFill = cellProperties["a:pattFill"];
9235
9698
  const fgColor = context.parseColor(pattFill["a:fgClr"]);
@@ -9536,8 +9999,7 @@ var PptxTableDataParser = class {
9536
9999
  return width / totalWidthEmu;
9537
10000
  }) : gridColumns.map(() => 1 / Math.max(gridColumns.length, 1));
9538
10001
  const tableProperties = tableNode["a:tblPr"] || {};
9539
- const tableStyleNode = tableProperties["a:tblStyle"];
9540
- const tableStyleId = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim() || void 0;
10002
+ const tableStyleId = this.extractTableStyleId(tableProperties);
9541
10003
  const xmlRows = this.context.ensureArray(tableNode["a:tr"]);
9542
10004
  const rows = xmlRows.map((rowNode) => {
9543
10005
  const rowHeightEmu = parseInt(String(rowNode?.["@_h"] || "0"), 10) || 0;
@@ -9572,6 +10034,28 @@ var PptxTableDataParser = class {
9572
10034
  return void 0;
9573
10035
  }
9574
10036
  }
10037
+ /**
10038
+ * Read the table style ID from `a:tblPr`.
10039
+ *
10040
+ * ECMA-376 §21.1.3.13 defines `<a:tableStyleId>{GUID}</a:tableStyleId>` as
10041
+ * a child element of `a:tblPr` carrying the GUID as element text. Older
10042
+ * inputs (and earlier versions of this library) used the legacy
10043
+ * `<a:tblStyle val="{GUID}"/>` child element or a `@_tblStyle` attribute.
10044
+ * Accept all three; the spec form takes precedence.
10045
+ */
10046
+ extractTableStyleId(tableProperties) {
10047
+ const tableStyleIdNode = tableProperties["a:tableStyleId"];
10048
+ if (tableStyleIdNode !== void 0 && tableStyleIdNode !== null) {
10049
+ const direct = typeof tableStyleIdNode === "string" || typeof tableStyleIdNode === "number" ? String(tableStyleIdNode) : String(tableStyleIdNode["#text"] ?? "");
10050
+ const trimmed = direct.trim();
10051
+ if (trimmed.length > 0) {
10052
+ return trimmed;
10053
+ }
10054
+ }
10055
+ const tableStyleNode = tableProperties["a:tblStyle"];
10056
+ const legacy = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim();
10057
+ return legacy.length > 0 ? legacy : void 0;
10058
+ }
9575
10059
  extractTableCellText(tableCell) {
9576
10060
  const paragraphs = this.context.ensureArray(tableCell?.["a:txBody"]?.["a:p"]);
9577
10061
  const lines = [];
@@ -9850,6 +10334,19 @@ var PptxGraphicFrameParser = class {
9850
10334
  if (graphicData["a:videoFile"] || graphicData["a:audioFile"] || uri.includes("/drawingml/2006/media")) {
9851
10335
  return "media";
9852
10336
  }
10337
+ if (graphicData["aink:ink"] || uri.includes("/2010/ink") || uri.includes("drawing/2010/ink")) {
10338
+ return "ink";
10339
+ }
10340
+ const alternateContent = graphicData["mc:AlternateContent"];
10341
+ if (alternateContent) {
10342
+ const choices = Array.isArray(alternateContent["mc:Choice"]) ? alternateContent["mc:Choice"] : alternateContent["mc:Choice"] ? [alternateContent["mc:Choice"]] : [];
10343
+ for (const choice of choices) {
10344
+ const requires = String(choice?.["@_Requires"] || "").toLowerCase();
10345
+ if (requires.includes("aink") || choice?.["aink:ink"]) {
10346
+ return "ink";
10347
+ }
10348
+ }
10349
+ }
9853
10350
  return "unknown";
9854
10351
  }
9855
10352
  };
@@ -10751,6 +11248,7 @@ var PptxDocumentPropertiesUpdater = class {
10751
11248
  }));
10752
11249
  if (sanitized.length === 0) {
10753
11250
  this.context.zip.remove("docProps/custom.xml");
11251
+ await this.removeCustomPropertiesPackagingArtifacts();
10754
11252
  return;
10755
11253
  }
10756
11254
  const customXml = {
@@ -10770,6 +11268,116 @@ var PptxDocumentPropertiesUpdater = class {
10770
11268
  }
10771
11269
  };
10772
11270
  this.context.zip.file("docProps/custom.xml", this.context.builder.build(customXml));
11271
+ await this.ensureCustomPropertiesPackagingArtifacts();
11272
+ }
11273
+ /**
11274
+ * Ensure `[Content_Types].xml` has an `Override` for `docProps/custom.xml`
11275
+ * and the root `_rels/.rels` references it (ECMA-376 §15.2.12.2 +
11276
+ * Part 2 §10.1.2.5). Without these, the package fails OPC validation
11277
+ * and Office strips the custom properties on next save.
11278
+ */
11279
+ async ensureCustomPropertiesPackagingArtifacts() {
11280
+ const customContentType = "application/vnd.openxmlformats-officedocument.custom-properties+xml";
11281
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
11282
+ const ctFile = this.context.zip.file("[Content_Types].xml");
11283
+ if (ctFile) {
11284
+ try {
11285
+ const ctXml = await ctFile.async("string");
11286
+ const ctData = this.context.parser.parse(ctXml);
11287
+ const types = ctData["Types"];
11288
+ if (types) {
11289
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
11290
+ const hasCustomOverride = overrides.some(
11291
+ (o) => String(o?.["@_PartName"] || "") === "/docProps/custom.xml"
11292
+ );
11293
+ if (!hasCustomOverride) {
11294
+ overrides.push({
11295
+ "@_PartName": "/docProps/custom.xml",
11296
+ "@_ContentType": customContentType
11297
+ });
11298
+ types["Override"] = overrides.length === 1 ? overrides[0] : overrides;
11299
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
11300
+ }
11301
+ }
11302
+ } catch (error) {
11303
+ console.warn("Failed to update [Content_Types].xml for custom properties:", error);
11304
+ }
11305
+ }
11306
+ const relsFile = this.context.zip.file("_rels/.rels");
11307
+ if (relsFile) {
11308
+ try {
11309
+ const relsXml = await relsFile.async("string");
11310
+ const relsData = this.context.parser.parse(relsXml);
11311
+ const relationships = relsData["Relationships"];
11312
+ if (relationships) {
11313
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
11314
+ const hasCustomRel = rels.some((r) => String(r?.["@_Type"] || "") === customRelType);
11315
+ if (!hasCustomRel) {
11316
+ let maxId = 0;
11317
+ for (const rel of rels) {
11318
+ const id = String(rel?.["@_Id"] || "");
11319
+ const num = Number.parseInt(id.replace(/^rId/, ""), 10);
11320
+ if (Number.isFinite(num) && num > maxId) {
11321
+ maxId = num;
11322
+ }
11323
+ }
11324
+ rels.push({
11325
+ "@_Id": `rId${maxId + 1}`,
11326
+ "@_Type": customRelType,
11327
+ "@_Target": "docProps/custom.xml"
11328
+ });
11329
+ relationships["Relationship"] = rels;
11330
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
11331
+ }
11332
+ }
11333
+ } catch (error) {
11334
+ console.warn("Failed to update _rels/.rels for custom properties:", error);
11335
+ }
11336
+ }
11337
+ }
11338
+ /**
11339
+ * Remove the Override + root rel for `docProps/custom.xml` when the
11340
+ * caller has emptied custom properties so the package doesn't keep an
11341
+ * orphan content-type entry referencing a deleted part.
11342
+ */
11343
+ async removeCustomPropertiesPackagingArtifacts() {
11344
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
11345
+ const ctFile = this.context.zip.file("[Content_Types].xml");
11346
+ if (ctFile) {
11347
+ try {
11348
+ const ctXml = await ctFile.async("string");
11349
+ const ctData = this.context.parser.parse(ctXml);
11350
+ const types = ctData["Types"];
11351
+ if (types) {
11352
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
11353
+ const filtered = overrides.filter(
11354
+ (o) => String(o?.["@_PartName"] || "") !== "/docProps/custom.xml"
11355
+ );
11356
+ if (filtered.length !== overrides.length) {
11357
+ types["Override"] = filtered.length === 1 ? filtered[0] : filtered;
11358
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
11359
+ }
11360
+ }
11361
+ } catch {
11362
+ }
11363
+ }
11364
+ const relsFile = this.context.zip.file("_rels/.rels");
11365
+ if (relsFile) {
11366
+ try {
11367
+ const relsXml = await relsFile.async("string");
11368
+ const relsData = this.context.parser.parse(relsXml);
11369
+ const relationships = relsData["Relationships"];
11370
+ if (relationships) {
11371
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
11372
+ const filtered = rels.filter((r) => String(r?.["@_Type"] || "") !== customRelType);
11373
+ if (filtered.length !== rels.length) {
11374
+ relationships["Relationship"] = filtered;
11375
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
11376
+ }
11377
+ }
11378
+ } catch {
11379
+ }
11380
+ }
10773
11381
  }
10774
11382
  normalizeCustomPropertyType(type) {
10775
11383
  const supportedTypes = /* @__PURE__ */ new Set([
@@ -11113,6 +11721,7 @@ var PptxSlideLoaderService = class {
11113
11721
  await params.loadSlideRelationships(path2, slideRelsPath);
11114
11722
  const clrMapOverride = params.parseSlideClrMapOverride(slideXmlObj);
11115
11723
  params.setCurrentSlideClrMapOverride(clrMapOverride);
11724
+ await params.setActiveMasterForSlide?.(path2);
11116
11725
  let restoreThemeOverride;
11117
11726
  try {
11118
11727
  const layoutPathForOverride = params.findLayoutPathForSlide(path2);
@@ -15864,6 +16473,20 @@ var AXIS_TYPE_MAP = {
15864
16473
  dateAx: "dateAx",
15865
16474
  serAx: "serAx"
15866
16475
  };
16476
+ function upsertChartAxisChild(parent, localName, value, getLocalName) {
16477
+ const existingKey = Object.keys(parent).find((k) => getLocalName(k) === localName);
16478
+ if (value === void 0) {
16479
+ if (existingKey) {
16480
+ delete parent[existingKey];
16481
+ }
16482
+ return;
16483
+ }
16484
+ if (existingKey) {
16485
+ parent[existingKey]["@_val"] = value;
16486
+ } else {
16487
+ parent[`c:${localName}`] = { "@_val": value };
16488
+ }
16489
+ }
15867
16490
  function parseChartAxes(plotArea, xmlLookup, colorParser, getLocalName) {
15868
16491
  const result = [];
15869
16492
  for (const key of Object.keys(plotArea)) {
@@ -15984,6 +16607,27 @@ function parseSingleAxis(axisNode, axisType, xmlLookup, colorParser) {
15984
16607
  if (dispUnitsNode) {
15985
16608
  parseDisplayUnits(dispUnitsNode, xmlLookup, result);
15986
16609
  }
16610
+ const majorUnitNode = xmlLookup.getChildByLocalName(axisNode, "majorUnit");
16611
+ if (majorUnitNode) {
16612
+ const majorVal = parseFloat(String(majorUnitNode["@_val"]));
16613
+ if (Number.isFinite(majorVal)) {
16614
+ result.majorUnit = majorVal;
16615
+ }
16616
+ }
16617
+ const minorUnitNode = xmlLookup.getChildByLocalName(axisNode, "minorUnit");
16618
+ if (minorUnitNode) {
16619
+ const minorVal = parseFloat(String(minorUnitNode["@_val"]));
16620
+ if (Number.isFinite(minorVal)) {
16621
+ result.minorUnit = minorVal;
16622
+ }
16623
+ }
16624
+ const tickLblPosNode = xmlLookup.getChildByLocalName(axisNode, "tickLblPos");
16625
+ if (tickLblPosNode) {
16626
+ const v = String(tickLblPosNode["@_val"] || "").trim();
16627
+ if (v === "high" || v === "low" || v === "nextTo" || v === "none") {
16628
+ result.tickLblPos = v;
16629
+ }
16630
+ }
15987
16631
  return result;
15988
16632
  }
15989
16633
  var VALID_DISPLAY_UNITS = /* @__PURE__ */ new Set([
@@ -17295,23 +17939,55 @@ var SHAPE_TREE_ELEMENT_TAGS = /* @__PURE__ */ new Set([
17295
17939
  "p16:model3D",
17296
17940
  ...VML_SHAPE_TAGS
17297
17941
  ]);
17942
+ function diagnoseSelection(ac) {
17943
+ const choices = ensureArray2(ac["mc:Choice"]);
17944
+ for (let i = 0; i < choices.length; i++) {
17945
+ const choice = choices[i];
17946
+ const requires = String(choice?.["@_Requires"] ?? "").trim();
17947
+ if (requires.length === 0 || areNamespacesSupported(requires)) {
17948
+ const resolved = resolveNestedAlternateContent(choice);
17949
+ return { branch: "choice", choiceIndex: i, resolved };
17950
+ }
17951
+ }
17952
+ const fallback = ac["mc:Fallback"];
17953
+ if (fallback) {
17954
+ return { branch: "fallback", resolved: resolveNestedAlternateContent(fallback) };
17955
+ }
17956
+ return void 0;
17957
+ }
17298
17958
  function unwrapAlternateContent(container) {
17299
17959
  const altContents = ensureArray2(container["mc:AlternateContent"]);
17300
17960
  if (altContents.length === 0) {
17301
- return;
17961
+ return [];
17302
17962
  }
17963
+ const blocks = [];
17303
17964
  for (const ac of altContents) {
17304
- const branch = selectAlternateContentBranch(ac);
17305
- if (!branch) {
17965
+ const diagnosis = diagnoseSelection(ac);
17966
+ if (!diagnosis) {
17306
17967
  continue;
17307
17968
  }
17969
+ const block = {
17970
+ rawAc: ac,
17971
+ selectedBranch: diagnosis.branch,
17972
+ choiceIndex: diagnosis.branch === "choice" ? diagnosis.choiceIndex : void 0,
17973
+ childRefs: []
17974
+ };
17975
+ const branch = diagnosis.resolved;
17308
17976
  for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
17309
17977
  const children = ensureArray2(branch[tag]);
17310
17978
  if (children.length > 0) {
17311
17979
  container[tag] = [...ensureArray2(container[tag]), ...children];
17980
+ for (const child of children) {
17981
+ block.childRefs.push({ tag, node: child });
17982
+ }
17312
17983
  }
17313
17984
  }
17985
+ if (block.childRefs.length > 0) {
17986
+ blocks.push(block);
17987
+ }
17314
17988
  }
17989
+ delete container["mc:AlternateContent"];
17990
+ return blocks;
17315
17991
  }
17316
17992
  function ensureArray2(val) {
17317
17993
  if (!val) {
@@ -17323,7 +17999,7 @@ function ensureArray2(val) {
17323
17999
 
17324
18000
  // src/core/utils/body-properties-parser.ts
17325
18001
  function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17326
- const parseBoolAttr = (attr) => {
18002
+ const parseBoolAttr2 = (attr) => {
17327
18003
  const raw = bodyPr[attr];
17328
18004
  if (raw === void 0) {
17329
18005
  return void 0;
@@ -17331,19 +18007,19 @@ function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17331
18007
  const val = String(raw).trim().toLowerCase();
17332
18008
  return val === "1" || val === "true";
17333
18009
  };
17334
- const compatLnSpc = parseBoolAttr("@_compatLnSpc");
18010
+ const compatLnSpc = parseBoolAttr2("@_compatLnSpc");
17335
18011
  if (compatLnSpc !== void 0) {
17336
18012
  textStyle.compatibleLineSpacing = compatLnSpc;
17337
18013
  }
17338
- const forceAA = parseBoolAttr("@_forceAA");
18014
+ const forceAA = parseBoolAttr2("@_forceAA");
17339
18015
  if (forceAA !== void 0) {
17340
18016
  textStyle.forceAntiAlias = forceAA;
17341
18017
  }
17342
- const upright = parseBoolAttr("@_upright");
18018
+ const upright = parseBoolAttr2("@_upright");
17343
18019
  if (upright !== void 0) {
17344
18020
  textStyle.upright = upright;
17345
18021
  }
17346
- const fromWordArt = parseBoolAttr("@_fromWordArt");
18022
+ const fromWordArt = parseBoolAttr2("@_fromWordArt");
17347
18023
  if (fromWordArt !== void 0) {
17348
18024
  textStyle.fromWordArt = fromWordArt;
17349
18025
  }
@@ -17366,46 +18042,6 @@ function writeBodyPrBooleanAttrs(bodyPr, textStyle) {
17366
18042
  }
17367
18043
  }
17368
18044
 
17369
- // src/core/utils/theme-override-utils.ts
17370
- var COLOR_MAP_ALIAS_KEYS = [
17371
- "bg1",
17372
- "tx1",
17373
- "bg2",
17374
- "tx2",
17375
- "accent1",
17376
- "accent2",
17377
- "accent3",
17378
- "accent4",
17379
- "accent5",
17380
- "accent6",
17381
- "hlink",
17382
- "folHlink"
17383
- ];
17384
- var DEFAULT_COLOR_MAP = {
17385
- bg1: "lt1",
17386
- tx1: "dk1",
17387
- bg2: "lt2",
17388
- tx2: "dk2",
17389
- accent1: "accent1",
17390
- accent2: "accent2",
17391
- accent3: "accent3",
17392
- accent4: "accent4",
17393
- accent5: "accent5",
17394
- accent6: "accent6",
17395
- hlink: "hlink",
17396
- folHlink: "folHlink"
17397
- };
17398
- function buildClrMapOverrideXml(override) {
17399
- if (!override || Object.keys(override).length === 0) {
17400
- return { "a:masterClrMapping": {} };
17401
- }
17402
- const attrs2 = {};
17403
- for (const key of COLOR_MAP_ALIAS_KEYS) {
17404
- attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
17405
- }
17406
- return { "a:overrideClrMapping": attrs2 };
17407
- }
17408
-
17409
18045
  // src/core/utils/data-url-utils.ts
17410
18046
  function parseDataUrlToBytes(dataUrl) {
17411
18047
  const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
@@ -17503,6 +18139,46 @@ async function fetchUrlToBytes(url) {
17503
18139
  }
17504
18140
  }
17505
18141
 
18142
+ // src/core/utils/theme-override-utils.ts
18143
+ var COLOR_MAP_ALIAS_KEYS = [
18144
+ "bg1",
18145
+ "tx1",
18146
+ "bg2",
18147
+ "tx2",
18148
+ "accent1",
18149
+ "accent2",
18150
+ "accent3",
18151
+ "accent4",
18152
+ "accent5",
18153
+ "accent6",
18154
+ "hlink",
18155
+ "folHlink"
18156
+ ];
18157
+ var DEFAULT_COLOR_MAP = {
18158
+ bg1: "lt1",
18159
+ tx1: "dk1",
18160
+ bg2: "lt2",
18161
+ tx2: "dk2",
18162
+ accent1: "accent1",
18163
+ accent2: "accent2",
18164
+ accent3: "accent3",
18165
+ accent4: "accent4",
18166
+ accent5: "accent5",
18167
+ accent6: "accent6",
18168
+ hlink: "hlink",
18169
+ folHlink: "folHlink"
18170
+ };
18171
+ function buildClrMapOverrideXml(override) {
18172
+ if (!override || Object.keys(override).length === 0) {
18173
+ return { "a:masterClrMapping": {} };
18174
+ }
18175
+ const attrs2 = {};
18176
+ for (const key of COLOR_MAP_ALIAS_KEYS) {
18177
+ attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
18178
+ }
18179
+ return { "a:overrideClrMapping": attrs2 };
18180
+ }
18181
+
17506
18182
  // src/core/utils/encryption-detection.ts
17507
18183
  var OLE_MAGIC = new Uint8Array([208, 207, 17, 224, 161, 27, 26, 225]);
17508
18184
  var ZIP_MAGIC = new Uint8Array([80, 75]);
@@ -23265,7 +23941,7 @@ function parseActiveXControlsFromSlide(slideXml2) {
23265
23941
  }
23266
23942
 
23267
23943
  // src/core/utils/theme-switching.ts
23268
- function normalizeHex(hex) {
23944
+ function normalizeHex2(hex) {
23269
23945
  if (!hex) {
23270
23946
  return "";
23271
23947
  }
@@ -23275,8 +23951,8 @@ function buildColorRemapTable(oldColorMap, newColorMap) {
23275
23951
  const remap = /* @__PURE__ */ new Map();
23276
23952
  const allKeys = [...THEME_COLOR_SCHEME_KEYS, "tx1", "bg1", "tx2", "bg2"];
23277
23953
  for (const key of allKeys) {
23278
- const oldVal = normalizeHex(oldColorMap[key]);
23279
- const newVal = normalizeHex(newColorMap[key]);
23954
+ const oldVal = normalizeHex2(oldColorMap[key]);
23955
+ const newVal = normalizeHex2(newColorMap[key]);
23280
23956
  if (oldVal && newVal && oldVal !== newVal) {
23281
23957
  remap.set(oldVal, `#${newVal}`);
23282
23958
  remap.set(`#${oldVal}`, `#${newVal}`);
@@ -23288,7 +23964,7 @@ function remapColor(color, remap) {
23288
23964
  if (!color) {
23289
23965
  return color;
23290
23966
  }
23291
- const normalized = normalizeHex(color);
23967
+ const normalized = normalizeHex2(color);
23292
23968
  const remapped = remap.get(normalized) ?? remap.get(`#${normalized}`);
23293
23969
  return remapped ?? color;
23294
23970
  }
@@ -23428,7 +24104,7 @@ function remapSlideColors(slide, remap) {
23428
24104
  function buildThemeColorMap(colorScheme) {
23429
24105
  const map = {};
23430
24106
  for (const key of THEME_COLOR_SCHEME_KEYS) {
23431
- map[key] = normalizeHex(colorScheme[key]);
24107
+ map[key] = normalizeHex2(colorScheme[key]);
23432
24108
  }
23433
24109
  map.tx1 = map.dk1;
23434
24110
  map.bg1 = map.lt1;
@@ -23464,8 +24140,11 @@ function applyThemeToData(data, newColorScheme, newFontScheme, themeName) {
23464
24140
 
23465
24141
  // src/core/core/runtime/PptxHandlerRuntimeSaveParagraphHelpers.ts
23466
24142
  var EMU_PER_PX4 = 9525;
23467
- function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing) {
24143
+ function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing, level) {
23468
24144
  const paragraphProps = {};
24145
+ if (typeof level === "number" && Number.isFinite(level) && level > 0) {
24146
+ paragraphProps["@_lvl"] = String(Math.min(Math.max(Math.round(level), 0), 8));
24147
+ }
23469
24148
  if (paragraphAlign) {
23470
24149
  paragraphProps["@_algn"] = paragraphAlign;
23471
24150
  }
@@ -23537,23 +24216,28 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23537
24216
  paragraphProps["a:buNone"] = {};
23538
24217
  return;
23539
24218
  }
23540
- if (bulletInfo.color) {
24219
+ if (bulletInfo.colorInherit) {
24220
+ paragraphProps["a:buClrTx"] = {};
24221
+ } else if (bulletInfo.color) {
23541
24222
  const colorHex = bulletInfo.color.replace("#", "");
23542
24223
  paragraphProps["a:buClr"] = {
23543
24224
  "a:srgbClr": { "@_val": colorHex }
23544
24225
  };
23545
24226
  }
23546
- if (bulletInfo.sizePercent !== void 0) {
24227
+ if (bulletInfo.sizeInherit) {
24228
+ paragraphProps["a:buSzTx"] = {};
24229
+ } else if (bulletInfo.sizePercent !== void 0) {
23547
24230
  paragraphProps["a:buSzPct"] = {
23548
24231
  "@_val": String(Math.round(bulletInfo.sizePercent * 1e3))
23549
24232
  };
23550
- }
23551
- if (bulletInfo.sizePts !== void 0) {
24233
+ } else if (bulletInfo.sizePts !== void 0) {
23552
24234
  paragraphProps["a:buSzPts"] = {
23553
24235
  "@_val": String(Math.round(bulletInfo.sizePts * 100))
23554
24236
  };
23555
24237
  }
23556
- if (bulletInfo.fontFamily) {
24238
+ if (bulletInfo.fontInherit) {
24239
+ paragraphProps["a:buFontTx"] = {};
24240
+ } else if (bulletInfo.fontFamily) {
23557
24241
  paragraphProps["a:buFont"] = {
23558
24242
  "@_typeface": bulletInfo.fontFamily
23559
24243
  };
@@ -23576,7 +24260,7 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23576
24260
  };
23577
24261
  }
23578
24262
  }
23579
- function assembleParagraphXml(runs, paragraphProps) {
24263
+ function assembleParagraphXml(runs, paragraphProps, endParaRunProperties) {
23580
24264
  const paragraph = {
23581
24265
  "a:pPr": paragraphProps
23582
24266
  };
@@ -23598,7 +24282,11 @@ function assembleParagraphXml(runs, paragraphProps) {
23598
24282
  if (cleanRegularRuns.length === 0 && fieldRuns.length === 0) {
23599
24283
  paragraph["a:r"] = runs.length > 1 ? runs : runs[0];
23600
24284
  }
23601
- paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
24285
+ if (endParaRunProperties && typeof endParaRunProperties === "object") {
24286
+ paragraph["a:endParaRPr"] = endParaRunProperties;
24287
+ } else {
24288
+ paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
24289
+ }
23602
24290
  return paragraph;
23603
24291
  }
23604
24292
  function computeUniformSegmentOverrides(textStyle, textSegments) {
@@ -23902,6 +24590,140 @@ var PptxHandlerRuntime = class {
23902
24590
  * `p:clrMapOvr / a:overrideClrMapping`.
23903
24591
  */
23904
24592
  currentSlideClrMapOverride = null;
24593
+ /**
24594
+ * Per-master colour map alias dictionaries parsed from each master's
24595
+ * `<p:clrMap>` element (e.g. `bg1 → lt1`, `tx1 → dk1`, `accent1 → accent1`).
24596
+ *
24597
+ * `clrMap` is the *aliasing* layer between logical colour names used in
24598
+ * DrawingML and the raw theme scheme slots. Per ECMA-376 §19.3.1.7 it
24599
+ * lives on each `p:sldMaster`, and slide layouts/slides may further
24600
+ * override it via `p:clrMapOvr`. Resolution must happen at colour-lookup
24601
+ * time, *not* by baking it into {@link themeColorMap}.
24602
+ *
24603
+ * Phase 2 Stream B / C-H4.
24604
+ */
24605
+ masterClrMaps = /* @__PURE__ */ new Map();
24606
+ /**
24607
+ * Per-master theme color maps. Each master may reference its own theme
24608
+ * file via `_rels/slideMasterN.xml.rels`. For multi-master decks, slides
24609
+ * must resolve scheme colours against their *own* master's theme.
24610
+ *
24611
+ * Falls back to {@link themeColorMap} when a master entry is missing.
24612
+ *
24613
+ * Phase 2 Stream B / C-H4.
24614
+ */
24615
+ masterThemeColorMaps = /* @__PURE__ */ new Map();
24616
+ /**
24617
+ * Per-master theme font maps. Same rationale as
24618
+ * {@link masterThemeColorMaps}: multi-master decks may have one font
24619
+ * scheme per theme.
24620
+ */
24621
+ masterThemeFontMaps = /* @__PURE__ */ new Map();
24622
+ /**
24623
+ * Per-master format schemes (fmtScheme). For multi-master decks each
24624
+ * master's slides should resolve fill/line/effect refs against the
24625
+ * matrix from that master's theme.
24626
+ */
24627
+ masterThemeFormatSchemes = /* @__PURE__ */ new Map();
24628
+ /**
24629
+ * Per-master mapping from slide-master path to the theme path it
24630
+ * references via `_rels/slideMasterN.xml.rels`. Populated by
24631
+ * {@link loadPerMasterThemes} during load. Used by the save-side
24632
+ * theme writer to know which themeN.xml to (re)emit for each master.
24633
+ *
24634
+ * Phase 4 Stream A / C-H3.
24635
+ */
24636
+ masterThemePaths = /* @__PURE__ */ new Map();
24637
+ /**
24638
+ * Per-script font tables for major and minor fonts. Captured per master
24639
+ * theme. Keys are master paths; values map `mj`/`mn` -> script tag (e.g.
24640
+ * `Hans`, `Hant`, `Arab`, `Hebr`, `Thai`, `Beng`, …) -> typeface name.
24641
+ *
24642
+ * Phase 4 Stream A / M4.
24643
+ */
24644
+ masterThemeMajorFontScripts = /* @__PURE__ */ new Map();
24645
+ masterThemeMinorFontScripts = /* @__PURE__ */ new Map();
24646
+ /**
24647
+ * Theme name attribute (`<a:theme @name>`) per master theme path.
24648
+ * Captured for byte-stable round-trip.
24649
+ */
24650
+ masterThemeNames = /* @__PURE__ */ new Map();
24651
+ /**
24652
+ * `<a:fontScheme @name>` per master theme path.
24653
+ */
24654
+ masterThemeFontSchemeNames = /* @__PURE__ */ new Map();
24655
+ /**
24656
+ * `<a:clrScheme @name>` per master theme path.
24657
+ */
24658
+ masterThemeColorSchemeNames = /* @__PURE__ */ new Map();
24659
+ /**
24660
+ * Raw original theme XML keyed by theme path. Captured at load-time.
24661
+ * Used by the save pipeline to passthrough the full theme XML when no
24662
+ * in-memory mutation has occurred — preserving fillStyleLst /
24663
+ * lnStyleLst / effectStyleLst / bgFillStyleLst /
24664
+ * extraClrSchemeLst / objectDefaults / extLst exactly as written.
24665
+ *
24666
+ * Phase 4 Stream A / C-H3.
24667
+ */
24668
+ originalThemeXmlByPath = /* @__PURE__ */ new Map();
24669
+ /**
24670
+ * Set of theme paths whose in-memory state has been mutated since
24671
+ * load. Saving a theme path that's NOT dirty is a no-op (the original
24672
+ * file already exists in the ZIP). Saving a dirty theme path
24673
+ * regenerates the part from in-memory state.
24674
+ *
24675
+ * Phase 4 Stream A / C-H3.
24676
+ */
24677
+ dirtyThemePaths = /* @__PURE__ */ new Set();
24678
+ /**
24679
+ * Per-master parsed `<p:txStyles>` (titleStyle/bodyStyle/otherStyle).
24680
+ * Populated by {@link enrichSlideMastersWithTxStyles} during load so the
24681
+ * inheritance chain can find the master text-style cascade without
24682
+ * re-parsing master XML on every lookup. Phase 4 Stream B / P-H1.
24683
+ */
24684
+ masterTxStylesCache = /* @__PURE__ */ new Map();
24685
+ /**
24686
+ * Captured `<a:objectDefaults>` snapshot per master theme path. The
24687
+ * full ECMA-376 inheritance chain (master / layout / placeholder /
24688
+ * objectDefaults) is non-trivial; we store the raw spDef/lnDef/txDef
24689
+ * subtrees and re-emit them verbatim for round-trip.
24690
+ *
24691
+ * Phase 4 Stream A / M5.
24692
+ */
24693
+ masterThemeObjectDefaults = /* @__PURE__ */ new Map();
24694
+ /**
24695
+ * Captured `<a:extraClrSchemeLst>` raw subtree per master theme path
24696
+ * for verbatim round-trip.
24697
+ *
24698
+ * Phase 4 Stream A.
24699
+ */
24700
+ masterThemeExtraClrSchemeLst = /* @__PURE__ */ new Map();
24701
+ /**
24702
+ * Captured `<a:custClrLst>` raw subtree per master theme path
24703
+ * for verbatim round-trip.
24704
+ *
24705
+ * Phase 4 Stream A.
24706
+ */
24707
+ masterThemeCustClrLst = /* @__PURE__ */ new Map();
24708
+ /**
24709
+ * Captured theme-level `<a:extLst>` raw subtree per master theme path.
24710
+ */
24711
+ masterThemeExtLst = /* @__PURE__ */ new Map();
24712
+ /**
24713
+ * Active master's clrMap for the slide currently being parsed. Walked
24714
+ * after `currentSlideClrMapOverride` (slide and layout overrides take
24715
+ * precedence). `null` means "fall through to themeColorMap directly".
24716
+ */
24717
+ currentMasterClrMap = null;
24718
+ /**
24719
+ * Snapshot of the global theme state taken right after
24720
+ * {@link loadThemeData} completes. Used as the fallback when a slide's
24721
+ * master has no per-master theme entry, so per-slide multi-master
24722
+ * switching does not leak the previous slide's master state.
24723
+ */
24724
+ globalThemeColorMapSnapshot = {};
24725
+ globalThemeFontMapSnapshot = {};
24726
+ globalThemeFormatSchemeSnapshot;
23905
24727
  /** Thumbnail image data from `docProps/thumbnail.jpeg` preserved for round-trip. */
23906
24728
  thumbnailData = null;
23907
24729
  /** Raw VBA project binary preserved for macro-enabled (.pptm) round-trip. */
@@ -23912,6 +24734,19 @@ var PptxHandlerRuntime = class {
23912
24734
  signatureDetection = null;
23913
24735
  /** Custom XML data parts parsed from `customXml/` in the OPC package. */
23914
24736
  customXmlParts = [];
24737
+ /**
24738
+ * Maps an element's `rawXml` reference to the `mc:AlternateContent`
24739
+ * envelope that originally wrapped it (CC-4). Populated during slide
24740
+ * (and `p:grpSp`) parsing; consulted at save time to re-emit the
24741
+ * original `<mc:Choice>` / `<mc:Fallback>` shape so legacy renderers
24742
+ * keep their fallback content.
24743
+ *
24744
+ * Multiple sibling elements may share the same `AlternateContentBlock`
24745
+ * value (a single AC envelope often wraps several child shapes — e.g.
24746
+ * `p14:media` + its `p:pic` fallback nest one each). WeakMap so AC
24747
+ * envelopes are GC'd if the parsed XmlObject is dropped.
24748
+ */
24749
+ alternateContentBlockByRawXml = /* @__PURE__ */ new WeakMap();
23915
24750
  /** Embedded fonts extracted during load, preserved for automatic re-embedding on save. */
23916
24751
  loadedEmbeddedFonts = [];
23917
24752
  /** Map of comment author IDs to display names (from `ppt/commentAuthors.xml`). */
@@ -25725,7 +26560,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25725
26560
  const bgColor = this.parseBackgroundColor(bg);
25726
26561
  const spTree = master["p:cSld"]?.["p:spTree"];
25727
26562
  const placeholders = this.extractPlaceholderList(spTree);
25728
- return { path: path2, backgroundColor: bgColor, placeholders };
26563
+ const result = { path: path2, backgroundColor: bgColor, placeholders };
26564
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
26565
+ if (hf) {
26566
+ result.headerFooter = hf;
26567
+ }
26568
+ return result;
25729
26569
  } catch (e) {
25730
26570
  console.warn("Failed to parse handout master:", e);
25731
26571
  return void 0;
@@ -25751,7 +26591,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25751
26591
  const bgColor = this.parseBackgroundColor(bg);
25752
26592
  const spTree = master["p:cSld"]?.["p:spTree"];
25753
26593
  const placeholders = this.extractPlaceholderList(spTree);
25754
- return { path: path2, backgroundColor: bgColor, placeholders };
26594
+ const result = { path: path2, backgroundColor: bgColor, placeholders };
26595
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
26596
+ if (hf) {
26597
+ result.headerFooter = hf;
26598
+ }
26599
+ return result;
25755
26600
  } catch (e) {
25756
26601
  console.warn("Failed to parse notes master:", e);
25757
26602
  return void 0;
@@ -25796,6 +26641,10 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25796
26641
  const uVal = String(userDrawn).trim().toLowerCase();
25797
26642
  layout.userDrawn = uVal === "1" || uVal === "true";
25798
26643
  }
26644
+ const hf = parseHeaderFooterFlags(sldLayout["p:hf"]);
26645
+ if (hf) {
26646
+ layout.headerFooter = hf;
26647
+ }
25799
26648
  const clrMapOvr = sldLayout["p:clrMapOvr"];
25800
26649
  if (clrMapOvr && clrMapOvr["a:masterClrMapping"] === void 0) {
25801
26650
  const overrideNode = clrMapOvr["a:overrideClrMapping"];
@@ -26401,6 +27250,18 @@ function buildClrChangeNode(style) {
26401
27250
  }
26402
27251
 
26403
27252
  // src/core/core/runtime/PptxHandlerRuntimeSaveRunProperties.ts
27253
+ function applyFontMetadata(fontNode, panose, pitchFamily, charset) {
27254
+ if (panose && panose.length > 0) {
27255
+ fontNode["@_panose"] = panose;
27256
+ }
27257
+ if (typeof pitchFamily === "number" && Number.isFinite(pitchFamily)) {
27258
+ fontNode["@_pitchFamily"] = String(pitchFamily);
27259
+ }
27260
+ if (typeof charset === "number" && Number.isFinite(charset)) {
27261
+ fontNode["@_charset"] = String(charset);
27262
+ }
27263
+ return fontNode;
27264
+ }
26404
27265
  var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime11 {
26405
27266
  createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId) {
26406
27267
  const runProps = {
@@ -26458,6 +27319,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26458
27319
  if (style.bookmark) {
26459
27320
  runProps["@_bmk"] = style.bookmark;
26460
27321
  }
27322
+ if (style.altLanguage) {
27323
+ runProps["@_altLang"] = style.altLanguage;
27324
+ }
27325
+ if (typeof style.smartTagId === "number" && Number.isFinite(style.smartTagId)) {
27326
+ runProps["@_smtId"] = String(style.smartTagId);
27327
+ }
26461
27328
  if (style.textOutlineWidth || style.textOutlineColor) {
26462
27329
  const lnObj = {};
26463
27330
  if (typeof style.textOutlineWidth === "number" && style.textOutlineWidth > 0) {
@@ -26473,11 +27340,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26473
27340
  runProps["a:ln"] = lnObj;
26474
27341
  }
26475
27342
  if (style.color) {
26476
- runProps["a:solidFill"] = {
26477
- "a:srgbClr": {
26478
- "@_val": style.color.replace("#", "")
26479
- }
26480
- };
27343
+ const resolvedOriginalColor = style.colorXml ? this.parseColor(style.colorXml) : void 0;
27344
+ runProps["a:solidFill"] = serializeColorChoice(
27345
+ style.colorXml,
27346
+ resolvedOriginalColor,
27347
+ style.color
27348
+ );
26481
27349
  } else if (style.textFillGradientStops && style.textFillGradientStops.length > 0) {
26482
27350
  const gradStops = style.textFillGradientStops.filter((stop) => Boolean(stop?.color)).map((stop) => {
26483
27351
  const rawPos = (stop.position ?? 0) / 100;
@@ -26550,16 +27418,32 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26550
27418
  };
26551
27419
  }
26552
27420
  if (style.fontFamily) {
26553
- runProps["a:latin"] = { "@_typeface": style.fontFamily };
26554
- runProps["a:ea"] = {
26555
- "@_typeface": style.eastAsiaFont || style.fontFamily
26556
- };
26557
- runProps["a:cs"] = {
26558
- "@_typeface": style.complexScriptFont || style.fontFamily
26559
- };
27421
+ runProps["a:latin"] = applyFontMetadata(
27422
+ { "@_typeface": style.fontFamily },
27423
+ style.latinFontPanose,
27424
+ style.latinFontPitchFamily,
27425
+ style.latinFontCharset
27426
+ );
27427
+ runProps["a:ea"] = applyFontMetadata(
27428
+ { "@_typeface": style.eastAsiaFont || style.fontFamily },
27429
+ style.eastAsiaFontPanose,
27430
+ style.eastAsiaFontPitchFamily,
27431
+ style.eastAsiaFontCharset
27432
+ );
27433
+ runProps["a:cs"] = applyFontMetadata(
27434
+ { "@_typeface": style.complexScriptFont || style.fontFamily },
27435
+ style.complexScriptFontPanose,
27436
+ style.complexScriptFontPitchFamily,
27437
+ style.complexScriptFontCharset
27438
+ );
26560
27439
  }
26561
27440
  if (style.symbolFont) {
26562
- runProps["a:sym"] = { "@_typeface": style.symbolFont };
27441
+ runProps["a:sym"] = applyFontMetadata(
27442
+ { "@_typeface": style.symbolFont },
27443
+ style.symbolFontPanose,
27444
+ style.symbolFontPitchFamily,
27445
+ style.symbolFontCharset
27446
+ );
26563
27447
  }
26564
27448
  if (style.hyperlink && resolveHyperlinkRelationshipId) {
26565
27449
  const hyperlinkTarget = String(style.hyperlink).trim();
@@ -26634,14 +27518,15 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26634
27518
  lineSpacing: this.createLineSpacingXmlFromMultiplier(textStyle?.lineSpacing),
26635
27519
  lineSpacingExactPt: textStyle?.lineSpacingExactPt
26636
27520
  };
26637
- const createParagraph = (runs, bulletInfo) => {
27521
+ const createParagraph = (runs, bulletInfo, level, endParaRunProperties) => {
26638
27522
  const paragraphProps = buildParagraphPropertiesXml(
26639
27523
  textStyle,
26640
27524
  paragraphAlign,
26641
27525
  bulletInfo,
26642
- spacing
27526
+ spacing,
27527
+ level
26643
27528
  );
26644
- return assembleParagraphXml(runs, paragraphProps);
27529
+ return assembleParagraphXml(runs, paragraphProps, endParaRunProperties);
26645
27530
  };
26646
27531
  const createRun = (runText, style) => ({
26647
27532
  "a:rPr": this.createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId),
@@ -26689,13 +27574,19 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26689
27574
  const paragraphs = [];
26690
27575
  let currentRuns = [];
26691
27576
  let currentBulletInfo;
27577
+ let currentLevel;
27578
+ let currentEndParaRunProperties;
26692
27579
  const pushParagraph = () => {
26693
27580
  if (currentRuns.length === 0) {
26694
27581
  currentRuns.push(createRun("", textStyle));
26695
27582
  }
26696
- paragraphs.push(createParagraph(currentRuns, currentBulletInfo));
27583
+ paragraphs.push(
27584
+ createParagraph(currentRuns, currentBulletInfo, currentLevel, currentEndParaRunProperties)
27585
+ );
26697
27586
  currentRuns = [];
26698
27587
  currentBulletInfo = void 0;
27588
+ currentLevel = void 0;
27589
+ currentEndParaRunProperties = void 0;
26699
27590
  };
26700
27591
  if (textSegments && textSegments.length > 0) {
26701
27592
  const uniformSegmentOverrides = computeUniformSegmentOverrides(textStyle, textSegments);
@@ -26707,8 +27598,16 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26707
27598
  };
26708
27599
  const segmentText = String(segment.text ?? "");
26709
27600
  const lineParts = segmentText.split("\n");
26710
- if (currentRuns.length === 0 && segment.bulletInfo) {
26711
- currentBulletInfo = segment.bulletInfo;
27601
+ if (currentRuns.length === 0) {
27602
+ if (segment.bulletInfo) {
27603
+ currentBulletInfo = segment.bulletInfo;
27604
+ }
27605
+ if (segment.paragraphLevel !== void 0) {
27606
+ currentLevel = segment.paragraphLevel;
27607
+ }
27608
+ if (segment.endParaRunProperties) {
27609
+ currentEndParaRunProperties = segment.endParaRunProperties;
27610
+ }
26712
27611
  }
26713
27612
  lineParts.forEach((linePart, lineIndex) => {
26714
27613
  if (segment.rubyText !== void 0) {
@@ -26806,7 +27705,201 @@ var PptxHandlerRuntime14 = class extends PptxHandlerRuntime13 {
26806
27705
  };
26807
27706
 
26808
27707
  // src/core/core/runtime/PptxHandlerRuntimeSaveShapeXml.ts
27708
+ var OLE_OBJECT_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject";
27709
+ var IMAGE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
27710
+ var OLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/presentationml/2006/ole";
26809
27711
  var PptxHandlerRuntime15 = class _PptxHandlerRuntime extends PptxHandlerRuntime14 {
27712
+ /**
27713
+ * Build a `p:graphicFrame` XML skeleton for an SDK-created table.
27714
+ *
27715
+ * Tables round-trip as `<p:graphicFrame>/<a:graphic>/<a:graphicData
27716
+ * uri=".../drawingml/2006/table">/<a:tbl>` inside `p:spTree`. When the
27717
+ * element was loaded from an existing file, `el.rawXml` already contains
27718
+ * this envelope and the downstream `serializeTableDataToXml` path
27719
+ * populates cells in place. When the element was created via the SDK
27720
+ * (`SlideBuilder.addTable`), there is no `rawXml`, so this method
27721
+ * fabricates a minimal envelope with an empty `a:tbl`. The element
27722
+ * writer then calls `serializeTableDataToXml`, which triggers
27723
+ * `rebuildTableXmlFromData` and fills in `a:tblGrid` / `a:tr` children.
27724
+ */
27725
+ createTableGraphicFrameXml(el) {
27726
+ const EMU = _PptxHandlerRuntime.EMU_PER_PX;
27727
+ const offX = String(Math.round(el.x * EMU));
27728
+ const offY = String(Math.round(el.y * EMU));
27729
+ const extCx = String(Math.round(Math.max(el.width, 1) * EMU));
27730
+ const extCy = String(Math.round(Math.max(el.height, 1) * EMU));
27731
+ const tblPr = {
27732
+ "@_firstRow": el.tableData?.firstRowHeader ? "1" : "0",
27733
+ "@_bandRow": el.tableData?.bandedRows ? "1" : "0"
27734
+ };
27735
+ if (el.tableData?.tableStyleId) {
27736
+ tblPr["a:tableStyleId"] = el.tableData.tableStyleId;
27737
+ }
27738
+ return {
27739
+ "p:nvGraphicFramePr": {
27740
+ "p:cNvPr": { "@_id": "0", "@_name": el.name || "Table" },
27741
+ "p:cNvGraphicFramePr": {
27742
+ "a:graphicFrameLocks": { "@_noGrp": "1" }
27743
+ },
27744
+ "p:nvPr": {}
27745
+ },
27746
+ "p:xfrm": {
27747
+ "a:off": { "@_x": offX, "@_y": offY },
27748
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27749
+ },
27750
+ "a:graphic": {
27751
+ "a:graphicData": {
27752
+ "@_uri": "http://schemas.openxmlformats.org/drawingml/2006/table",
27753
+ "a:tbl": {
27754
+ "a:tblPr": tblPr,
27755
+ "a:tblGrid": {}
27756
+ }
27757
+ }
27758
+ }
27759
+ };
27760
+ }
27761
+ /**
27762
+ * Build a `p:graphicFrame` XML skeleton for an OLE object element.
27763
+ *
27764
+ * Used both for SDK-created OLE elements (no `rawXml`) and to refresh
27765
+ * a few key attributes on a loaded element when the typed fields have
27766
+ * been mutated. The output is the canonical
27767
+ * `p:graphicFrame > a:graphic > a:graphicData uri="…/ole" > p:oleObj`
27768
+ * shape per ECMA-376 §19.3.1.34 / §13.3.4.
27769
+ *
27770
+ * The caller (`processSlideElement`) is responsible for ensuring the
27771
+ * embed / preview-image relationships referenced from `r:id` / `r:embed`
27772
+ * exist in the slide's rels file. This method does not register them
27773
+ * itself because the typed model does not currently carry the binary
27774
+ * payload — the binary part must already be in the package (loaded from
27775
+ * the original file). A fully-fabricated SDK OLE element therefore
27776
+ * still requires the consumer to attach the binary out-of-band; this
27777
+ * method simply emits a schema-valid envelope referencing the
27778
+ * specified relationship ID.
27779
+ */
27780
+ createOleGraphicFrameXml(el, embedRelationshipId) {
27781
+ const EMU = _PptxHandlerRuntime.EMU_PER_PX;
27782
+ const offX = String(Math.round(el.x * EMU));
27783
+ const offY = String(Math.round(el.y * EMU));
27784
+ const extCx = String(Math.round(Math.max(el.width, 1) * EMU));
27785
+ const extCy = String(Math.round(Math.max(el.height, 1) * EMU));
27786
+ const oleObj = {
27787
+ "@_showAsIcon": "0",
27788
+ "@_imgW": extCx,
27789
+ "@_imgH": extCy
27790
+ };
27791
+ if (el.oleProgId) {
27792
+ oleObj["@_progId"] = el.oleProgId;
27793
+ }
27794
+ if (el.oleName) {
27795
+ oleObj["@_name"] = el.oleName;
27796
+ }
27797
+ if (el.oleClsId) {
27798
+ oleObj["@_classid"] = el.oleClsId;
27799
+ }
27800
+ if (embedRelationshipId) {
27801
+ oleObj["@_r:id"] = embedRelationshipId;
27802
+ }
27803
+ if (el.isLinked) {
27804
+ oleObj["p:link"] = {
27805
+ "@_r:id": embedRelationshipId,
27806
+ "@_updateAutomatic": "1"
27807
+ };
27808
+ } else {
27809
+ oleObj["p:embed"] = {};
27810
+ }
27811
+ oleObj["p:pic"] = {
27812
+ "p:nvPicPr": {
27813
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || "OleObject" },
27814
+ "p:cNvPicPr": {},
27815
+ "p:nvPr": {}
27816
+ },
27817
+ "p:blipFill": {
27818
+ "a:blip": {},
27819
+ "a:stretch": { "a:fillRect": {} }
27820
+ },
27821
+ "p:spPr": {
27822
+ "a:xfrm": {
27823
+ "a:off": { "@_x": offX, "@_y": offY },
27824
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27825
+ },
27826
+ "a:prstGeom": { "@_prst": "rect", "a:avLst": {} }
27827
+ }
27828
+ };
27829
+ return {
27830
+ "p:nvGraphicFramePr": {
27831
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || el.fileName || "OleObject" },
27832
+ "p:cNvGraphicFramePr": {
27833
+ "a:graphicFrameLocks": { "@_noChangeAspect": "1" }
27834
+ },
27835
+ "p:nvPr": {}
27836
+ },
27837
+ "p:xfrm": {
27838
+ "a:off": { "@_x": offX, "@_y": offY },
27839
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27840
+ },
27841
+ "a:graphic": {
27842
+ "a:graphicData": {
27843
+ "@_uri": OLE_GRAPHIC_DATA_URI,
27844
+ "p:oleObj": oleObj
27845
+ }
27846
+ }
27847
+ };
27848
+ }
27849
+ /**
27850
+ * Refresh editable typed-field attributes on a loaded OLE graphicFrame's
27851
+ * raw XML. Only attributes that round-trip through the typed model
27852
+ * (`progId`, `name`, `classid`) are touched so unknown extension data
27853
+ * passes through verbatim.
27854
+ */
27855
+ applyOleTypedFieldUpdates(shape, el) {
27856
+ const oleObj = shape["a:graphic"]?.["a:graphicData"]?.["p:oleObj"];
27857
+ if (!oleObj) {
27858
+ return;
27859
+ }
27860
+ if (el.oleProgId) {
27861
+ oleObj["@_progId"] = el.oleProgId;
27862
+ }
27863
+ if (el.oleName !== void 0) {
27864
+ if (el.oleName.length > 0) {
27865
+ oleObj["@_name"] = el.oleName;
27866
+ } else {
27867
+ delete oleObj["@_name"];
27868
+ }
27869
+ }
27870
+ if (el.oleClsId) {
27871
+ oleObj["@_classid"] = el.oleClsId;
27872
+ }
27873
+ }
27874
+ /** Look up the existing OLE binary relationship ID for this slide, if any. */
27875
+ resolveOleEmbedRelationshipId(slideRelationships, oleTarget) {
27876
+ if (!oleTarget) {
27877
+ return void 0;
27878
+ }
27879
+ const normalisedTarget = oleTarget.replace(/^ppt\//, "../").replace(/^\/+/, "");
27880
+ const lowerTarget = normalisedTarget.toLowerCase();
27881
+ for (const rel of slideRelationships) {
27882
+ const relType = String(rel?.["@_Type"] || "");
27883
+ if (relType !== OLE_OBJECT_RELATIONSHIP_TYPE) {
27884
+ continue;
27885
+ }
27886
+ const target = String(rel?.["@_Target"] || "").toLowerCase().trim();
27887
+ if (target === lowerTarget || target.endsWith(lowerTarget) || lowerTarget.endsWith(target)) {
27888
+ const relId = String(rel?.["@_Id"] || "").trim();
27889
+ if (relId.length > 0) {
27890
+ return relId;
27891
+ }
27892
+ }
27893
+ }
27894
+ const fallback = slideRelationships.find(
27895
+ (rel) => String(rel?.["@_Type"] || "") === OLE_OBJECT_RELATIONSHIP_TYPE
27896
+ );
27897
+ const fallbackId = String(fallback?.["@_Id"] || "").trim();
27898
+ return fallbackId.length > 0 ? fallbackId : void 0;
27899
+ }
27900
+ /** Constants are exposed so the element-writer mixin can reuse them. */
27901
+ static OLE_OBJECT_RELATIONSHIP_TYPE = OLE_OBJECT_RELATIONSHIP_TYPE;
27902
+ static OLE_IMAGE_RELATIONSHIP_TYPE = IMAGE_RELATIONSHIP_TYPE;
26810
27903
  /**
26811
27904
  * Build a p:sp XML object for an ink annotation element.
26812
27905
  * Each ink path becomes a separate a:path within a:pathLst,
@@ -27123,6 +28216,9 @@ var PptxHandlerRuntime16 = class extends PptxHandlerRuntime15 {
27123
28216
  buildOuterShadowXml(shapeStyle) {
27124
28217
  return this.colorStyleCodec.buildOuterShadowXml(shapeStyle);
27125
28218
  }
28219
+ buildPresetShadowXml(shapeStyle) {
28220
+ return this.colorStyleCodec.buildPresetShadowXml(shapeStyle);
28221
+ }
27126
28222
  buildInnerShadowXml(shapeStyle) {
27127
28223
  return this.colorStyleCodec.buildInnerShadowXml(shapeStyle);
27128
28224
  }
@@ -27673,6 +28769,10 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27673
28769
  const solidFill = runProperties["a:solidFill"];
27674
28770
  if (solidFill) {
27675
28771
  style.color = this.parseColor(solidFill);
28772
+ const colorXml = extractColorChoiceXml(solidFill);
28773
+ if (colorXml) {
28774
+ style.colorXml = colorXml;
28775
+ }
27676
28776
  }
27677
28777
  this.applyHyperlinkStyle(style, runProperties, relationshipMap);
27678
28778
  const capAttr = String(runProperties["@_cap"] || "").trim().toLowerCase();
@@ -27720,12 +28820,86 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27720
28820
  if (bmk) {
27721
28821
  style.bookmark = bmk;
27722
28822
  }
28823
+ const altLang = String(runProperties["@_altLang"] || "").trim();
28824
+ if (altLang) {
28825
+ style.altLanguage = altLang;
28826
+ }
28827
+ if (runProperties["@_smtId"] !== void 0) {
28828
+ const smtIdRaw = Number.parseInt(String(runProperties["@_smtId"]), 10);
28829
+ if (Number.isFinite(smtIdRaw)) {
28830
+ style.smartTagId = smtIdRaw;
28831
+ }
28832
+ }
28833
+ this.applyTextFontMetadata(style, latin, "latin");
28834
+ this.applyTextFontMetadata(style, eastAsian, "eastAsia");
28835
+ this.applyTextFontMetadata(style, complexScript, "complexScript");
28836
+ this.applyTextFontMetadata(style, runProperties["a:sym"], "symbol");
27723
28837
  const runEffectList = runProperties["a:effectLst"];
27724
28838
  if (runEffectList) {
27725
28839
  this.applyTextRunEffects(style, runEffectList);
27726
28840
  }
27727
28841
  return style;
27728
28842
  }
28843
+ /**
28844
+ * Copy `@panose` / `@pitchFamily` / `@charset` from a font child node
28845
+ * (`a:latin`, `a:ea`, `a:cs`, `a:sym`) onto the matching `*Font*`
28846
+ * fields of `style`.
28847
+ */
28848
+ applyTextFontMetadata(style, fontNode, kind) {
28849
+ if (!fontNode) {
28850
+ return;
28851
+ }
28852
+ const panose = String(fontNode["@_panose"] || "").trim();
28853
+ const pitchRaw = fontNode["@_pitchFamily"];
28854
+ const charsetRaw = fontNode["@_charset"];
28855
+ const pitch = pitchRaw !== void 0 && pitchRaw !== null ? Number.parseInt(String(pitchRaw), 10) : void 0;
28856
+ const charset = charsetRaw !== void 0 && charsetRaw !== null ? Number.parseInt(String(charsetRaw), 10) : void 0;
28857
+ if (kind === "latin") {
28858
+ if (panose) {
28859
+ style.latinFontPanose = panose;
28860
+ }
28861
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28862
+ style.latinFontPitchFamily = pitch;
28863
+ }
28864
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28865
+ style.latinFontCharset = charset;
28866
+ }
28867
+ return;
28868
+ }
28869
+ if (kind === "eastAsia") {
28870
+ if (panose) {
28871
+ style.eastAsiaFontPanose = panose;
28872
+ }
28873
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28874
+ style.eastAsiaFontPitchFamily = pitch;
28875
+ }
28876
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28877
+ style.eastAsiaFontCharset = charset;
28878
+ }
28879
+ return;
28880
+ }
28881
+ if (kind === "complexScript") {
28882
+ if (panose) {
28883
+ style.complexScriptFontPanose = panose;
28884
+ }
28885
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28886
+ style.complexScriptFontPitchFamily = pitch;
28887
+ }
28888
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28889
+ style.complexScriptFontCharset = charset;
28890
+ }
28891
+ return;
28892
+ }
28893
+ if (panose) {
28894
+ style.symbolFontPanose = panose;
28895
+ }
28896
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28897
+ style.symbolFontPitchFamily = pitch;
28898
+ }
28899
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28900
+ style.symbolFontCharset = charset;
28901
+ }
28902
+ }
27729
28903
  };
27730
28904
 
27731
28905
  // src/core/core/runtime/PptxHandlerRuntimeTextEditing.ts
@@ -28161,7 +29335,7 @@ var PptxHandlerRuntime21 = class extends PptxHandlerRuntime20 {
28161
29335
  };
28162
29336
 
28163
29337
  // src/core/core/runtime/table-cell-save-helpers.ts
28164
- function writeCellFill(tcPr, style) {
29338
+ function writeCellFill(tcPr, style, resolveColorXml) {
28165
29339
  if (style.fillMode === "gradient" && style.gradientFillStops && style.gradientFillStops.length > 0) {
28166
29340
  delete tcPr["a:solidFill"];
28167
29341
  delete tcPr["a:pattFill"];
@@ -28240,11 +29414,12 @@ function writeCellFill(tcPr, style) {
28240
29414
  } else if (style.backgroundColor) {
28241
29415
  delete tcPr["a:gradFill"];
28242
29416
  delete tcPr["a:pattFill"];
28243
- tcPr["a:solidFill"] = {
28244
- "a:srgbClr": {
28245
- "@_val": style.backgroundColor.replace("#", "")
28246
- }
28247
- };
29417
+ const resolvedOriginal = style.backgroundColorXml && resolveColorXml ? resolveColorXml(style.backgroundColorXml) : void 0;
29418
+ tcPr["a:solidFill"] = serializeColorChoice(
29419
+ style.backgroundColorXml,
29420
+ resolvedOriginal,
29421
+ style.backgroundColor
29422
+ );
28248
29423
  }
28249
29424
  }
28250
29425
  function writeDiagonalBorders(tcPr, style, emuPerPx) {
@@ -28288,14 +29463,15 @@ function writeCellTextFormatting(xmlCell, style, ensureArray6) {
28288
29463
  const paragraphs = ensureArray6(xmlCell["a:txBody"]?.["a:p"]);
28289
29464
  for (const paragraph of paragraphs) {
28290
29465
  const runs = ensureArray6(paragraph?.["a:r"]);
29466
+ const rebuiltRuns = [];
29467
+ let runsChanged = false;
28291
29468
  for (const run of runs) {
28292
29469
  if (!run) {
29470
+ rebuiltRuns.push(run);
28293
29471
  continue;
28294
29472
  }
28295
- if (!run["a:rPr"]) {
28296
- run["a:rPr"] = {};
28297
- }
28298
- const rPr = run["a:rPr"];
29473
+ const existingRPr = run["a:rPr"] ?? {};
29474
+ const rPr = { ...existingRPr };
28299
29475
  if (style.bold !== void 0) {
28300
29476
  rPr["@_b"] = style.bold ? "1" : "0";
28301
29477
  }
@@ -28315,6 +29491,18 @@ function writeCellTextFormatting(xmlCell, style, ensureArray6) {
28315
29491
  }
28316
29492
  };
28317
29493
  }
29494
+ const rebuilt = { "a:rPr": rPr };
29495
+ for (const key of Object.keys(run)) {
29496
+ if (key === "a:rPr") {
29497
+ continue;
29498
+ }
29499
+ rebuilt[key] = run[key];
29500
+ }
29501
+ rebuiltRuns.push(rebuilt);
29502
+ runsChanged = true;
29503
+ }
29504
+ if (runsChanged && rebuiltRuns.length > 0) {
29505
+ paragraph["a:r"] = rebuiltRuns.length === 1 ? rebuiltRuns[0] : rebuiltRuns;
28318
29506
  }
28319
29507
  }
28320
29508
  }
@@ -28336,6 +29524,9 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28336
29524
  const existingParagraphs = this.ensureArray(txBody["a:p"]);
28337
29525
  const firstRPr = this.ensureArray(existingParagraphs[0]?.["a:r"])[0]?.["a:rPr"];
28338
29526
  const firstPPr = existingParagraphs[0]?.["a:pPr"];
29527
+ const firstEndParaRPr = existingParagraphs[0]?.["a:endParaRPr"];
29528
+ const rPrForRun = firstRPr ? { ...firstRPr } : { "@_lang": "en-US", "@_dirty": "0" };
29529
+ const endParaRPr = firstEndParaRPr ? { ...firstEndParaRPr } : { "@_lang": "en-US", "@_dirty": "0" };
28339
29530
  const lines = text.split("\n");
28340
29531
  const paragraphs = lines.map((line) => {
28341
29532
  const paragraph = {};
@@ -28343,9 +29534,10 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28343
29534
  paragraph["a:pPr"] = firstPPr;
28344
29535
  }
28345
29536
  paragraph["a:r"] = {
28346
- ...firstRPr ? { "a:rPr": firstRPr } : {},
29537
+ "a:rPr": rPrForRun,
28347
29538
  "a:t": line
28348
29539
  };
29540
+ paragraph["a:endParaRPr"] = endParaRPr;
28349
29541
  return paragraph;
28350
29542
  });
28351
29543
  txBody["a:p"] = paragraphs.length === 1 ? paragraphs[0] : paragraphs;
@@ -28358,7 +29550,7 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28358
29550
  xmlCell["a:tcPr"] = {};
28359
29551
  }
28360
29552
  const tcPr = xmlCell["a:tcPr"];
28361
- writeCellFill(tcPr, style);
29553
+ writeCellFill(tcPr, style, (colorXml) => this.parseColor(colorXml));
28362
29554
  if (style.vAlign) {
28363
29555
  const vAlignMap = {
28364
29556
  top: "t",
@@ -28440,35 +29632,29 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28440
29632
  }
28441
29633
  }
28442
29634
  }
28443
- if (style.marginLeft !== void 0 || style.marginRight !== void 0 || style.marginTop !== void 0 || style.marginBottom !== void 0) {
28444
- const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
28445
- if (!tcPr["a:tcMar"]) {
28446
- tcPr["a:tcMar"] = {};
28447
- }
28448
- const tcMar = tcPr["a:tcMar"];
28449
- if (style.marginLeft !== void 0) {
28450
- tcMar["a:marL"] = {
28451
- "@_w": String(Math.round(style.marginLeft * emuPerPx))
28452
- };
28453
- }
28454
- if (style.marginRight !== void 0) {
28455
- tcMar["a:marR"] = {
28456
- "@_w": String(Math.round(style.marginRight * emuPerPx))
28457
- };
28458
- }
28459
- if (style.marginTop !== void 0) {
28460
- tcMar["a:marT"] = {
28461
- "@_w": String(Math.round(style.marginTop * emuPerPx))
28462
- };
28463
- }
28464
- if (style.marginBottom !== void 0) {
28465
- tcMar["a:marB"] = {
28466
- "@_w": String(Math.round(style.marginBottom * emuPerPx))
28467
- };
28468
- }
29635
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
29636
+ if (style.marginLeft !== void 0) {
29637
+ tcPr["@_marL"] = String(Math.round(style.marginLeft * emuPerPx));
29638
+ }
29639
+ if (style.marginRight !== void 0) {
29640
+ tcPr["@_marR"] = String(Math.round(style.marginRight * emuPerPx));
29641
+ }
29642
+ if (style.marginTop !== void 0) {
29643
+ tcPr["@_marT"] = String(Math.round(style.marginTop * emuPerPx));
29644
+ }
29645
+ if (style.marginBottom !== void 0) {
29646
+ tcPr["@_marB"] = String(Math.round(style.marginBottom * emuPerPx));
28469
29647
  }
29648
+ delete tcPr["a:tcMar"];
28470
29649
  writeDiagonalBorders(tcPr, style, _PptxHandlerRuntime.EMU_PER_PX);
28471
29650
  writeCellTextFormatting(xmlCell, style, this.ensureArray.bind(this));
29651
+ const reordered = reorderObjectKeys(tcPr, TC_PR_BORDERS_ORDER);
29652
+ for (const key of Object.keys(tcPr)) {
29653
+ delete tcPr[key];
29654
+ }
29655
+ for (const key of Object.keys(reordered)) {
29656
+ tcPr[key] = reordered[key];
29657
+ }
28472
29658
  }
28473
29659
  };
28474
29660
 
@@ -28495,14 +29681,27 @@ function serializeCellMergeAttributes(xmlCell, cell) {
28495
29681
  delete xmlCell["@_vMerge"];
28496
29682
  }
28497
29683
  }
29684
+ var DEFAULT_POWERPOINT_TABLE_STYLE_ID = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
28498
29685
  function serializeTablePropertyFlags(tbl, tableData) {
28499
29686
  const tblPr = tbl["a:tblPr"] ?? {};
28500
- tblPr["@_bandRow"] = tableData.bandedRows ? "1" : "0";
28501
- tblPr["@_bandCol"] = tableData.bandedColumns ? "1" : "0";
28502
- tblPr["@_firstRow"] = tableData.firstRowHeader ? "1" : "0";
28503
- tblPr["@_lastRow"] = tableData.lastRow ? "1" : "0";
28504
- tblPr["@_firstCol"] = tableData.firstCol ? "1" : "0";
28505
- tblPr["@_lastCol"] = tableData.lastCol ? "1" : "0";
29687
+ const setOrDelete = (key, truthy) => {
29688
+ if (truthy) {
29689
+ tblPr[key] = "1";
29690
+ } else {
29691
+ delete tblPr[key];
29692
+ }
29693
+ };
29694
+ setOrDelete("@_bandRow", tableData.bandedRows);
29695
+ setOrDelete("@_bandCol", tableData.bandedColumns);
29696
+ setOrDelete("@_firstRow", tableData.firstRowHeader);
29697
+ setOrDelete("@_lastRow", tableData.lastRow);
29698
+ setOrDelete("@_firstCol", tableData.firstCol);
29699
+ setOrDelete("@_lastCol", tableData.lastCol);
29700
+ if (tableData.tableStyleId) {
29701
+ tblPr["a:tableStyleId"] = tableData.tableStyleId;
29702
+ } else if (!tblPr["a:tableStyleId"]) {
29703
+ tblPr["a:tableStyleId"] = DEFAULT_POWERPOINT_TABLE_STYLE_ID;
29704
+ }
28506
29705
  tbl["a:tblPr"] = tblPr;
28507
29706
  }
28508
29707
  function replaceFirstTextValueInTree(node, localName, newValue, getXmlLocalName) {
@@ -28547,7 +29746,9 @@ function createDefaultXmlCell() {
28547
29746
  "a:bodyPr": {},
28548
29747
  "a:lstStyle": {},
28549
29748
  "a:p": {
28550
- "a:endParaRPr": { "@_lang": "en-US" }
29749
+ // Match PowerPoint's "Insert Table" default: every paragraph-end
29750
+ // run carries `lang="en-US" dirty="0"`.
29751
+ "a:endParaRPr": { "@_lang": "en-US", "@_dirty": "0" }
28551
29752
  }
28552
29753
  },
28553
29754
  "a:tcPr": {}
@@ -28555,6 +29756,54 @@ function createDefaultXmlCell() {
28555
29756
  }
28556
29757
 
28557
29758
  // src/core/core/runtime/table-xml-rebuild.ts
29759
+ var GRID_COL_ID_EXT_URI = "{9D8B030D-6E8A-4147-A177-3AD203B41FA5}";
29760
+ var A16_NAMESPACE = "http://schemas.microsoft.com/office/drawing/2014/main";
29761
+ function ensureA16NamespaceOnSlideRoot(slideRoot) {
29762
+ if (!slideRoot["@_xmlns:a16"]) {
29763
+ slideRoot["@_xmlns:a16"] = A16_NAMESPACE;
29764
+ }
29765
+ if (!slideRoot["@_xmlns:mc"]) {
29766
+ slideRoot["@_xmlns:mc"] = "http://schemas.openxmlformats.org/markup-compatibility/2006";
29767
+ }
29768
+ const existingIgnorable = String(slideRoot["@_mc:Ignorable"] || "").trim();
29769
+ if (existingIgnorable.length === 0) {
29770
+ slideRoot["@_mc:Ignorable"] = "a16";
29771
+ return;
29772
+ }
29773
+ const tokens = existingIgnorable.split(/\s+/).filter((token) => token.length > 0);
29774
+ if (!tokens.includes("a16")) {
29775
+ tokens.push("a16");
29776
+ slideRoot["@_mc:Ignorable"] = tokens.join(" ");
29777
+ }
29778
+ }
29779
+ function slideContainsA16Element(node) {
29780
+ if (node === null || node === void 0) {
29781
+ return false;
29782
+ }
29783
+ if (Array.isArray(node)) {
29784
+ for (const entry of node) {
29785
+ if (slideContainsA16Element(entry)) {
29786
+ return true;
29787
+ }
29788
+ }
29789
+ return false;
29790
+ }
29791
+ if (typeof node !== "object") {
29792
+ return false;
29793
+ }
29794
+ for (const [key, value] of Object.entries(node)) {
29795
+ if (key.startsWith("a16:")) {
29796
+ return true;
29797
+ }
29798
+ if (slideContainsA16Element(value)) {
29799
+ return true;
29800
+ }
29801
+ }
29802
+ return false;
29803
+ }
29804
+ function randomColumnId() {
29805
+ return String(Math.floor(Math.random() * 4294967295));
29806
+ }
28558
29807
  function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
28559
29808
  const existingXmlRows = ensureArrayFn(tbl["a:tr"]);
28560
29809
  const existingGridCols = ensureArrayFn(
@@ -28563,8 +29812,33 @@ function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
28563
29812
  const totalWidthEmu = existingGridCols.reduce((sum, col) => {
28564
29813
  return sum + (parseInt(String(col?.["@_w"] || "0"), 10) || 0);
28565
29814
  }, 0) || 9144e3;
28566
- const newGridCols = tableData.columnWidths.map((w) => ({
28567
- "@_w": String(Math.round(w * totalWidthEmu))
29815
+ const existingColIds = existingGridCols.map((col) => {
29816
+ const extList = col?.["a:extLst"];
29817
+ const exts = Array.isArray(extList?.["a:ext"]) ? extList["a:ext"] : extList?.["a:ext"] ? [extList["a:ext"]] : [];
29818
+ for (const ext of exts) {
29819
+ if (ext?.["@_uri"] === GRID_COL_ID_EXT_URI) {
29820
+ const colId = ext["a16:colId"];
29821
+ const val = colId?.["@_val"];
29822
+ if (typeof val === "string" && val.length > 0) {
29823
+ return val;
29824
+ }
29825
+ }
29826
+ }
29827
+ return "";
29828
+ });
29829
+ const newGridCols = tableData.columnWidths.map((w, i) => ({
29830
+ "@_w": String(Math.round(w * totalWidthEmu)),
29831
+ "a:extLst": {
29832
+ "a:ext": {
29833
+ "@_uri": GRID_COL_ID_EXT_URI,
29834
+ // `xmlns:a16` is declared on the slide root by
29835
+ // `ensureA16NamespaceOnSlideRoot`; emitting it here too is
29836
+ // schema-redundant and PowerPoint flags it.
29837
+ "a16:colId": {
29838
+ "@_val": existingColIds[i] || randomColumnId()
29839
+ }
29840
+ }
29841
+ }
28568
29842
  }));
28569
29843
  if (!tbl["a:tblGrid"]) {
28570
29844
  tbl["a:tblGrid"] = {};
@@ -28919,26 +30193,64 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28919
30193
  continue;
28920
30194
  }
28921
30195
  const scalingNode = this.xmlLookupService.getChildByLocalName(axisNode, "scaling");
28922
- if (!scalingNode) {
28923
- continue;
30196
+ if (scalingNode) {
30197
+ if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
30198
+ const logBaseKey = Object.keys(scalingNode).find(
30199
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30200
+ );
30201
+ if (logBaseKey) {
30202
+ scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
30203
+ } else {
30204
+ scalingNode["c:logBase"] = {
30205
+ "@_val": String(matchingAxis.logBase)
30206
+ };
30207
+ }
30208
+ } else if (matchingAxis.logScale === false) {
30209
+ const logBaseKey = Object.keys(scalingNode).find(
30210
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30211
+ );
30212
+ if (logBaseKey) {
30213
+ delete scalingNode[logBaseKey];
30214
+ }
30215
+ }
30216
+ this.upsertChartAxisChild(
30217
+ scalingNode,
30218
+ "min",
30219
+ matchingAxis.min !== void 0 ? String(matchingAxis.min) : void 0
30220
+ );
30221
+ this.upsertChartAxisChild(
30222
+ scalingNode,
30223
+ "max",
30224
+ matchingAxis.max !== void 0 ? String(matchingAxis.max) : void 0
30225
+ );
28924
30226
  }
28925
- if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
28926
- const logBaseKey = Object.keys(scalingNode).find(
28927
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30227
+ if (matchingAxis.numFmt) {
30228
+ const numFmtKey = Object.keys(axisNode).find(
30229
+ (k) => this.compatibilityService.getXmlLocalName(k) === "numFmt"
28928
30230
  );
28929
- if (logBaseKey) {
28930
- scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
30231
+ const numFmtAttrs = {
30232
+ "@_formatCode": matchingAxis.numFmt.formatCode,
30233
+ "@_sourceLinked": matchingAxis.numFmt.sourceLinked ? "1" : "0"
30234
+ };
30235
+ if (numFmtKey) {
30236
+ axisNode[numFmtKey] = numFmtAttrs;
28931
30237
  } else {
28932
- scalingNode["c:logBase"] = {
28933
- "@_val": String(matchingAxis.logBase)
28934
- };
30238
+ axisNode["c:numFmt"] = numFmtAttrs;
28935
30239
  }
28936
- } else if (matchingAxis.logScale === false) {
28937
- const logBaseKey = Object.keys(scalingNode).find(
28938
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30240
+ }
30241
+ this.upsertChartAxisChild(
30242
+ axisNode,
30243
+ "majorUnit",
30244
+ matchingAxis.majorUnit !== void 0 ? String(matchingAxis.majorUnit) : void 0
30245
+ );
30246
+ if (matchingAxis.tickLblPos !== void 0) {
30247
+ const tickLblKey = Object.keys(axisNode).find(
30248
+ (k) => this.compatibilityService.getXmlLocalName(k) === "tickLblPos"
28939
30249
  );
28940
- if (logBaseKey) {
28941
- delete scalingNode[logBaseKey];
30250
+ if (tickLblKey) {
30251
+ axisNode[tickLblKey]["@_val"] = matchingAxis.tickLblPos;
30252
+ } else {
30253
+ axisNode["c:tickLblPos"] = { "@_val": matchingAxis.tickLblPos };
28942
30254
  }
28943
30255
  }
28944
30256
  }
@@ -28951,6 +30263,18 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28951
30263
  }
28952
30264
  this.pendingChartUpdates = void 0;
28953
30265
  }
30266
+ /**
30267
+ * Upsert a `c:<localName>` child with `@_val` on an axis or scaling node.
30268
+ * When `value` is undefined, removes any existing child of that local name.
30269
+ */
30270
+ upsertChartAxisChild(parent, localName, value) {
30271
+ upsertChartAxisChild(
30272
+ parent,
30273
+ localName,
30274
+ value,
30275
+ (key) => this.compatibilityService.getXmlLocalName(key)
30276
+ );
30277
+ }
28954
30278
  /**
28955
30279
  * Update the cached point values in a chart reference node
28956
30280
  * (numRef/strRef or numLit/strLit).
@@ -30230,17 +31554,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30230
31554
  delete spPr["a:noFill"];
30231
31555
  delete spPr["a:gradFill"];
30232
31556
  delete spPr["a:blipFill"];
30233
- const solidFill = {
30234
- "a:srgbClr": {
30235
- "@_val": fillColor.replace("#", "")
30236
- }
30237
- };
30238
- if (typeof shapeStyle.fillOpacity === "number" && shapeStyle.fillOpacity >= 0 && shapeStyle.fillOpacity < 1) {
30239
- solidFill["a:srgbClr"]["a:alpha"] = {
30240
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.fillOpacity) * 1e5))
30241
- };
30242
- }
30243
- spPr["a:solidFill"] = solidFill;
31557
+ const resolvedOriginal = shapeStyle.fillColorXml ? this.parseColor(shapeStyle.fillColorXml) : void 0;
31558
+ spPr["a:solidFill"] = serializeColorChoice(
31559
+ shapeStyle.fillColorXml,
31560
+ resolvedOriginal,
31561
+ fillColor,
31562
+ shapeStyle.fillOpacity
31563
+ );
30244
31564
  }
30245
31565
  }
30246
31566
  if (shapeStyle.strokeColor !== void 0) {
@@ -30255,17 +31575,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30255
31575
  delete lineNode["a:solidFill"];
30256
31576
  } else {
30257
31577
  delete lineNode["a:noFill"];
30258
- const lineFill = {
30259
- "a:srgbClr": {
30260
- "@_val": shapeStyle.strokeColor.replace("#", "")
30261
- }
30262
- };
30263
- if (typeof shapeStyle.strokeOpacity === "number" && shapeStyle.strokeOpacity >= 0 && shapeStyle.strokeOpacity < 1) {
30264
- lineFill["a:srgbClr"]["a:alpha"] = {
30265
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.strokeOpacity) * 1e5))
30266
- };
30267
- }
30268
- lineNode["a:solidFill"] = lineFill;
31578
+ const resolvedStrokeOriginal = shapeStyle.strokeColorXml ? this.parseColor(shapeStyle.strokeColorXml) : void 0;
31579
+ lineNode["a:solidFill"] = serializeColorChoice(
31580
+ shapeStyle.strokeColorXml,
31581
+ resolvedStrokeOriginal,
31582
+ shapeStyle.strokeColor,
31583
+ shapeStyle.strokeOpacity
31584
+ );
30269
31585
  }
30270
31586
  }
30271
31587
  if (shapeStyle.strokeDash !== void 0) {
@@ -30346,7 +31662,11 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30346
31662
  } else if (shapeStyle.lineJoin === "bevel") {
30347
31663
  lineNode["a:bevel"] = {};
30348
31664
  } else if (shapeStyle.lineJoin === "miter") {
30349
- lineNode["a:miter"] = { "@_lim": "800000" };
31665
+ const miterNode = {};
31666
+ if (typeof shapeStyle.miterLimit === "number" && Number.isFinite(shapeStyle.miterLimit) && shapeStyle.miterLimit !== 8e5) {
31667
+ miterNode["@_lim"] = String(Math.round(shapeStyle.miterLimit));
31668
+ }
31669
+ lineNode["a:miter"] = miterNode;
30350
31670
  }
30351
31671
  }
30352
31672
  if (shapeStyle.lineCap !== void 0) {
@@ -30366,6 +31686,82 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30366
31686
  spPr["a:ln"]["a:effectLst"] = lineEffectListXml;
30367
31687
  }
30368
31688
  }
31689
+ /**
31690
+ * Serialize the shape's `<p:style>` block (CT_ShapeStyle §20.1.2.2.36)
31691
+ * from the persisted ref indices/colour XML. Emits children in spec
31692
+ * order: `lnRef → fillRef → effectRef → fontRef`.
31693
+ *
31694
+ * When the original shape XML already contained a `<p:style>` we mutate
31695
+ * that node in place so any unmodelled attributes/children are preserved.
31696
+ * When it didn't, we create one. When the shape no longer has any ref
31697
+ * data we leave the existing `<p:style>` (if any) untouched — silently
31698
+ * dropping it would break round-tripping.
31699
+ *
31700
+ * Phase 2 Stream B / C-H2.
31701
+ */
31702
+ applyShapeStyleRefs(shape, shapeStyle) {
31703
+ const hasAnyRef = shapeStyle.lnRefIdx !== void 0 || shapeStyle.fillRefIdx !== void 0 || shapeStyle.effectRefIdx !== void 0 || shapeStyle.fontRefIdx !== void 0;
31704
+ if (!hasAnyRef) {
31705
+ return;
31706
+ }
31707
+ const existing = shape["p:style"];
31708
+ const styleNode = existing ?? {};
31709
+ if (shapeStyle.lnRefIdx !== void 0) {
31710
+ const lnRef = styleNode["a:lnRef"] ?? {};
31711
+ lnRef["@_idx"] = String(shapeStyle.lnRefIdx);
31712
+ this.replaceRefColorChoice(lnRef, shapeStyle.lnRefColorXml);
31713
+ styleNode["a:lnRef"] = lnRef;
31714
+ }
31715
+ if (shapeStyle.fillRefIdx !== void 0) {
31716
+ const fillRef = styleNode["a:fillRef"] ?? {};
31717
+ fillRef["@_idx"] = String(shapeStyle.fillRefIdx);
31718
+ this.replaceRefColorChoice(fillRef, shapeStyle.fillRefColorXml);
31719
+ styleNode["a:fillRef"] = fillRef;
31720
+ }
31721
+ if (shapeStyle.effectRefIdx !== void 0) {
31722
+ const effectRef = styleNode["a:effectRef"] ?? {};
31723
+ effectRef["@_idx"] = String(shapeStyle.effectRefIdx);
31724
+ this.replaceRefColorChoice(effectRef, shapeStyle.effectRefColorXml);
31725
+ styleNode["a:effectRef"] = effectRef;
31726
+ }
31727
+ if (shapeStyle.fontRefIdx !== void 0) {
31728
+ const fontRef = styleNode["a:fontRef"] ?? {};
31729
+ fontRef["@_idx"] = shapeStyle.fontRefIdx;
31730
+ this.replaceRefColorChoice(fontRef, shapeStyle.fontRefColorXml);
31731
+ styleNode["a:fontRef"] = fontRef;
31732
+ }
31733
+ const reordered = reorderObjectKeys(styleNode, SHAPE_STYLE_ORDER);
31734
+ for (const key of Object.keys(styleNode)) {
31735
+ delete styleNode[key];
31736
+ }
31737
+ for (const key of Object.keys(reordered)) {
31738
+ styleNode[key] = reordered[key];
31739
+ }
31740
+ shape["p:style"] = styleNode;
31741
+ }
31742
+ /**
31743
+ * Replace any existing colour-choice child on a style-matrix-reference
31744
+ * element with the given preserved XML, or strip all colour children
31745
+ * when the override is undefined.
31746
+ */
31747
+ replaceRefColorChoice(refNode, colorXml) {
31748
+ for (const key of [
31749
+ "a:scrgbClr",
31750
+ "a:srgbClr",
31751
+ "a:hslClr",
31752
+ "a:sysClr",
31753
+ "a:schemeClr",
31754
+ "a:prstClr"
31755
+ ]) {
31756
+ delete refNode[key];
31757
+ }
31758
+ if (!colorXml) {
31759
+ return;
31760
+ }
31761
+ for (const [key, value] of Object.entries(colorXml)) {
31762
+ refNode[key] = value;
31763
+ }
31764
+ }
30369
31765
  };
30370
31766
 
30371
31767
  // src/core/core/runtime/PptxHandlerRuntimeSaveEffectsWriter.ts
@@ -30375,17 +31771,22 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30375
31771
  * effectDag, 3D scene, and 3D shape properties to the given spPr XML object.
30376
31772
  */
30377
31773
  applyEffectsAndThreeD(spPr, shapeStyle) {
30378
- const outerShadowXml = this.buildOuterShadowXml(shapeStyle);
31774
+ const presetShadowXml = shapeStyle.presetShadowName ? this.buildPresetShadowXml(shapeStyle) : void 0;
31775
+ const outerShadowXml = presetShadowXml ? void 0 : this.buildOuterShadowXml(shapeStyle);
30379
31776
  const innerShadowXml = this.buildInnerShadowXml(shapeStyle);
30380
31777
  const glowXml = this.buildGlowXml(shapeStyle);
30381
31778
  const softEdgeXml = this.buildSoftEdgeXml(shapeStyle);
30382
31779
  const reflectionXml = this.buildReflectionXml(shapeStyle);
30383
31780
  const blurXml = this.buildBlurXml(shapeStyle);
30384
- const hasAnyEffect = outerShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
31781
+ const hasAnyEffect = outerShadowXml || presetShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
30385
31782
  if (hasAnyEffect) {
30386
31783
  const effectList = spPr["a:effectLst"] || {};
30387
- if (outerShadowXml) {
31784
+ if (presetShadowXml) {
31785
+ effectList["a:prstShdw"] = presetShadowXml;
31786
+ delete effectList["a:outerShdw"];
31787
+ } else if (outerShadowXml) {
30388
31788
  effectList["a:outerShdw"] = outerShadowXml;
31789
+ delete effectList["a:prstShdw"];
30389
31790
  }
30390
31791
  if (innerShadowXml) {
30391
31792
  effectList["a:innerShdw"] = innerShadowXml;
@@ -30402,12 +31803,13 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30402
31803
  if (blurXml) {
30403
31804
  effectList["a:blur"] = blurXml;
30404
31805
  }
30405
- spPr["a:effectLst"] = effectList;
31806
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30406
31807
  } else {
30407
31808
  const effectList = spPr["a:effectLst"];
30408
31809
  if (effectList) {
30409
- if (shapeStyle.shadowColor !== void 0 && !outerShadowXml) {
31810
+ if (shapeStyle.shadowColor !== void 0 && !outerShadowXml && !presetShadowXml) {
30410
31811
  delete effectList["a:outerShdw"];
31812
+ delete effectList["a:prstShdw"];
30411
31813
  }
30412
31814
  if (shapeStyle.innerShadowColor !== void 0 && !innerShadowXml) {
30413
31815
  delete effectList["a:innerShdw"];
@@ -30426,6 +31828,8 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30426
31828
  }
30427
31829
  if (Object.keys(effectList).length === 0) {
30428
31830
  delete spPr["a:effectLst"];
31831
+ } else {
31832
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30429
31833
  }
30430
31834
  }
30431
31835
  }
@@ -30586,7 +31990,8 @@ var PptxHandlerRuntime30 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30586
31990
  );
30587
31991
  }
30588
31992
  if (el.textStyle?.hOverflow) {
30589
- bodyPr["@_hOverflow"] = el.textStyle.hOverflow;
31993
+ bodyPr["@_horzOverflow"] = el.textStyle.hOverflow;
31994
+ delete bodyPr["@_hOverflow"];
30590
31995
  }
30591
31996
  if (el.textStyle?.vertOverflow) {
30592
31997
  bodyPr["@_vertOverflow"] = el.textStyle.vertOverflow;
@@ -30817,7 +32222,10 @@ var PptxHandlerRuntime31 = class extends PptxHandlerRuntime30 {
30817
32222
  const elWithPaths = el;
30818
32223
  if (elWithPaths.customGeometryPaths && elWithPaths.customGeometryPaths.length > 0) {
30819
32224
  delete spPr["a:prstGeom"];
30820
- spPr["a:custGeom"] = customGeometryPathsToXml(elWithPaths.customGeometryPaths);
32225
+ spPr["a:custGeom"] = customGeometryPathsToXml(
32226
+ elWithPaths.customGeometryPaths,
32227
+ elWithPaths.customGeometryRawData
32228
+ );
30821
32229
  } else if (spPr["a:prstGeom"]) {
30822
32230
  const presetGeometry = el.type === "connector" ? this.normalizePresetGeometry(el.shapeType || "straightConnector1") : this.normalizePresetGeometry(el.shapeType);
30823
32231
  const prstGeom = spPr["a:prstGeom"];
@@ -30942,6 +32350,42 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
30942
32350
  isGraphicFrameShape(shape) {
30943
32351
  return Boolean(shape["p:nvGraphicFramePr"] || shape["a:graphic"] && shape["p:xfrm"]);
30944
32352
  }
32353
+ /**
32354
+ * Reorder children of `p:spPr` to match CT_ShapeProperties (§20.1.2.2.35).
32355
+ * Also reorders any nested `a:blipFill` per CT_BlipFillProperties.
32356
+ * fast-xml-parser preserves insertion order; PowerPoint validates against
32357
+ * the schema's required order, so save-side mutations must be re-sorted.
32358
+ */
32359
+ finalizeSpPrSchemaOrder(shape) {
32360
+ const spPr = shape["p:spPr"];
32361
+ if (!spPr) {
32362
+ return;
32363
+ }
32364
+ const blipFill = spPr["a:blipFill"];
32365
+ if (blipFill) {
32366
+ this.reorderInPlace(blipFill, BLIP_FILL_ORDER);
32367
+ }
32368
+ this.reorderInPlace(spPr, SP_PR_ORDER);
32369
+ }
32370
+ /**
32371
+ * Reorder children of the picture-level `p:blipFill` (CT_BlipFillProperties).
32372
+ * Picture elements carry their blip data on the `p:pic` root, not under spPr.
32373
+ */
32374
+ finalizePictureBlipFillOrder(shape) {
32375
+ const pBlipFill = shape["p:blipFill"];
32376
+ if (pBlipFill) {
32377
+ this.reorderInPlace(pBlipFill, BLIP_FILL_ORDER);
32378
+ }
32379
+ }
32380
+ reorderInPlace(target, schemaOrder) {
32381
+ const reordered = reorderObjectKeys(target, schemaOrder);
32382
+ for (const key of Object.keys(target)) {
32383
+ delete target[key];
32384
+ }
32385
+ for (const key of Object.keys(reordered)) {
32386
+ target[key] = reordered[key];
32387
+ }
32388
+ }
30945
32389
  /** Whether an element ID indicates a template (layout/master) element. */
30946
32390
  isTemplateElementId(elementId) {
30947
32391
  return elementId.startsWith("layout-") || elementId.startsWith("master-");
@@ -30965,14 +32409,50 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
30965
32409
  }
30966
32410
  return;
30967
32411
  }
32412
+ if (el.type === "contentPart") {
32413
+ if (shape) {
32414
+ this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
32415
+ collectors.contentParts.push(shape);
32416
+ } else {
32417
+ this.compatibilityService.reportWarning({
32418
+ code: "SAVE_ELEMENT_SKIPPED",
32419
+ message: `Content part '${el.id}' has no rawXml and was skipped during save.`,
32420
+ scope: "save",
32421
+ slideId: ctx.slide.id,
32422
+ elementId: el.id
32423
+ });
32424
+ }
32425
+ return;
32426
+ }
30968
32427
  if (!shape && (el.type === "text" || el.type === "shape")) {
30969
32428
  shape = this.createElementXml(el);
30970
32429
  }
30971
32430
  if (!shape && el.type === "connector") {
30972
32431
  shape = this.createConnectorXml(el);
30973
32432
  }
30974
- if (!shape && el.type === "ink") {
30975
- shape = this.createInkShapeXml(el);
32433
+ if (el.type === "ink") {
32434
+ if (!shape) {
32435
+ shape = this.createInkShapeXml(el);
32436
+ this.compatibilityService.reportWarning({
32437
+ code: "SAVE_INK_ENCODED_AS_CUSTGEOM",
32438
+ message: "SDK-created ink element serialized as custGeom shape; pressure/tool metadata not represented in OOXML aink format.",
32439
+ scope: "save",
32440
+ slideId: ctx.slide.id,
32441
+ elementId: el.id
32442
+ });
32443
+ }
32444
+ }
32445
+ if (!shape && el.type === "table") {
32446
+ shape = this.createTableGraphicFrameXml(el);
32447
+ }
32448
+ if (el.type === "ole") {
32449
+ const oleEl = el;
32450
+ if (shape) {
32451
+ this.applyOleTypedFieldUpdates(shape, oleEl);
32452
+ } else {
32453
+ const embedRid = this.resolveOleEmbedRelationshipId(ctx.slideRelationships, oleEl.oleTarget) || ctx.slideRelationshipRegistry.nextRelationshipId();
32454
+ shape = this.createOleGraphicFrameXml(oleEl, embedRid);
32455
+ }
30976
32456
  }
30977
32457
  if (!shape) {
30978
32458
  this.compatibilityService.reportWarning({
@@ -30986,11 +32466,14 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
30986
32466
  }
30987
32467
  this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
30988
32468
  this.applyImageProperties(shape, el);
32469
+ this.finalizePictureBlipFillOrder(shape);
30989
32470
  this.applyGeometryUpdate(shape, el);
30990
32471
  if (hasShapeProperties(el) && el.shapeStyle && shape["p:spPr"]) {
30991
32472
  const spPr = shape["p:spPr"];
30992
32473
  this.applyFillAndStroke(spPr, el.shapeStyle);
30993
32474
  this.applyEffectsAndThreeD(spPr, el.shapeStyle);
32475
+ this.finalizeSpPrSchemaOrder(shape);
32476
+ this.applyShapeStyleRefs(shape, el.shapeStyle);
30994
32477
  }
30995
32478
  if (hasTextProperties(el)) {
30996
32479
  this.applyTextBodyContent(
@@ -31148,7 +32631,8 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31148
32631
  connectors: [],
31149
32632
  graphicFrames: [],
31150
32633
  groups: [],
31151
- model3ds: []
32634
+ model3ds: [],
32635
+ contentParts: []
31152
32636
  };
31153
32637
  const ctx = {
31154
32638
  slide,
@@ -31180,6 +32664,12 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31180
32664
  } else {
31181
32665
  delete spTree["p16:model3D"];
31182
32666
  }
32667
+ if (collectors.contentParts.length > 0) {
32668
+ spTree["p:contentPart"] = collectors.contentParts;
32669
+ } else {
32670
+ delete spTree["p:contentPart"];
32671
+ }
32672
+ this.reapplyAlternateContentEnvelopes(spTree, collectors);
31183
32673
  const reassigned = shapeIdValidator.validateAndDeduplicateIds(
31184
32674
  spTree,
31185
32675
  (v) => this.ensureArray(v)
@@ -31197,12 +32687,426 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31197
32687
  this.zip.file(slideRelsPath, this.builder.build(slideRelsData));
31198
32688
  this.applySlideDrawingGuides(slideNode, slide);
31199
32689
  this.deduplicateExtensionLists(xmlObj);
32690
+ if (slideContainsA16Element(slideNode)) {
32691
+ ensureA16NamespaceOnSlideRoot(slideNode);
32692
+ }
31200
32693
  this.zip.file(slide.id, this.builder.build(xmlObj));
31201
32694
  }
32695
+ /**
32696
+ * Re-wrap selected children with their original `<mc:AlternateContent>`
32697
+ * envelope (CC-4).
32698
+ *
32699
+ * Parsing merged the selected branch (Choice when supported, otherwise
32700
+ * Fallback) into the spTree's tag arrays. Without re-wrapping, dirty
32701
+ * save would emit flat `<p:sp>`/`<p:pic>` etc. and drop the
32702
+ * `<mc:Fallback>` branch — losing legacy rendering for files originally
32703
+ * authored with newer-namespace features.
32704
+ *
32705
+ * Strategy: for each XmlObject in `collectors.*` that traces back to a
32706
+ * known AC block, group by block and:
32707
+ * 1. Remove the node from its flat collector / spTree array.
32708
+ * 2. Clone the original AC envelope.
32709
+ * 3. Replace the selected branch's `<{tag}>` children with the
32710
+ * live (possibly edited) nodes from the collectors.
32711
+ * 4. Leave the unselected branch verbatim.
32712
+ *
32713
+ * Final envelopes are appended to `spTree['mc:AlternateContent']`.
32714
+ */
32715
+ reapplyAlternateContentEnvelopes(spTree, collectors) {
32716
+ const TAG_TO_COLLECTOR = {
32717
+ "p:sp": collectors.shapes,
32718
+ "p:pic": collectors.pics,
32719
+ "p:cxnSp": collectors.connectors,
32720
+ "p:graphicFrame": collectors.graphicFrames,
32721
+ "p:grpSp": collectors.groups,
32722
+ "p:contentPart": collectors.contentParts,
32723
+ // `model3d` does not flow through SHAPE_TREE_ELEMENT_TAGS, but the
32724
+ // AC pathway in OpenXML decks frequently uses Choice = p16:model3D
32725
+ // + Fallback = p:pic, so map it for completeness.
32726
+ "p16:model3D": collectors.model3ds
32727
+ };
32728
+ const blockGroups = /* @__PURE__ */ new Map();
32729
+ for (const tag of Object.keys(TAG_TO_COLLECTOR)) {
32730
+ const collector = TAG_TO_COLLECTOR[tag];
32731
+ if (!collector) {
32732
+ continue;
32733
+ }
32734
+ for (const node of collector) {
32735
+ const block = this.alternateContentBlockByRawXml.get(node);
32736
+ if (!block) {
32737
+ continue;
32738
+ }
32739
+ let entries = blockGroups.get(block);
32740
+ if (!entries) {
32741
+ entries = [];
32742
+ blockGroups.set(block, entries);
32743
+ }
32744
+ entries.push({ tag, node, collector });
32745
+ }
32746
+ }
32747
+ if (blockGroups.size === 0) {
32748
+ return;
32749
+ }
32750
+ const envelopes = [];
32751
+ for (const [block, entries] of blockGroups) {
32752
+ for (const entry of entries) {
32753
+ const idx = entry.collector.indexOf(entry.node);
32754
+ if (idx !== -1) {
32755
+ entry.collector.splice(idx, 1);
32756
+ }
32757
+ }
32758
+ const clonedAc = { ...block.rawAc };
32759
+ const liveByTag = /* @__PURE__ */ new Map();
32760
+ for (const entry of entries) {
32761
+ let arr = liveByTag.get(entry.tag);
32762
+ if (!arr) {
32763
+ arr = [];
32764
+ liveByTag.set(entry.tag, arr);
32765
+ }
32766
+ arr.push(entry.node);
32767
+ }
32768
+ if (block.selectedBranch === "choice") {
32769
+ const choices = this.ensureArray(clonedAc["mc:Choice"]);
32770
+ const targetIdx = block.choiceIndex ?? 0;
32771
+ const original = choices[targetIdx];
32772
+ if (original) {
32773
+ const rebuilt = { ...original };
32774
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
32775
+ delete rebuilt[tag];
32776
+ }
32777
+ for (const [tag, nodes] of liveByTag) {
32778
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
32779
+ }
32780
+ choices[targetIdx] = rebuilt;
32781
+ clonedAc["mc:Choice"] = choices.length === 1 ? choices[0] : choices;
32782
+ }
32783
+ } else {
32784
+ const fallback = clonedAc["mc:Fallback"];
32785
+ if (fallback) {
32786
+ const rebuilt = { ...fallback };
32787
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
32788
+ delete rebuilt[tag];
32789
+ }
32790
+ for (const [tag, nodes] of liveByTag) {
32791
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
32792
+ }
32793
+ clonedAc["mc:Fallback"] = rebuilt;
32794
+ }
32795
+ }
32796
+ envelopes.push(clonedAc);
32797
+ }
32798
+ spTree["p:sp"] = collectors.shapes;
32799
+ spTree["p:pic"] = collectors.pics;
32800
+ spTree["p:cxnSp"] = collectors.connectors;
32801
+ spTree["p:graphicFrame"] = collectors.graphicFrames;
32802
+ if (collectors.groups.length > 0) {
32803
+ spTree["p:grpSp"] = collectors.groups;
32804
+ } else {
32805
+ delete spTree["p:grpSp"];
32806
+ }
32807
+ if (collectors.contentParts.length > 0) {
32808
+ spTree["p:contentPart"] = collectors.contentParts;
32809
+ } else {
32810
+ delete spTree["p:contentPart"];
32811
+ }
32812
+ if (collectors.model3ds.length > 0) {
32813
+ spTree["p16:model3D"] = collectors.model3ds;
32814
+ } else {
32815
+ delete spTree["p16:model3D"];
32816
+ }
32817
+ spTree["mc:AlternateContent"] = envelopes.length === 1 ? envelopes[0] : envelopes;
32818
+ }
32819
+ };
32820
+
32821
+ // src/core/core/runtime/PptxHandlerRuntimeSaveTheme.ts
32822
+ var PptxHandlerRuntime34 = class extends PptxHandlerRuntime33 {
32823
+ /**
32824
+ * Mark a theme path as dirty so the save pipeline will regenerate
32825
+ * the theme XML from in-memory state. Optional — without this the
32826
+ * original XML is preserved verbatim on save (C-H3).
32827
+ */
32828
+ markThemeDirty(themePath) {
32829
+ this.dirtyThemePaths.add(themePath);
32830
+ }
32831
+ /**
32832
+ * Mark all known theme paths dirty in one call.
32833
+ */
32834
+ markAllThemesDirty() {
32835
+ for (const themePath of this.originalThemeXmlByPath.keys()) {
32836
+ this.dirtyThemePaths.add(themePath);
32837
+ }
32838
+ for (const themePath of this.masterThemePaths.values()) {
32839
+ this.dirtyThemePaths.add(themePath);
32840
+ }
32841
+ }
32842
+ /**
32843
+ * Persist all theme parts during save. Called from the save pipeline
32844
+ * after master / layout XML have been flushed and before
32845
+ * presentation.xml is serialized.
32846
+ *
32847
+ * Order of operations per theme path:
32848
+ *
32849
+ * 1. If the path is *not* in {@link dirtyThemePaths}, the existing
32850
+ * ZIP entry is already correct — no-op. (Original XML was placed
32851
+ * into the ZIP at load time.)
32852
+ * 2. If the path is dirty, build a fresh `<a:theme>` document from
32853
+ * in-memory state and the captured raw subtrees, then overwrite
32854
+ * the ZIP entry.
32855
+ */
32856
+ async persistThemeParts() {
32857
+ const seenThemePaths = /* @__PURE__ */ new Set();
32858
+ for (const [masterPath, themePath] of this.masterThemePaths.entries()) {
32859
+ if (!themePath) {
32860
+ continue;
32861
+ }
32862
+ seenThemePaths.add(themePath);
32863
+ if (!this.dirtyThemePaths.has(themePath)) {
32864
+ continue;
32865
+ }
32866
+ const themeXml2 = this.buildThemeXml(themePath, masterPath);
32867
+ if (themeXml2) {
32868
+ this.zip.file(themePath, themeXml2);
32869
+ }
32870
+ }
32871
+ for (const [themePath] of this.originalThemeXmlByPath.entries()) {
32872
+ if (seenThemePaths.has(themePath)) {
32873
+ continue;
32874
+ }
32875
+ if (!this.dirtyThemePaths.has(themePath)) {
32876
+ continue;
32877
+ }
32878
+ const themeXml2 = this.buildThemeXml(themePath, void 0);
32879
+ if (themeXml2) {
32880
+ this.zip.file(themePath, themeXml2);
32881
+ }
32882
+ }
32883
+ }
32884
+ /**
32885
+ * Build a complete `<a:theme>` XML document from in-memory state.
32886
+ * Returns the serialized XML string (with XML prolog), or `undefined`
32887
+ * if there is no source data to emit.
32888
+ *
32889
+ * - Color scheme: built from per-master color map (or global fallback).
32890
+ * - Font scheme: built from per-master font map + per-script entries.
32891
+ * - Format scheme: re-emit the original XML subtree if available; else
32892
+ * build a minimal scheme from {@link themeFormatScheme}.
32893
+ * - objectDefaults / extraClrSchemeLst / custClrLst / extLst: re-emit
32894
+ * captured raw subtrees.
32895
+ */
32896
+ buildThemeXml(themePath, masterPath) {
32897
+ const colorMap = masterPath && this.masterThemeColorMaps.get(masterPath) || this.globalThemeColorMapSnapshot || this.themeColorMap;
32898
+ const fontMap = masterPath && this.masterThemeFontMaps.get(masterPath) || this.globalThemeFontMapSnapshot || this.themeFontMap;
32899
+ const themeName = this.masterThemeNames.get(themePath) || "Office Theme";
32900
+ const colorSchemeName = this.masterThemeColorSchemeNames.get(themePath) || themeName;
32901
+ const fontSchemeName = this.masterThemeFontSchemeNames.get(themePath) || themeName;
32902
+ const majorScripts = this.masterThemeMajorFontScripts.get(themePath) || {};
32903
+ const minorScripts = this.masterThemeMinorFontScripts.get(themePath) || {};
32904
+ const clrScheme = this.buildClrSchemeObject(colorSchemeName, colorMap);
32905
+ const fontScheme = this.buildFontSchemeObject(
32906
+ fontSchemeName,
32907
+ fontMap,
32908
+ majorScripts,
32909
+ minorScripts
32910
+ );
32911
+ const fmtScheme = this.extractRawSubtreeFromOriginal(themePath, [
32912
+ "a:theme",
32913
+ "a:themeElements",
32914
+ "a:fmtScheme"
32915
+ ]);
32916
+ const themeElements = {
32917
+ "a:clrScheme": clrScheme,
32918
+ "a:fontScheme": fontScheme
32919
+ };
32920
+ if (fmtScheme !== void 0) {
32921
+ themeElements["a:fmtScheme"] = fmtScheme;
32922
+ } else {
32923
+ themeElements["a:fmtScheme"] = this.buildMinimalFmtScheme(themeName);
32924
+ }
32925
+ const themeRoot = {
32926
+ "@_xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
32927
+ "@_name": themeName,
32928
+ "a:themeElements": themeElements
32929
+ };
32930
+ const objectDefaults = this.masterThemeObjectDefaults.get(themePath);
32931
+ if (objectDefaults && (objectDefaults.spDef || objectDefaults.lnDef || objectDefaults.txDef)) {
32932
+ const od = {};
32933
+ if (objectDefaults.spDef !== void 0) {
32934
+ od["a:spDef"] = objectDefaults.spDef;
32935
+ }
32936
+ if (objectDefaults.lnDef !== void 0) {
32937
+ od["a:lnDef"] = objectDefaults.lnDef;
32938
+ }
32939
+ if (objectDefaults.txDef !== void 0) {
32940
+ od["a:txDef"] = objectDefaults.txDef;
32941
+ }
32942
+ themeRoot["a:objectDefaults"] = od;
32943
+ } else {
32944
+ themeRoot["a:objectDefaults"] = {};
32945
+ }
32946
+ const extraClr = this.masterThemeExtraClrSchemeLst.get(themePath);
32947
+ themeRoot["a:extraClrSchemeLst"] = extraClr !== void 0 ? extraClr : {};
32948
+ const custClr = this.masterThemeCustClrLst.get(themePath);
32949
+ if (custClr !== void 0) {
32950
+ themeRoot["a:custClrLst"] = custClr;
32951
+ }
32952
+ const themeExt = this.masterThemeExtLst.get(themePath);
32953
+ if (themeExt !== void 0) {
32954
+ themeRoot["a:extLst"] = themeExt;
32955
+ }
32956
+ const doc = {
32957
+ "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
32958
+ "a:theme": themeRoot
32959
+ };
32960
+ try {
32961
+ return this.builder.build(doc);
32962
+ } catch (error) {
32963
+ console.warn(`Failed to build theme XML for ${themePath}:`, error);
32964
+ return void 0;
32965
+ }
32966
+ }
32967
+ /**
32968
+ * Build the `a:clrScheme` XmlObject from a colour map. Each slot
32969
+ * value is interpreted as either a `#RRGGBB` srgb hex or a known
32970
+ * sysClr token (currently always emitted as srgbClr — the in-memory
32971
+ * map is hex-typed; sysClr round-trip belongs to the broader C-H3
32972
+ * fix to preserve original color XML and is out of scope here).
32973
+ */
32974
+ buildClrSchemeObject(schemeName, colorMap) {
32975
+ const slot = (key) => {
32976
+ const hex = String(colorMap[key] || "").replace(/^#/, "");
32977
+ const srgb = hex.length === 6 ? hex.toUpperCase() : "000000";
32978
+ return { "a:srgbClr": { "@_val": srgb } };
32979
+ };
32980
+ return {
32981
+ "@_name": schemeName,
32982
+ "a:dk1": slot("dk1"),
32983
+ "a:lt1": slot("lt1"),
32984
+ "a:dk2": slot("dk2"),
32985
+ "a:lt2": slot("lt2"),
32986
+ "a:accent1": slot("accent1"),
32987
+ "a:accent2": slot("accent2"),
32988
+ "a:accent3": slot("accent3"),
32989
+ "a:accent4": slot("accent4"),
32990
+ "a:accent5": slot("accent5"),
32991
+ "a:accent6": slot("accent6"),
32992
+ "a:hlink": slot("hlink"),
32993
+ "a:folHlink": slot("folHlink")
32994
+ };
32995
+ }
32996
+ /**
32997
+ * Build the `a:fontScheme` XmlObject from a font map plus per-script
32998
+ * font tables.
32999
+ *
33000
+ * Phase 4 Stream A / M4.
33001
+ */
33002
+ buildFontSchemeObject(schemeName, fontMap, majorScripts, minorScripts) {
33003
+ const buildFontGroup = (latinKey, eaKey, csKey, scripts) => {
33004
+ const group = {
33005
+ "a:latin": { "@_typeface": fontMap[latinKey] || "Calibri" },
33006
+ "a:ea": { "@_typeface": fontMap[eaKey] || "" },
33007
+ "a:cs": { "@_typeface": fontMap[csKey] || "" }
33008
+ };
33009
+ const scriptKeys = Object.keys(scripts);
33010
+ if (scriptKeys.length > 0) {
33011
+ const fontEntries = scriptKeys.map((script) => ({
33012
+ "@_script": script,
33013
+ "@_typeface": scripts[script]
33014
+ }));
33015
+ group["a:font"] = fontEntries.length === 1 ? fontEntries[0] : fontEntries;
33016
+ }
33017
+ return group;
33018
+ };
33019
+ return {
33020
+ "@_name": schemeName,
33021
+ "a:majorFont": buildFontGroup("mj-lt", "mj-ea", "mj-cs", majorScripts),
33022
+ "a:minorFont": buildFontGroup("mn-lt", "mn-ea", "mn-cs", minorScripts)
33023
+ };
33024
+ }
33025
+ /**
33026
+ * Re-parse the original theme XML and pluck out a subtree by path,
33027
+ * returning the raw parser object. Returns `undefined` when the
33028
+ * original is missing or the path doesn't exist.
33029
+ *
33030
+ * Used to preserve `a:fmtScheme` byte-for-byte through a regenerate
33031
+ * round-trip, since the in-memory PptxThemeFormatScheme is lossy.
33032
+ */
33033
+ extractRawSubtreeFromOriginal(themePath, path2) {
33034
+ const original = this.originalThemeXmlByPath.get(themePath);
33035
+ if (!original) {
33036
+ return void 0;
33037
+ }
33038
+ try {
33039
+ const parsed = this.parser.parse(original);
33040
+ let cursor = parsed;
33041
+ for (const segment of path2) {
33042
+ if (cursor && typeof cursor === "object" && segment in cursor) {
33043
+ cursor = cursor[segment];
33044
+ } else {
33045
+ return void 0;
33046
+ }
33047
+ }
33048
+ return cursor;
33049
+ } catch {
33050
+ return void 0;
33051
+ }
33052
+ }
33053
+ /**
33054
+ * Last-resort minimal `<a:fmtScheme>` body. Mirrors the SDK new-deck
33055
+ * builder's output for new presentations, scaled down to the smallest
33056
+ * schema-valid form.
33057
+ */
33058
+ buildMinimalFmtScheme(name) {
33059
+ const phClrSolid = { "a:solidFill": { "a:schemeClr": { "@_val": "phClr" } } };
33060
+ return {
33061
+ "@_name": name,
33062
+ "a:fillStyleLst": {
33063
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
33064
+ "a:gradFill": []
33065
+ },
33066
+ "a:lnStyleLst": {
33067
+ "a:ln": [
33068
+ {
33069
+ "@_w": "6350",
33070
+ "@_cap": "flat",
33071
+ "@_cmpd": "sng",
33072
+ "@_algn": "ctr",
33073
+ ...phClrSolid,
33074
+ "a:prstDash": { "@_val": "solid" },
33075
+ "a:miter": { "@_lim": "800000" }
33076
+ },
33077
+ {
33078
+ "@_w": "12700",
33079
+ "@_cap": "flat",
33080
+ "@_cmpd": "sng",
33081
+ "@_algn": "ctr",
33082
+ ...phClrSolid,
33083
+ "a:prstDash": { "@_val": "solid" },
33084
+ "a:miter": { "@_lim": "800000" }
33085
+ },
33086
+ {
33087
+ "@_w": "19050",
33088
+ "@_cap": "flat",
33089
+ "@_cmpd": "sng",
33090
+ "@_algn": "ctr",
33091
+ ...phClrSolid,
33092
+ "a:prstDash": { "@_val": "solid" },
33093
+ "a:miter": { "@_lim": "800000" }
33094
+ }
33095
+ ]
33096
+ },
33097
+ "a:effectStyleLst": {
33098
+ "a:effectStyle": [{ "a:effectLst": {} }, { "a:effectLst": {} }, { "a:effectLst": {} }]
33099
+ },
33100
+ "a:bgFillStyleLst": {
33101
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
33102
+ "a:gradFill": []
33103
+ }
33104
+ };
33105
+ }
31202
33106
  };
31203
33107
 
31204
33108
  // src/core/core/runtime/PptxHandlerRuntimeSavePipeline.ts
31205
- var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime33 {
33109
+ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
31206
33110
  /**
31207
33111
  * Resolve the effective conformance class for this save operation.
31208
33112
  *
@@ -31289,6 +33193,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31289
33193
  for (const [masterPath, masterXmlObj] of this.masterXmlMap.entries()) {
31290
33194
  this.zip.file(masterPath, this.builder.build(masterXmlObj));
31291
33195
  }
33196
+ await this.persistThemeParts();
31292
33197
  await this.applyEmbeddedFontPreservation(options?.embeddedFonts);
31293
33198
  if (this.presentationData) {
31294
33199
  this.presentationSaveBuilder.applySaveOptions({
@@ -31378,7 +33283,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31378
33283
  };
31379
33284
 
31380
33285
  // src/core/core/runtime/PptxHandlerRuntimeElementParsing.ts
31381
- var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
33286
+ var PptxHandlerRuntime36 = class _PptxHandlerRuntime extends PptxHandlerRuntime35 {
31382
33287
  /**
31383
33288
  * Parse media data (video/audio path and MIME type) from graphic frame data.
31384
33289
  */
@@ -31600,7 +33505,7 @@ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31600
33505
  };
31601
33506
 
31602
33507
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderLookup.ts
31603
- var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
33508
+ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31604
33509
  findPlaceholderInShapeTree(spTree, expected) {
31605
33510
  if (!spTree) {
31606
33511
  return void 0;
@@ -31753,7 +33658,7 @@ var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
31753
33658
  };
31754
33659
 
31755
33660
  // src/core/core/runtime/PptxHandlerRuntimeGeometryParsing.ts
31756
- var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
33661
+ var PptxHandlerRuntime38 = class extends PptxHandlerRuntime37 {
31757
33662
  parseGeometryAdjustments(prstGeom) {
31758
33663
  if (!prstGeom) {
31759
33664
  return void 0;
@@ -31862,6 +33767,103 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31862
33767
  }
31863
33768
  return handles.length > 0 ? handles : void 0;
31864
33769
  }
33770
+ /**
33771
+ * Extract `a:gdLst`/`a:ahLst`/`a:cxnLst`/`a:rect` raw XML from a `a:custGeom`
33772
+ * node so they can be re-emitted on save when the geometry is edited.
33773
+ * Returns `undefined` when none of these auxiliary children carry data.
33774
+ */
33775
+ extractCustomGeometryRawData(custGeom) {
33776
+ if (!custGeom) {
33777
+ return void 0;
33778
+ }
33779
+ const isNonEmpty = (node) => {
33780
+ if (node === void 0 || node === null) {
33781
+ return false;
33782
+ }
33783
+ if (typeof node !== "object") {
33784
+ return true;
33785
+ }
33786
+ return Object.keys(node).length > 0;
33787
+ };
33788
+ const result = {};
33789
+ const gdLst = custGeom["a:gdLst"];
33790
+ if (isNonEmpty(gdLst)) {
33791
+ result.gdLstXml = gdLst;
33792
+ }
33793
+ const ahLst = custGeom["a:ahLst"];
33794
+ if (isNonEmpty(ahLst)) {
33795
+ result.ahLstXml = ahLst;
33796
+ }
33797
+ const cxnLst = custGeom["a:cxnLst"];
33798
+ if (isNonEmpty(cxnLst)) {
33799
+ result.cxnLstXml = cxnLst;
33800
+ }
33801
+ const rect = custGeom["a:rect"];
33802
+ if (isNonEmpty(rect)) {
33803
+ result.rectXml = rect;
33804
+ }
33805
+ return Object.keys(result).length > 0 ? result : void 0;
33806
+ }
33807
+ /**
33808
+ * Build structured `CustomGeometryPath[]` from a parsed `a:custGeom` node,
33809
+ * including per-path `@fill`/`@stroke`/`@extrusionOk` attributes so they
33810
+ * survive a round-trip when the path list is later regenerated.
33811
+ *
33812
+ * Falls back to SVG → structured-path conversion when no structured path
33813
+ * info is otherwise available.
33814
+ */
33815
+ buildStructuredCustomGeometryPaths(custGeom, pathData, pathWidth, pathHeight) {
33816
+ if (!custGeom) {
33817
+ return void 0;
33818
+ }
33819
+ const pathLst = custGeom["a:pathLst"];
33820
+ if (!pathLst) {
33821
+ return void 0;
33822
+ }
33823
+ const pathNodes = this.ensureArray(pathLst["a:path"]);
33824
+ if (pathNodes.length === 0) {
33825
+ return void 0;
33826
+ }
33827
+ const segments = svgToCustomGeometryPaths(pathData, pathWidth, pathHeight);
33828
+ if (segments.length === 0) {
33829
+ return void 0;
33830
+ }
33831
+ const validFillModes = /* @__PURE__ */ new Set([
33832
+ "norm",
33833
+ "lighten",
33834
+ "lightenLess",
33835
+ "darken",
33836
+ "darkenLess",
33837
+ "none"
33838
+ ]);
33839
+ const parseBoolAttr2 = (value) => {
33840
+ if (value === void 0 || value === null || value === "") {
33841
+ return void 0;
33842
+ }
33843
+ if (value === "1" || value === "true" || value === true) {
33844
+ return true;
33845
+ }
33846
+ if (value === "0" || value === "false" || value === false) {
33847
+ return false;
33848
+ }
33849
+ return void 0;
33850
+ };
33851
+ const target = segments[0];
33852
+ const firstNode = pathNodes[0];
33853
+ const fillAttr = String(firstNode["@_fill"] ?? "").trim();
33854
+ if (validFillModes.has(fillAttr)) {
33855
+ target.fillMode = fillAttr;
33856
+ }
33857
+ const strokeAttr = parseBoolAttr2(firstNode["@_stroke"]);
33858
+ if (strokeAttr !== void 0) {
33859
+ target.stroke = strokeAttr;
33860
+ }
33861
+ const extrusionAttr = parseBoolAttr2(firstNode["@_extrusionOk"]);
33862
+ if (extrusionAttr !== void 0) {
33863
+ target.extrusionOk = extrusionAttr;
33864
+ }
33865
+ return segments;
33866
+ }
31865
33867
  parseCustomGeometry(custGeom, shapeWidth, shapeHeight) {
31866
33868
  if (!custGeom || !custGeom["a:pathLst"] || !custGeom["a:pathLst"]?.["a:path"]) {
31867
33869
  return null;
@@ -31930,7 +33932,7 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31930
33932
  };
31931
33933
 
31932
33934
  // src/core/core/runtime/PptxHandlerRuntimeShapeImageFill.ts
31933
- var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime37 {
33935
+ var PptxHandlerRuntime39 = class _PptxHandlerRuntime extends PptxHandlerRuntime38 {
31934
33936
  /**
31935
33937
  * Parse a shape that has an image fill (a:blipFill inside spPr)
31936
33938
  * This handles shapes like rectangles filled with images (e.g., wood texture backgrounds)
@@ -32130,7 +34132,7 @@ var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
32130
34132
  };
32131
34133
 
32132
34134
  // src/core/core/runtime/PptxHandlerRuntimeTextDefaults.ts
32133
- var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
34135
+ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32134
34136
  /**
32135
34137
  * Apply {@link PlaceholderDefaults} body-level properties to a
32136
34138
  * {@link TextStyle} as fallback values (only sets fields that are
@@ -32254,7 +34256,7 @@ var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
32254
34256
  };
32255
34257
 
32256
34258
  // src/core/core/runtime/PptxHandlerRuntimeBulletParsing.ts
32257
- var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34259
+ var PptxHandlerRuntime41 = class extends PptxHandlerRuntime40 {
32258
34260
  resolveParagraphBulletInfo(paragraph, paragraphIndex, txBody, inheritedTxBody, isBodyPlaceholder = false, slidePath) {
32259
34261
  if (!paragraph) {
32260
34262
  return null;
@@ -32285,7 +34287,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32285
34287
  if (candidate["a:buNone"]) {
32286
34288
  return { none: true };
32287
34289
  }
32288
- if (candidate["a:buChar"] || candidate["a:buAutoNum"] || candidate["a:buBlip"]) {
34290
+ 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) {
32289
34291
  resolvedBulletProps = candidate;
32290
34292
  break;
32291
34293
  }
@@ -32299,6 +34301,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32299
34301
  }
32300
34302
  const buFont = resolvedBulletProps["a:buFont"];
32301
34303
  const fontFamily = buFont?.["@_typeface"] ? String(buFont["@_typeface"]) : void 0;
34304
+ const fontInherit = resolvedBulletProps["a:buFontTx"] !== void 0;
32302
34305
  const buSzPct = resolvedBulletProps["a:buSzPct"];
32303
34306
  let sizePercent;
32304
34307
  if (buSzPct?.["@_val"] !== void 0) {
@@ -32315,6 +34318,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32315
34318
  sizePts = ptsRaw / 100;
32316
34319
  }
32317
34320
  }
34321
+ const sizeInherit = resolvedBulletProps["a:buSzTx"] !== void 0;
32318
34322
  const buClr = resolvedBulletProps["a:buClr"];
32319
34323
  let color;
32320
34324
  if (buClr) {
@@ -32323,6 +34327,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32323
34327
  color = String(srgb["@_val"]);
32324
34328
  }
32325
34329
  }
34330
+ const colorInherit = resolvedBulletProps["a:buClrTx"] !== void 0;
32326
34331
  const bulletChar = String(
32327
34332
  resolvedBulletProps["a:buChar"]?.["@_char"] || ""
32328
34333
  );
@@ -32332,7 +34337,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32332
34337
  fontFamily,
32333
34338
  sizePercent,
32334
34339
  sizePts,
32335
- color
34340
+ color,
34341
+ ...fontInherit ? { fontInherit: true } : {},
34342
+ ...colorInherit ? { colorInherit: true } : {},
34343
+ ...sizeInherit ? { sizeInherit: true } : {}
32336
34344
  };
32337
34345
  }
32338
34346
  const autoNum = resolvedBulletProps["a:buAutoNum"];
@@ -32347,7 +34355,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32347
34355
  fontFamily,
32348
34356
  sizePercent,
32349
34357
  sizePts,
32350
- color
34358
+ color,
34359
+ ...fontInherit ? { fontInherit: true } : {},
34360
+ ...colorInherit ? { colorInherit: true } : {},
34361
+ ...sizeInherit ? { sizeInherit: true } : {}
32351
34362
  };
32352
34363
  }
32353
34364
  const buBlip = resolvedBulletProps["a:buBlip"];
@@ -32459,7 +34470,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32459
34470
  };
32460
34471
 
32461
34472
  // src/core/core/runtime/PptxHandlerRuntimeShapeBodyParsing.ts
32462
- var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime40 {
34473
+ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
32463
34474
  /**
32464
34475
  * Parse `a:spLocks` attributes into a structured {@link PptxShapeLocks} object.
32465
34476
  * Returns `undefined` when the node is absent or contains no lock attributes.
@@ -32554,7 +34565,8 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32554
34565
  if (Number.isFinite(spcColRaw) && spcColRaw > 0) {
32555
34566
  textStyle.columnSpacing = spcColRaw / _PptxHandlerRuntime.EMU_PER_PX;
32556
34567
  }
32557
- const hOverflow = String(bodyPr["@_hOverflow"] || "").trim();
34568
+ const hOverflowRaw = bodyPr["@_horzOverflow"] ?? bodyPr["@_hOverflow"];
34569
+ const hOverflow = String(hOverflowRaw || "").trim();
32558
34570
  if (hOverflow === "overflow" || hOverflow === "clip") {
32559
34571
  textStyle.hOverflow = hOverflow;
32560
34572
  }
@@ -32737,7 +34749,7 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32737
34749
  };
32738
34750
 
32739
34751
  // src/core/core/runtime/PptxHandlerRuntimeShapeTextParsing.ts
32740
- var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
34752
+ var PptxHandlerRuntime43 = class _PptxHandlerRuntime extends PptxHandlerRuntime42 {
32741
34753
  /**
32742
34754
  * Resolve paragraph-level styles (alignment, spacing, margins, tabs,
32743
34755
  * level styles) for a single paragraph. Modifies `textStyle` in place
@@ -32919,7 +34931,7 @@ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32919
34931
  };
32920
34932
 
32921
34933
  // src/core/core/runtime/PptxHandlerRuntimeShapeParagraphContentParsing.ts
32922
- var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
34934
+ var PptxHandlerRuntime44 = class extends PptxHandlerRuntime43 {
32923
34935
  /**
32924
34936
  * Collect text content (runs, fields, equations, bullets) for a single
32925
34937
  * paragraph and return text parts + segments. The returned `seedStyle`
@@ -33102,6 +35114,23 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33102
35114
  parts.push("\n");
33103
35115
  segments.push({ text: "\n", style: { ...mergedDefaultRunStyle } });
33104
35116
  }
35117
+ const firstSegmentIndex = segments.length === 0 ? -1 : 0;
35118
+ if (firstSegmentIndex >= 0) {
35119
+ const pPrRaw = p["a:pPr"];
35120
+ const lvlRaw = pPrRaw?.["@_lvl"];
35121
+ if (lvlRaw !== void 0) {
35122
+ const lvlParsed = Number.parseInt(String(lvlRaw), 10);
35123
+ if (Number.isFinite(lvlParsed) && lvlParsed > 0) {
35124
+ segments[firstSegmentIndex].paragraphLevel = Math.min(Math.max(lvlParsed, 0), 8);
35125
+ }
35126
+ }
35127
+ const endParaRPrRaw = p["a:endParaRPr"];
35128
+ if (endParaRPrRaw && typeof endParaRPrRaw === "object") {
35129
+ segments[firstSegmentIndex].endParaRunProperties = {
35130
+ ...endParaRPrRaw
35131
+ };
35132
+ }
35133
+ }
33105
35134
  return { parts, segments, seedStyle };
33106
35135
  }
33107
35136
  /**
@@ -33211,7 +35240,7 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33211
35240
  };
33212
35241
 
33213
35242
  // src/core/core/runtime/PptxHandlerRuntimeShapeParsing.ts
33214
- var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime43 {
35243
+ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
33215
35244
  parseShape(shape, id, slidePath) {
33216
35245
  try {
33217
35246
  const spPr = shape["p:spPr"];
@@ -33247,6 +35276,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33247
35276
  let pathData;
33248
35277
  let pathWidth;
33249
35278
  let pathHeight;
35279
+ let customGeometryRawData;
33250
35280
  const custGeom = effectiveSpPr?.["a:custGeom"];
33251
35281
  if (custGeom) {
33252
35282
  const customPath = this.parseCustomGeometry(
@@ -33259,6 +35289,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33259
35289
  pathData = customPath.pathData;
33260
35290
  pathWidth = customPath.pathWidth;
33261
35291
  pathHeight = customPath.pathHeight;
35292
+ customGeometryRawData = this.extractCustomGeometryRawData(custGeom);
33262
35293
  }
33263
35294
  }
33264
35295
  const geomNode = custGeom ?? effectiveSpPr?.["a:prstGeom"];
@@ -33416,7 +35447,8 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33416
35447
  type: "shape",
33417
35448
  pathData,
33418
35449
  pathWidth,
33419
- pathHeight
35450
+ pathHeight,
35451
+ customGeometryRawData
33420
35452
  };
33421
35453
  } catch (e) {
33422
35454
  console.warn(`[pptx] Skipping shape element (${id}):`, e);
@@ -33426,7 +35458,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33426
35458
  };
33427
35459
 
33428
35460
  // src/core/core/runtime/PptxHandlerRuntimePictureParsing.ts
33429
- var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
35461
+ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
33430
35462
  async parsePicture(pic, id, slidePath) {
33431
35463
  try {
33432
35464
  const spPr = pic["p:spPr"];
@@ -33665,7 +35697,7 @@ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33665
35697
  };
33666
35698
 
33667
35699
  // src/core/core/runtime/PptxHandlerRuntimeSpTreeParsing.ts
33668
- var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
35700
+ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
33669
35701
  /**
33670
35702
  * Known element tag names that appear as direct children of `p:spTree`
33671
35703
  * (or `p:grpSp`) and represent renderable shapes/objects.
@@ -34010,9 +36042,18 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34010
36042
  * Unwrap mc:AlternateContent elements within a shape tree (or group),
34011
36043
  * merging selected branch children into the parent element arrays.
34012
36044
  * Delegates to the standalone alternate-content utility.
36045
+ *
36046
+ * Records each consumed AC envelope in {@link alternateContentBlockByRawXml}
36047
+ * so the save layer can re-emit the original `<mc:Choice>` /
36048
+ * `<mc:Fallback>` shape on dirty save (CC-4).
34013
36049
  */
34014
36050
  unwrapAlternateContent(container) {
34015
- unwrapAlternateContent(container);
36051
+ const blocks = unwrapAlternateContent(container);
36052
+ for (const block of blocks) {
36053
+ for (const ref of block.childRefs) {
36054
+ this.alternateContentBlockByRawXml.set(ref.node, block);
36055
+ }
36056
+ }
34016
36057
  }
34017
36058
  /**
34018
36059
  * Forward declaration – implemented in PptxHandlerRuntimeGroupParsing.
@@ -34023,7 +36064,7 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34023
36064
  };
34024
36065
 
34025
36066
  // src/core/core/runtime/PptxHandlerRuntimeGroupParsing.ts
34026
- var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
36067
+ var PptxHandlerRuntime48 = class _PptxHandlerRuntime extends PptxHandlerRuntime47 {
34027
36068
  async parseGroupShape(group, baseId, slidePath, rawXmlStr) {
34028
36069
  const grpSpPr = group["p:grpSpPr"];
34029
36070
  const xfrm = grpSpPr?.["a:xfrm"];
@@ -34196,7 +36237,7 @@ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34196
36237
  };
34197
36238
 
34198
36239
  // src/core/core/runtime/PptxHandlerRuntimeSlideParsing.ts
34199
- var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
36240
+ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34200
36241
  /**
34201
36242
  * Parse text body from a connector shape (p:cxnSp > p:txBody).
34202
36243
  * Uses the same text run extraction logic as regular shapes but
@@ -34308,7 +36349,7 @@ var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
34308
36349
  };
34309
36350
 
34310
36351
  // src/core/core/runtime/PptxHandlerRuntimeColorAndEffects.ts
34311
- var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
36352
+ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34312
36353
  /**
34313
36354
  * Forward declaration – implemented in PptxHandlerRuntimeThemeProcessing.
34314
36355
  * Re-resolves gradient stops by substituting `phClr` with the given colour.
@@ -34349,8 +36390,9 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34349
36390
  return void 0;
34350
36391
  }
34351
36392
  const resolvedKey = normalized === "phclr" ? "accent1" : normalized;
34352
- if (this.currentSlideClrMapOverride) {
34353
- const remapped = this.currentSlideClrMapOverride[resolvedKey];
36393
+ const overrideMap = this.currentSlideClrMapOverride ?? this.currentMasterClrMap;
36394
+ if (overrideMap) {
36395
+ const remapped = overrideMap[resolvedKey];
34354
36396
  if (remapped) {
34355
36397
  return this.themeColorMap[remapped] || this.getDefaultSchemeColorMap()[remapped];
34356
36398
  }
@@ -34433,7 +36475,7 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34433
36475
  };
34434
36476
 
34435
36477
  // src/core/core/runtime/PptxHandlerRuntimeBackgroundParsing.ts
34436
- var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
36478
+ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34437
36479
  async extractBackgroundImage(slideXml2, slidePath, rootElement = "p:sld") {
34438
36480
  try {
34439
36481
  const bg = slideXml2[rootElement]?.["p:cSld"]?.["p:bg"];
@@ -34644,7 +36686,7 @@ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34644
36686
  };
34645
36687
 
34646
36688
  // src/core/core/runtime/PptxHandlerRuntimeSlideUtils.ts
34647
- var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
36689
+ var PptxHandlerRuntime52 = class extends PptxHandlerRuntime51 {
34648
36690
  /**
34649
36691
  * Retrieve the background gradient from a layout, falling back to master.
34650
36692
  */
@@ -34715,6 +36757,64 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34715
36757
  }
34716
36758
  return void 0;
34717
36759
  }
36760
+ /**
36761
+ * Find the master file path referenced by a layout via its relationships.
36762
+ */
36763
+ findMasterPathForLayoutBase(layoutPath) {
36764
+ const layoutRels = this.slideRelsMap.get(layoutPath);
36765
+ if (!layoutRels) {
36766
+ return void 0;
36767
+ }
36768
+ for (const [, target] of layoutRels.entries()) {
36769
+ if (target.includes("slideMaster")) {
36770
+ const layoutDir = layoutPath.substring(0, layoutPath.lastIndexOf("/") + 1);
36771
+ return target.startsWith("..") ? this.resolvePath(layoutDir, target) : `ppt/${target.replace("../", "")}`;
36772
+ }
36773
+ }
36774
+ return void 0;
36775
+ }
36776
+ /**
36777
+ * Switch the active master state (clrMap + theme color/font/format
36778
+ * scheme) so that scheme-colour resolution for the slide currently
36779
+ * being parsed walks through the correct master.
36780
+ *
36781
+ * Multi-master decks must resolve scheme colours against each slide's
36782
+ * own master rather than always against `masterFiles[0]`.
36783
+ *
36784
+ * Phase 2 Stream B / C-H4.
36785
+ */
36786
+ async setActiveMasterForSlide(slidePath) {
36787
+ const layoutPath = this.findLayoutPathForSlide(slidePath);
36788
+ if (!layoutPath) {
36789
+ this.currentMasterClrMap = null;
36790
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
36791
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
36792
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
36793
+ return;
36794
+ }
36795
+ if (!this.slideRelsMap.has(layoutPath)) {
36796
+ const layoutRelsPath = `${layoutPath.replace("slideLayouts/", "slideLayouts/_rels/")}.rels`;
36797
+ try {
36798
+ await this.loadSlideRelationships(layoutPath, layoutRelsPath);
36799
+ } catch {
36800
+ }
36801
+ }
36802
+ const masterPath = this.findMasterPathForLayoutBase(layoutPath);
36803
+ if (!masterPath) {
36804
+ this.currentMasterClrMap = null;
36805
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
36806
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
36807
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
36808
+ return;
36809
+ }
36810
+ this.currentMasterClrMap = this.masterClrMaps.get(masterPath) ?? null;
36811
+ const masterColorMap = this.masterThemeColorMaps.get(masterPath);
36812
+ this.themeColorMap = masterColorMap ? { ...masterColorMap } : { ...this.globalThemeColorMapSnapshot };
36813
+ const masterFontMap = this.masterThemeFontMaps.get(masterPath);
36814
+ this.themeFontMap = masterFontMap ? { ...masterFontMap } : { ...this.globalThemeFontMapSnapshot };
36815
+ const masterFormatScheme = this.masterThemeFormatSchemes.get(masterPath);
36816
+ this.themeFormatScheme = masterFormatScheme ?? this.globalThemeFormatSchemeSnapshot;
36817
+ }
34718
36818
  /**
34719
36819
  * Extract the `p:bg/@showAnimation` flag from a slide's XML.
34720
36820
  * Returns `true` when the background should animate, `false` when
@@ -34855,7 +36955,7 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34855
36955
  };
34856
36956
 
34857
36957
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderStyles.ts
34858
- var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime51 {
36958
+ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
34859
36959
  /**
34860
36960
  * Parse a single `a:lvlXpPr` node into a structured
34861
36961
  * {@link PlaceholderTextLevelStyle}.
@@ -34976,7 +37076,7 @@ var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
34976
37076
  };
34977
37077
 
34978
37078
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderDefaults.ts
34979
- var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
37079
+ var PptxHandlerRuntime54 = class _PptxHandlerRuntime extends PptxHandlerRuntime53 {
34980
37080
  /**
34981
37081
  * Extract structured {@link PlaceholderDefaults} from a layout or master
34982
37082
  * shape that carries a `p:ph` element.
@@ -35119,7 +37219,118 @@ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35119
37219
  };
35120
37220
 
35121
37221
  // src/core/core/runtime/PptxHandlerRuntimeMasterElements.ts
35122
- var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
37222
+ function parseHeaderFooterFlags(hf) {
37223
+ if (!hf) {
37224
+ return void 0;
37225
+ }
37226
+ const result = {};
37227
+ if (hf["@_hdr"] !== void 0) {
37228
+ result.hasHeader = String(hf["@_hdr"]) !== "0";
37229
+ }
37230
+ if (hf["@_ftr"] !== void 0) {
37231
+ result.hasFooter = String(hf["@_ftr"]) !== "0";
37232
+ }
37233
+ if (hf["@_dt"] !== void 0) {
37234
+ result.hasDateTime = String(hf["@_dt"]) !== "0";
37235
+ }
37236
+ if (hf["@_sldNum"] !== void 0) {
37237
+ result.hasSlideNumber = String(hf["@_sldNum"]) !== "0";
37238
+ }
37239
+ return Object.keys(result).length > 0 ? result : void 0;
37240
+ }
37241
+ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37242
+ /**
37243
+ * Parse a `CT_TextListStyle` node (`a:defPPr` + `a:lvl1pPr` … `a:lvl9pPr`)
37244
+ * into a level-keyed style map. Used for `<p:txStyles>` children
37245
+ * (`p:titleStyle`, `p:bodyStyle`, `p:otherStyle`) — see ECMA-376 §19.3.1.52.
37246
+ */
37247
+ parseTextListStyle(node) {
37248
+ if (!node) {
37249
+ return void 0;
37250
+ }
37251
+ const levels = {};
37252
+ const defParsed = this.parsePlaceholderLevelStyle(node["a:defPPr"]);
37253
+ if (defParsed) {
37254
+ levels[-1] = defParsed;
37255
+ }
37256
+ for (let lvl = 1; lvl <= 9; lvl++) {
37257
+ const parsed = this.parsePlaceholderLevelStyle(
37258
+ node[`a:lvl${lvl}pPr`]
37259
+ );
37260
+ if (parsed) {
37261
+ levels[lvl - 1] = parsed;
37262
+ }
37263
+ }
37264
+ return Object.keys(levels).length > 0 ? levels : void 0;
37265
+ }
37266
+ /**
37267
+ * Parse `<p:txStyles>` from a slide-master XML object into a structured
37268
+ * {@link PptxMasterTextStyles}. Used to populate `PptxSlideMaster.txStyles`
37269
+ * so the title/body/other text-style cascade (P-H1) is visible on the
37270
+ * typed model.
37271
+ */
37272
+ parseMasterTxStyles(masterXml) {
37273
+ const txStyles = masterXml?.["p:txStyles"];
37274
+ if (!txStyles) {
37275
+ return void 0;
37276
+ }
37277
+ const titleStyle = this.parseTextListStyle(txStyles["p:titleStyle"]);
37278
+ const bodyStyle = this.parseTextListStyle(txStyles["p:bodyStyle"]);
37279
+ const otherStyle = this.parseTextListStyle(txStyles["p:otherStyle"]);
37280
+ if (!titleStyle && !bodyStyle && !otherStyle) {
37281
+ return void 0;
37282
+ }
37283
+ const result = {};
37284
+ if (titleStyle) {
37285
+ result.titleStyle = titleStyle;
37286
+ }
37287
+ if (bodyStyle) {
37288
+ result.bodyStyle = bodyStyle;
37289
+ }
37290
+ if (otherStyle) {
37291
+ result.otherStyle = otherStyle;
37292
+ }
37293
+ return result;
37294
+ }
37295
+ /**
37296
+ * Enrich an array of {@link PptxSlideMaster} entries (already produced by
37297
+ * `parseSlideMasters`) with parsed `<p:txStyles>`. Loads each master's XML
37298
+ * once, parses, and caches it in `masterXmlMap` for downstream consumers.
37299
+ *
37300
+ * Also stores the parsed result on the per-master cache so that the
37301
+ * inheritance chain in `applyMasterTextStyleCascade` can find it without
37302
+ * re-parsing.
37303
+ */
37304
+ async enrichSlideMastersWithTxStyles(slideMasters) {
37305
+ for (const master of slideMasters) {
37306
+ try {
37307
+ let masterXmlObj = this.masterXmlMap.get(master.path);
37308
+ if (!masterXmlObj) {
37309
+ const xmlStr = await this.zip.file(master.path)?.async("string");
37310
+ if (!xmlStr) {
37311
+ continue;
37312
+ }
37313
+ masterXmlObj = this.parser.parse(xmlStr);
37314
+ this.masterXmlMap.set(master.path, masterXmlObj);
37315
+ }
37316
+ const sldMaster = masterXmlObj["p:sldMaster"];
37317
+ if (!sldMaster) {
37318
+ continue;
37319
+ }
37320
+ const parsed = this.parseMasterTxStyles(sldMaster);
37321
+ if (parsed) {
37322
+ master.txStyles = parsed;
37323
+ this.masterTxStylesCache.set(master.path, parsed);
37324
+ }
37325
+ const hf = parseHeaderFooterFlags(sldMaster["p:hf"]);
37326
+ if (hf) {
37327
+ master.headerFooter = hf;
37328
+ }
37329
+ } catch (e) {
37330
+ console.warn("Failed to parse master txStyles:", e);
37331
+ }
37332
+ }
37333
+ }
35123
37334
  parsePresentationDefaultTextStyle() {
35124
37335
  const presentation = this.presentationData?.["p:presentation"];
35125
37336
  const defaultTextStyle = presentation?.["p:defaultTextStyle"];
@@ -35275,7 +37486,7 @@ var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
35275
37486
  };
35276
37487
 
35277
37488
  // src/core/core/runtime/PptxHandlerRuntimeLayoutElements.ts
35278
- var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37489
+ var PptxHandlerRuntime56 = class extends PptxHandlerRuntime55 {
35279
37490
  async getLayoutElements(slidePath) {
35280
37491
  const slideRels = this.slideRelsMap.get(slidePath);
35281
37492
  if (!slideRels) {
@@ -35399,10 +37610,10 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35399
37610
  }
35400
37611
  }
35401
37612
  }
35402
- this.currentSlideClrMapOverride = prevClrMapOverride;
35403
37613
  const layoutShowMasterSp = layoutXmlObj["p:sldLayout"]?.["@_showMasterSp"];
35404
37614
  const showMasterSp = layoutShowMasterSp === void 0 || String(layoutShowMasterSp).trim().toLowerCase() !== "0" && String(layoutShowMasterSp).trim().toLowerCase() !== "false";
35405
37615
  const masterElements = showMasterSp ? await this.getMasterElements(layoutPath) : [];
37616
+ this.currentSlideClrMapOverride = prevClrMapOverride;
35406
37617
  const allElements = [...masterElements, ...elements];
35407
37618
  this.layoutCache.set(layoutPath, allElements);
35408
37619
  return allElements;
@@ -35414,7 +37625,7 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35414
37625
  };
35415
37626
 
35416
37627
  // src/core/core/runtime/PptxHandlerRuntimeThemeFormatScheme.ts
35417
- var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime55 {
37628
+ var PptxHandlerRuntime57 = class _PptxHandlerRuntime extends PptxHandlerRuntime56 {
35418
37629
  /**
35419
37630
  * Collect fill-style children from a style list node, preserving
35420
37631
  * document order. Handles `a:solidFill`, `a:gradFill`, `a:pattFill`,
@@ -35656,7 +37867,7 @@ var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35656
37867
  };
35657
37868
 
35658
37869
  // src/core/core/runtime/PptxHandlerRuntimeThemeOverrides.ts
35659
- var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
37870
+ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
35660
37871
  /**
35661
37872
  * Parse the `a:fmtScheme` element from the theme into a structured
35662
37873
  * {@link PptxThemeFormatScheme}. Each sub-list (fillStyleLst, lnStyleLst,
@@ -35853,8 +38064,10 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
35853
38064
  }
35854
38065
  const fontScheme = root["a:fontScheme"];
35855
38066
  if (fontScheme) {
35856
- const majorLatin = fontScheme["a:majorFont"]?.["a:latin"];
35857
- const minorLatin = fontScheme["a:minorFont"]?.["a:latin"];
38067
+ const majorFontNode = fontScheme["a:majorFont"];
38068
+ const minorFontNode = fontScheme["a:minorFont"];
38069
+ const majorLatin = majorFontNode?.["a:latin"];
38070
+ const minorLatin = minorFontNode?.["a:latin"];
35858
38071
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
35859
38072
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
35860
38073
  if (!result.colorOverrides) {
@@ -35877,13 +38090,46 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
35877
38090
  };
35878
38091
 
35879
38092
  // src/core/core/runtime/PptxHandlerRuntimeThemeRefResolution.ts
35880
- var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
38093
+ var COLOR_CHOICE_KEYS2 = [
38094
+ "a:scrgbClr",
38095
+ "a:srgbClr",
38096
+ "a:hslClr",
38097
+ "a:sysClr",
38098
+ "a:schemeClr",
38099
+ "a:prstClr"
38100
+ ];
38101
+ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38102
+ /**
38103
+ * Pull the verbatim colour-choice child out of a style-matrix-reference
38104
+ * element (`a:lnRef`/`a:fillRef`/`a:effectRef`/`a:fontRef`). Returns the
38105
+ * full child object so it can be round-tripped at save time, preserving
38106
+ * any contained colour transforms (`a:lumMod`, `a:tint`, etc.).
38107
+ */
38108
+ extractRefColorXml(refNode) {
38109
+ if (!refNode) {
38110
+ return void 0;
38111
+ }
38112
+ for (const key of COLOR_CHOICE_KEYS2) {
38113
+ const child = refNode[key];
38114
+ if (child !== void 0) {
38115
+ return { [key]: child };
38116
+ }
38117
+ }
38118
+ return void 0;
38119
+ }
35881
38120
  /**
35882
38121
  * Resolve a `a:effectRef` element into concrete effect properties
35883
38122
  * by looking up `@_idx` in the theme format scheme's effect style list.
35884
38123
  */
35885
38124
  resolveThemeEffectRef(refNode, style) {
35886
38125
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
38126
+ if (Number.isFinite(idx) && idx > 0) {
38127
+ style.effectRefIdx = idx;
38128
+ }
38129
+ const overrideColorXml = this.extractRefColorXml(refNode);
38130
+ if (overrideColorXml) {
38131
+ style.effectRefColorXml = overrideColorXml;
38132
+ }
35887
38133
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.effectStyles.length) {
35888
38134
  return;
35889
38135
  }
@@ -35936,6 +38182,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
35936
38182
  resolveThemeLineRef(refNode, style) {
35937
38183
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
35938
38184
  const overrideColor = this.parseColor(refNode);
38185
+ if (Number.isFinite(idx) && idx > 0) {
38186
+ style.lnRefIdx = idx;
38187
+ }
38188
+ const overrideColorXml = this.extractRefColorXml(refNode);
38189
+ if (overrideColorXml) {
38190
+ style.lnRefColorXml = overrideColorXml;
38191
+ }
35939
38192
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.lineStyles.length) {
35940
38193
  style.strokeColor = overrideColor;
35941
38194
  if (overrideColor) {
@@ -36007,6 +38260,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36007
38260
  */
36008
38261
  resolveThemeFillRef(refNode, style) {
36009
38262
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
38263
+ if (Number.isFinite(idx) && idx > 0) {
38264
+ style.fillRefIdx = idx;
38265
+ }
38266
+ const overrideColorXml = this.extractRefColorXml(refNode);
38267
+ if (overrideColorXml) {
38268
+ style.fillRefColorXml = overrideColorXml;
38269
+ }
36010
38270
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme) {
36011
38271
  style.fillMode = "theme";
36012
38272
  style.fillColor = this.parseColor(refNode);
@@ -36072,7 +38332,7 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36072
38332
  };
36073
38333
 
36074
38334
  // src/core/core/runtime/PptxHandlerRuntimeThemeLoading.ts
36075
- var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38335
+ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36076
38336
  async resolvePrimaryThemePath() {
36077
38337
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36078
38338
  if (!masterFiles || masterFiles.length === 0) {
@@ -36182,62 +38442,97 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36182
38442
  formatScheme: this.themeFormatScheme
36183
38443
  };
36184
38444
  }
36185
- async applySlideMasterColorMap(defaultMap) {
38445
+ /**
38446
+ * Parse every slide master's `<p:clrMap>` element and store the alias
38447
+ * dictionaries on {@link masterClrMaps}. Do *not* mutate
38448
+ * {@link themeColorMap} — alias resolution happens at colour-lookup
38449
+ * time so that:
38450
+ *
38451
+ * 1. The raw theme scheme stays the source of truth (clrMap is a
38452
+ * routing layer, not a colour table).
38453
+ * 2. Multi-master decks resolve each slide against its own master's
38454
+ * clrMap rather than always against `masterFiles[0]`.
38455
+ * 3. Layout `clrMapOvr` semantics work correctly when a slide's master
38456
+ * differs from the deck's first master.
38457
+ *
38458
+ * Phase 2 Stream B / C-H4.
38459
+ */
38460
+ async applySlideMasterColorMap(_defaultMap) {
36186
38461
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36187
38462
  if (!masterFiles || masterFiles.length === 0) {
36188
38463
  return;
36189
38464
  }
36190
- try {
36191
- const masterXml = await masterFiles[0].async("string");
36192
- const masterData = this.parser.parse(masterXml);
36193
- const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
36194
- if (!clrMap) {
36195
- return;
36196
- }
36197
- const aliasKeys = [
36198
- "bg1",
36199
- "tx1",
36200
- "bg2",
36201
- "tx2",
36202
- "accent1",
36203
- "accent2",
36204
- "accent3",
36205
- "accent4",
36206
- "accent5",
36207
- "accent6",
36208
- "hlink",
36209
- "folHlink"
36210
- ];
36211
- for (const aliasKey of aliasKeys) {
36212
- const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
36213
- if (!mappedKey) {
38465
+ const aliasKeys = [
38466
+ "bg1",
38467
+ "tx1",
38468
+ "bg2",
38469
+ "tx2",
38470
+ "accent1",
38471
+ "accent2",
38472
+ "accent3",
38473
+ "accent4",
38474
+ "accent5",
38475
+ "accent6",
38476
+ "hlink",
38477
+ "folHlink"
38478
+ ];
38479
+ for (const file of masterFiles) {
38480
+ try {
38481
+ const masterXml = await file.async("string");
38482
+ const masterData = this.parser.parse(masterXml);
38483
+ const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
38484
+ if (!clrMap) {
36214
38485
  continue;
36215
38486
  }
36216
- const mappedColor = this.themeColorMap[mappedKey] || defaultMap[mappedKey];
36217
- if (mappedColor) {
36218
- this.themeColorMap[aliasKey] = mappedColor;
38487
+ const aliasMap = {};
38488
+ for (const aliasKey of aliasKeys) {
38489
+ const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
38490
+ if (mappedKey) {
38491
+ aliasMap[aliasKey] = mappedKey;
38492
+ }
38493
+ }
38494
+ if (Object.keys(aliasMap).length > 0) {
38495
+ this.masterClrMaps.set(file.name, aliasMap);
36219
38496
  }
38497
+ } catch (error) {
38498
+ console.warn(`Failed to parse slide master color map for ${file.name}:`, error);
36220
38499
  }
36221
- } catch (error) {
36222
- console.warn("Failed to parse slide master color map:", error);
36223
38500
  }
36224
38501
  }
36225
- async loadThemeData() {
36226
- const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
36227
- if (!themeFiles || themeFiles.length === 0) {
36228
- return;
38502
+ /**
38503
+ * Parse a single theme part into structured colour, font, and format
38504
+ * scheme dictionaries. Used both for the global default theme and for
38505
+ * each master's per-master theme (multi-master support).
38506
+ *
38507
+ * Phase 2 Stream B / C-H4.
38508
+ */
38509
+ async parseThemePart(themePath) {
38510
+ const themeFile = this.zip.file(themePath);
38511
+ if (!themeFile) {
38512
+ return null;
36229
38513
  }
36230
- const preferredThemePath = await this.resolvePrimaryThemePath();
36231
- const preferredThemeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) : void 0;
36232
- const themeFile = preferredThemeFile ?? themeFiles[0];
36233
38514
  const themeXml2 = await themeFile.async("string");
38515
+ this.originalThemeXmlByPath.set(themePath, themeXml2);
36234
38516
  const themeData = this.parser.parse(themeXml2);
36235
38517
  const themeRoot = themeData["a:theme"];
36236
38518
  const themeElements = themeRoot?.["a:themeElements"];
36237
38519
  const colorScheme = themeElements?.["a:clrScheme"];
36238
38520
  const fontScheme = themeElements?.["a:fontScheme"];
38521
+ const fmtScheme = themeElements?.["a:fmtScheme"];
38522
+ const themeName = String(themeRoot?.["@_name"] || "").trim();
38523
+ if (themeName) {
38524
+ this.masterThemeNames.set(themePath, themeName);
38525
+ }
38526
+ const colorSchemeName = String(colorScheme?.["@_name"] || "").trim();
38527
+ if (colorSchemeName) {
38528
+ this.masterThemeColorSchemeNames.set(themePath, colorSchemeName);
38529
+ }
38530
+ const fontSchemeName = String(fontScheme?.["@_name"] || "").trim();
38531
+ if (fontSchemeName) {
38532
+ this.masterThemeFontSchemeNames.set(themePath, fontSchemeName);
38533
+ }
36239
38534
  const defaultMap = this.getDefaultSchemeColorMap();
36240
- this.themeColorMap = { ...defaultMap };
38535
+ const colorMap = { ...defaultMap };
36241
38536
  if (colorScheme) {
36242
38537
  const schemeKeys = [
36243
38538
  "dk1",
@@ -36257,39 +38552,196 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36257
38552
  const colorNode = colorScheme[`a:${key}`];
36258
38553
  const parsed = this.parseColorChoice(colorNode);
36259
38554
  if (parsed) {
36260
- this.themeColorMap[key] = parsed;
38555
+ colorMap[key] = parsed;
36261
38556
  }
36262
38557
  }
36263
38558
  }
36264
- this.themeColorMap["tx1"] = this.themeColorMap["dk1"] || defaultMap["dk1"];
36265
- this.themeColorMap["bg1"] = this.themeColorMap["lt1"] || defaultMap["lt1"];
36266
- this.themeColorMap["tx2"] = this.themeColorMap["dk2"] || defaultMap["dk2"];
36267
- this.themeColorMap["bg2"] = this.themeColorMap["lt2"] || defaultMap["lt2"];
36268
- await this.applySlideMasterColorMap(defaultMap);
36269
- const majorLatin = fontScheme?.["a:majorFont"]?.["a:latin"];
36270
- const minorLatin = fontScheme?.["a:minorFont"]?.["a:latin"];
38559
+ colorMap["tx1"] = colorMap["dk1"] || defaultMap["dk1"];
38560
+ colorMap["bg1"] = colorMap["lt1"] || defaultMap["lt1"];
38561
+ colorMap["tx2"] = colorMap["dk2"] || defaultMap["dk2"];
38562
+ colorMap["bg2"] = colorMap["lt2"] || defaultMap["lt2"];
38563
+ const majorFontNode = fontScheme?.["a:majorFont"];
38564
+ const minorFontNode = fontScheme?.["a:minorFont"];
38565
+ const majorLatin = majorFontNode?.["a:latin"];
38566
+ const minorLatin = minorFontNode?.["a:latin"];
36271
38567
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
36272
38568
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
36273
- this.themeFontMap = {};
38569
+ const fontMap = {};
36274
38570
  if (majorFont) {
36275
- this.themeFontMap["mj-lt"] = majorFont;
36276
- this.themeFontMap["mj-ea"] = majorFont;
36277
- this.themeFontMap["mj-cs"] = majorFont;
38571
+ fontMap["mj-lt"] = majorFont;
38572
+ fontMap["mj-ea"] = majorFont;
38573
+ fontMap["mj-cs"] = majorFont;
36278
38574
  }
36279
38575
  if (minorFont) {
36280
- this.themeFontMap["mn-lt"] = minorFont;
36281
- this.themeFontMap["mn-ea"] = minorFont;
36282
- this.themeFontMap["mn-cs"] = minorFont;
38576
+ fontMap["mn-lt"] = minorFont;
38577
+ fontMap["mn-ea"] = minorFont;
38578
+ fontMap["mn-cs"] = minorFont;
36283
38579
  }
36284
- const fmtScheme = themeElements?.["a:fmtScheme"];
36285
- if (fmtScheme) {
36286
- this.themeFormatScheme = this.parseFormatScheme(fmtScheme);
38580
+ const majorEa = this.normalizeTypefaceToken(
38581
+ String(majorFontNode?.["a:ea"]?.["@_typeface"] || "")
38582
+ );
38583
+ if (majorEa) {
38584
+ fontMap["mj-ea"] = majorEa;
38585
+ }
38586
+ const majorCs = this.normalizeTypefaceToken(
38587
+ String(majorFontNode?.["a:cs"]?.["@_typeface"] || "")
38588
+ );
38589
+ if (majorCs) {
38590
+ fontMap["mj-cs"] = majorCs;
38591
+ }
38592
+ const minorEa = this.normalizeTypefaceToken(
38593
+ String(minorFontNode?.["a:ea"]?.["@_typeface"] || "")
38594
+ );
38595
+ if (minorEa) {
38596
+ fontMap["mn-ea"] = minorEa;
38597
+ }
38598
+ const minorCs = this.normalizeTypefaceToken(
38599
+ String(minorFontNode?.["a:cs"]?.["@_typeface"] || "")
38600
+ );
38601
+ if (minorCs) {
38602
+ fontMap["mn-cs"] = minorCs;
38603
+ }
38604
+ const majorScripts = this.collectFontScriptOverrides(majorFontNode);
38605
+ if (Object.keys(majorScripts).length > 0) {
38606
+ this.masterThemeMajorFontScripts.set(themePath, majorScripts);
38607
+ }
38608
+ const minorScripts = this.collectFontScriptOverrides(minorFontNode);
38609
+ if (Object.keys(minorScripts).length > 0) {
38610
+ this.masterThemeMinorFontScripts.set(themePath, minorScripts);
38611
+ }
38612
+ const objectDefaultsNode = themeRoot?.["a:objectDefaults"];
38613
+ if (objectDefaultsNode) {
38614
+ const od = {
38615
+ spDef: objectDefaultsNode["a:spDef"],
38616
+ lnDef: objectDefaultsNode["a:lnDef"],
38617
+ txDef: objectDefaultsNode["a:txDef"]
38618
+ };
38619
+ if (od.spDef !== void 0 || od.lnDef !== void 0 || od.txDef !== void 0) {
38620
+ this.masterThemeObjectDefaults.set(themePath, od);
38621
+ }
38622
+ }
38623
+ const extraClrSchemeLst = themeRoot?.["a:extraClrSchemeLst"];
38624
+ if (extraClrSchemeLst !== void 0) {
38625
+ this.masterThemeExtraClrSchemeLst.set(themePath, extraClrSchemeLst);
38626
+ }
38627
+ const custClrLst = themeRoot?.["a:custClrLst"];
38628
+ if (custClrLst !== void 0) {
38629
+ this.masterThemeCustClrLst.set(themePath, custClrLst);
38630
+ }
38631
+ const themeExtLst = themeRoot?.["a:extLst"];
38632
+ if (themeExtLst !== void 0) {
38633
+ this.masterThemeExtLst.set(themePath, themeExtLst);
38634
+ }
38635
+ const formatScheme = fmtScheme ? this.parseFormatScheme(fmtScheme) : void 0;
38636
+ return { colorMap, fontMap, formatScheme };
38637
+ }
38638
+ /**
38639
+ * Parse `<a:font script="…" typeface="…"/>` children of a major or
38640
+ * minor font node into a `script -> typeface` dictionary.
38641
+ *
38642
+ * fast-xml-parser collapses repeated tags into arrays, so iterate
38643
+ * over the array form regardless of how many siblings are present.
38644
+ *
38645
+ * Phase 4 Stream A / M4.
38646
+ */
38647
+ collectFontScriptOverrides(fontNode) {
38648
+ const overrides = {};
38649
+ if (!fontNode) {
38650
+ return overrides;
38651
+ }
38652
+ const fontEntries = this.ensureArray(fontNode["a:font"]);
38653
+ for (const entry of fontEntries) {
38654
+ const script = String(entry?.["@_script"] || "").trim();
38655
+ const typeface = this.normalizeTypefaceToken(String(entry?.["@_typeface"] || ""));
38656
+ if (script && typeface) {
38657
+ overrides[script] = typeface;
38658
+ }
38659
+ }
38660
+ return overrides;
38661
+ }
38662
+ /**
38663
+ * Resolve the theme file path referenced by a given master's `.rels`.
38664
+ * Returns `undefined` when the master has no theme relationship.
38665
+ */
38666
+ async resolveThemePathForMaster(masterPath) {
38667
+ const relsPath = masterPath.replace(
38668
+ /ppt\/slideMasters\/(slideMaster\d+)\.xml/,
38669
+ "ppt/slideMasters/_rels/$1.xml.rels"
38670
+ );
38671
+ const relsXml = this.zip.file(relsPath);
38672
+ if (!relsXml) {
38673
+ return void 0;
38674
+ }
38675
+ const relsData = this.parser.parse(await relsXml.async("string"));
38676
+ const relNodes = this.ensureArray(relsData?.Relationships?.Relationship);
38677
+ for (const rel of relNodes) {
38678
+ const target = String(rel["@_Target"] || "");
38679
+ if (!target.includes("theme")) {
38680
+ continue;
38681
+ }
38682
+ const themePath = target.startsWith("..") ? this.resolvePath(masterPath.substring(0, masterPath.lastIndexOf("/") + 1), target) : target.startsWith("/") ? target.slice(1) : `ppt/${target.replace(/^\.?\//, "")}`;
38683
+ if (themePath.startsWith("ppt/theme/")) {
38684
+ return themePath;
38685
+ }
38686
+ }
38687
+ return void 0;
38688
+ }
38689
+ /**
38690
+ * Populate {@link masterThemeColorMaps}, {@link masterThemeFontMaps},
38691
+ * and {@link masterThemeFormatSchemes} for every slide master in the
38692
+ * deck. Multi-master support — Phase 2 Stream B / C-H4.
38693
+ */
38694
+ async loadPerMasterThemes() {
38695
+ const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
38696
+ if (!masterFiles || masterFiles.length === 0) {
38697
+ return;
38698
+ }
38699
+ for (const file of masterFiles) {
38700
+ try {
38701
+ const themePath = await this.resolveThemePathForMaster(file.name);
38702
+ if (!themePath) {
38703
+ continue;
38704
+ }
38705
+ this.masterThemePaths.set(file.name, themePath);
38706
+ const parsed = await this.parseThemePart(themePath);
38707
+ if (!parsed) {
38708
+ continue;
38709
+ }
38710
+ this.masterThemeColorMaps.set(file.name, parsed.colorMap);
38711
+ this.masterThemeFontMaps.set(file.name, parsed.fontMap);
38712
+ if (parsed.formatScheme) {
38713
+ this.masterThemeFormatSchemes.set(file.name, parsed.formatScheme);
38714
+ }
38715
+ } catch (error) {
38716
+ console.warn(`Failed to load per-master theme for ${file.name}:`, error);
38717
+ }
38718
+ }
38719
+ }
38720
+ async loadThemeData() {
38721
+ const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
38722
+ if (!themeFiles || themeFiles.length === 0) {
38723
+ return;
36287
38724
  }
38725
+ const preferredThemePath = await this.resolvePrimaryThemePath();
38726
+ const themeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) ?? themeFiles[0] : themeFiles[0];
38727
+ const parsed = await this.parseThemePart(themeFile.name);
38728
+ if (parsed) {
38729
+ this.themeColorMap = parsed.colorMap;
38730
+ this.themeFontMap = parsed.fontMap;
38731
+ if (parsed.formatScheme) {
38732
+ this.themeFormatScheme = parsed.formatScheme;
38733
+ }
38734
+ }
38735
+ await this.applySlideMasterColorMap(this.getDefaultSchemeColorMap());
38736
+ await this.loadPerMasterThemes();
38737
+ this.globalThemeColorMapSnapshot = { ...this.themeColorMap };
38738
+ this.globalThemeFontMapSnapshot = { ...this.themeFontMap };
38739
+ this.globalThemeFormatSchemeSnapshot = this.themeFormatScheme;
36288
38740
  }
36289
38741
  };
36290
38742
 
36291
38743
  // src/core/core/runtime/PptxHandlerRuntimeThemeProcessing.ts
36292
- var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
38744
+ var PptxHandlerRuntime61 = class extends PptxHandlerRuntime60 {
36293
38745
  // ---------------------------------------------------------------------------
36294
38746
  // Theme editing — update colour scheme, font scheme, and name in the zip
36295
38747
  // ---------------------------------------------------------------------------
@@ -36439,7 +38891,7 @@ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36439
38891
  };
36440
38892
 
36441
38893
  // src/core/core/runtime/PptxHandlerRuntimeComments.ts
36442
- var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime60 {
38894
+ var PptxHandlerRuntime62 = class _PptxHandlerRuntime extends PptxHandlerRuntime61 {
36443
38895
  /**
36444
38896
  * Parse modern threaded comments (PowerPoint 2019+ / Office 365).
36445
38897
  * Modern comments use `p188:cmLst`/`p15:cmLst` roots within
@@ -36672,7 +39124,7 @@ var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
36672
39124
  };
36673
39125
 
36674
39126
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtXmlUtils.ts
36675
- var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
39127
+ var PptxHandlerRuntime63 = class extends PptxHandlerRuntime62 {
36676
39128
  async readXmlPartByRelationshipId(slidePath, relationshipId) {
36677
39129
  const normalizedRelationshipId = String(relationshipId || "").trim();
36678
39130
  if (normalizedRelationshipId.length === 0) {
@@ -36827,7 +39279,7 @@ var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
36827
39279
  };
36828
39280
 
36829
39281
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtParsing.ts
36830
- var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime62 {
39282
+ var PptxHandlerRuntime64 = class _PptxHandlerRuntime extends PptxHandlerRuntime63 {
36831
39283
  /**
36832
39284
  * Parse quick style from `ppt/diagrams/quickStyles*.xml`.
36833
39285
  */
@@ -36986,7 +39438,7 @@ var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
36986
39438
  };
36987
39439
 
36988
39440
  // src/core/core/runtime/PptxHandlerRuntimeSmartArt.ts
36989
- var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
39441
+ var PptxHandlerRuntime65 = class _PptxHandlerRuntime extends PptxHandlerRuntime64 {
36990
39442
  async getSmartArtDataForGraphicFrame(slidePath, graphicFrame) {
36991
39443
  const graphicData = this.xmlLookupService.getChildByLocalName(
36992
39444
  this.xmlLookupService.getChildByLocalName(graphicFrame, "graphic"),
@@ -37037,10 +39489,14 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37037
39489
  const layoutPart = layoutRelationshipId.length > 0 ? await this.readXmlPartByRelationshipId(slidePath, layoutRelationshipId) : void 0;
37038
39490
  const layoutType = layoutPart?.partPath?.split("/").pop()?.replace(/\.[^.]+$/, "") || void 0;
37039
39491
  const chrome = this.parseSmartArtChrome(dataModel);
37040
- const drawingRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37041
- const drawingShapes = await this.parseSmartArtDrawingShapes(slidePath, drawingRelationshipId);
37042
39492
  const colorsRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37043
39493
  const colorTransform = await this.parseSmartArtColorTransform(slidePath, colorsRelationshipId);
39494
+ const drawingResolution = await this.resolveSmartArtDrawingPart(
39495
+ slidePath,
39496
+ diagramDataRelationshipId
39497
+ );
39498
+ const drawingShapes = drawingResolution ? await this.parseSmartArtDrawingShapesFromPath(drawingResolution.path) : [];
39499
+ const drawingRelationshipId = drawingResolution?.relId;
37044
39500
  const styleRelationshipId = String(relationshipIds["@_r:qs"] || "").trim();
37045
39501
  const quickStyle = await this.parseSmartArtQuickStyle(slidePath, styleRelationshipId);
37046
39502
  return {
@@ -37052,11 +39508,87 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37052
39508
  colorTransform,
37053
39509
  quickStyle,
37054
39510
  dataRelId: diagramDataRelationshipId,
37055
- drawingRelId: drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
39511
+ drawingRelId: drawingRelationshipId && drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
37056
39512
  colorsRelId: colorsRelationshipId.length > 0 ? colorsRelationshipId : void 0,
37057
39513
  styleRelId: styleRelationshipId.length > 0 ? styleRelationshipId : void 0
37058
39514
  };
37059
39515
  }
39516
+ /**
39517
+ * Resolve the SmartArt drawing-shapes part path + relationship id.
39518
+ *
39519
+ * Strategy:
39520
+ * 1. Locate the data-model part's rels file
39521
+ * (`ppt/diagrams/_rels/data*.xml.rels`) via `slideRelsMap`.
39522
+ * 2. Find a relationship whose `Type` matches the `…/diagramDrawing`
39523
+ * URI. PowerPoint emits this for any deck that has had its
39524
+ * SmartArt rendered to drawing shapes.
39525
+ * 3. Return the matched part path (so the caller can load it
39526
+ * directly) and the relationship id (for round-trip preservation).
39527
+ */
39528
+ async resolveSmartArtDrawingPart(slidePath, diagramDataRelationshipId) {
39529
+ if (diagramDataRelationshipId.length === 0) {
39530
+ return void 0;
39531
+ }
39532
+ const slideRels = this.slideRelsMap.get(slidePath);
39533
+ const dataTarget = slideRels?.get(diagramDataRelationshipId);
39534
+ if (!dataTarget) {
39535
+ return void 0;
39536
+ }
39537
+ const dataPath = this.resolveImagePath(slidePath, dataTarget);
39538
+ const dataDir = dataPath.replace(/\/[^/]+$/, "");
39539
+ const dataFile = dataPath.split("/").pop() ?? "";
39540
+ const dataRelsPath = `${dataDir}/_rels/${dataFile}.rels`;
39541
+ const relsXml = await this.zip.file(dataRelsPath)?.async("string");
39542
+ if (!relsXml) {
39543
+ return void 0;
39544
+ }
39545
+ try {
39546
+ const parsed = this.parser.parse(relsXml);
39547
+ const relsRoot = parsed["Relationships"];
39548
+ if (!relsRoot) {
39549
+ return void 0;
39550
+ }
39551
+ const rels = this.ensureArray(relsRoot["Relationship"]);
39552
+ const drawingRel = rels.find(
39553
+ (rel) => String(rel?.["@_Type"] || "").endsWith("/diagramDrawing")
39554
+ );
39555
+ const id = String(drawingRel?.["@_Id"] || "").trim();
39556
+ const target = String(drawingRel?.["@_Target"] || "").trim();
39557
+ if (id.length === 0 || target.length === 0) {
39558
+ return void 0;
39559
+ }
39560
+ const drawingPath = this.resolveImagePath(dataPath, target);
39561
+ return { relId: id, path: drawingPath };
39562
+ } catch {
39563
+ return void 0;
39564
+ }
39565
+ }
39566
+ /**
39567
+ * Parse SmartArt drawing shapes given an absolute part path.
39568
+ *
39569
+ * Wraps `parseSmartArtDrawingShapes` (which expects a slide-relative
39570
+ * relationship id) with a path-based lookup so the resolution layer
39571
+ * can pull the part from anywhere in the package.
39572
+ */
39573
+ async parseSmartArtDrawingShapesFromPath(drawingPath) {
39574
+ const xmlString = await this.zip.file(drawingPath)?.async("string");
39575
+ if (!xmlString) {
39576
+ return [];
39577
+ }
39578
+ try {
39579
+ const xml = this.parser.parse(xmlString);
39580
+ const drawing = this.xmlLookupService.getChildByLocalName(xml, "drawing");
39581
+ const spTree = this.xmlLookupService.getChildByLocalName(drawing || xml, "spTree");
39582
+ if (!spTree) {
39583
+ return [];
39584
+ }
39585
+ const shapes = this.xmlLookupService.getChildrenArrayByLocalName(spTree, "sp");
39586
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
39587
+ return shapes.map((sp, index) => this.parseDrawingShape(sp, index, emuPerPx)).filter((entry) => entry !== null);
39588
+ } catch {
39589
+ return [];
39590
+ }
39591
+ }
37060
39592
  parseSmartArtConnections(dataModel) {
37061
39593
  const connectionList = this.xmlLookupService.getChildByLocalName(dataModel, "cxnLst");
37062
39594
  const rawConnections = this.xmlLookupService.getChildrenArrayByLocalName(connectionList, "cxn");
@@ -37087,7 +39619,7 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37087
39619
  };
37088
39620
 
37089
39621
  // src/core/core/runtime/PptxHandlerRuntimeChartDetection.ts
37090
- var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
39622
+ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37091
39623
  detectChartType(plotArea) {
37092
39624
  if (!plotArea) {
37093
39625
  return "unknown";
@@ -37196,7 +39728,7 @@ var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
37196
39728
  };
37197
39729
 
37198
39730
  // src/core/core/runtime/PptxHandlerRuntimeChartParsingHelpers.ts
37199
- var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
39731
+ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37200
39732
  /**
37201
39733
  * Parse `c:plotVisOnly` from the chart root element.
37202
39734
  *
@@ -37257,7 +39789,7 @@ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37257
39789
  };
37258
39790
 
37259
39791
  // src/core/core/runtime/PptxHandlerRuntimeChartExternalData.ts
37260
- var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
39792
+ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37261
39793
  /**
37262
39794
  * Parse `c:externalData` from the chart's `c:chartSpace` and resolve
37263
39795
  * the external relationship target from the chart part's .rels file.
@@ -37373,7 +39905,7 @@ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37373
39905
  };
37374
39906
 
37375
39907
  // src/core/core/runtime/PptxHandlerRuntimeChartColorStyle.ts
37376
- var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
39908
+ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37377
39909
  /**
37378
39910
  * Parse the Office 2013+ chart color style part (`chartColorStyle*.xml`)
37379
39911
  * referenced from the chart's relationships.
@@ -37480,7 +40012,7 @@ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37480
40012
  };
37481
40013
 
37482
40014
  // src/core/core/runtime/PptxHandlerRuntimeChartParsing.ts
37483
- var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
40015
+ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
37484
40016
  /**
37485
40017
  * Parse chart data from a graphic frame element on a slide.
37486
40018
  *
@@ -37727,7 +40259,7 @@ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37727
40259
  };
37728
40260
 
37729
40261
  // src/core/core/runtime/PptxHandlerRuntimePresentationStructure.ts
37730
- var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
40262
+ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
37731
40263
  parseEditorAnimations(slideXml2) {
37732
40264
  return this.editorAnimationService.parseEditorAnimations(slideXml2);
37733
40265
  }
@@ -37972,7 +40504,7 @@ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
37972
40504
  };
37973
40505
 
37974
40506
  // src/core/core/runtime/PptxHandlerRuntimeEmbeddedFonts.ts
37975
- var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
40507
+ var PptxHandlerRuntime72 = class extends PptxHandlerRuntime71 {
37976
40508
  async getEmbeddedFonts() {
37977
40509
  const embeddedFontEntries = this.ensureArray(
37978
40510
  this.presentationData?.["p:presentation"]?.["p:embeddedFontLst"]?.["p:embeddedFont"]
@@ -38144,7 +40676,7 @@ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
38144
40676
  };
38145
40677
 
38146
40678
  // src/core/core/runtime/PptxHandlerRuntimeLoadSession.ts
38147
- var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime71 {
40679
+ var PptxHandlerRuntime73 = class _PptxHandlerRuntime extends PptxHandlerRuntime72 {
38148
40680
  isZipContainer(data) {
38149
40681
  const bytes = new Uint8Array(data);
38150
40682
  if (bytes.byteLength < 4) {
@@ -38187,6 +40719,23 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38187
40719
  this.imageDataCache.clear();
38188
40720
  this.themeColorMap = {};
38189
40721
  this.themeFontMap = {};
40722
+ this.masterClrMaps.clear();
40723
+ this.masterThemeColorMaps.clear();
40724
+ this.masterThemeFontMaps.clear();
40725
+ this.masterThemeFormatSchemes.clear();
40726
+ this.masterThemePaths.clear();
40727
+ this.masterThemeMajorFontScripts.clear();
40728
+ this.masterThemeMinorFontScripts.clear();
40729
+ this.masterThemeNames.clear();
40730
+ this.masterThemeFontSchemeNames.clear();
40731
+ this.masterThemeColorSchemeNames.clear();
40732
+ this.originalThemeXmlByPath.clear();
40733
+ this.dirtyThemePaths.clear();
40734
+ this.masterThemeObjectDefaults.clear();
40735
+ this.masterThemeExtraClrSchemeLst.clear();
40736
+ this.masterThemeCustClrLst.clear();
40737
+ this.masterThemeExtLst.clear();
40738
+ this.currentMasterClrMap = null;
38190
40739
  this.presentationDefaultTextStyle = void 0;
38191
40740
  this.commentAuthorMap.clear();
38192
40741
  this.commentAuthorDetails.clear();
@@ -38341,6 +40890,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38341
40890
  setCurrentSlideClrMapOverride: (override) => {
38342
40891
  this.currentSlideClrMapOverride = override;
38343
40892
  },
40893
+ setActiveMasterForSlide: (slidePath) => this.setActiveMasterForSlide(slidePath),
38344
40894
  findLayoutPathForSlide: (slidePath) => this.findLayoutPathForSlide(slidePath),
38345
40895
  loadThemeOverride: (partBasePath) => this.loadThemeOverride(partBasePath),
38346
40896
  applyThemeOverrideState: (override) => this.applyThemeOverrideState(override),
@@ -38371,7 +40921,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38371
40921
  };
38372
40922
 
38373
40923
  // src/core/core/runtime/PptxHandlerRuntimeLoadPipeline.ts
38374
- var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
40924
+ var PptxHandlerRuntime74 = class extends PptxHandlerRuntime73 {
38375
40925
  async buildLoadData(presentationState, slidesWithWarnings) {
38376
40926
  const headerFooter = this.extractHeaderFooter();
38377
40927
  const presentationProperties = await this.parsePresentationProperties();
@@ -38383,6 +40933,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38383
40933
  const notesMaster = await this.parseNotesMaster();
38384
40934
  const handoutMaster = await this.parseHandoutMaster();
38385
40935
  const slideMasters = await this.parseSlideMasters();
40936
+ await this.enrichSlideMastersWithTxStyles(slideMasters);
38386
40937
  const tags = await this.parseTags();
38387
40938
  const customProperties = await this.parseCustomProperties();
38388
40939
  const coreProperties = await this.parseCoreProperties();
@@ -38717,7 +41268,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38717
41268
  };
38718
41269
 
38719
41270
  // src/core/core/runtime/PptxHandlerRuntimeImplementation.ts
38720
- var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime73 {
41271
+ var PptxHandlerRuntime75 = class _PptxHandlerRuntime extends PptxHandlerRuntime74 {
38721
41272
  constructor(dependencyFactory = new PptxRuntimeDependencyFactory()) {
38722
41273
  super();
38723
41274
  this.dependencyFactory = dependencyFactory;
@@ -38765,6 +41316,9 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38765
41316
  extractGradientPathType: (gradFill) => this.colorStyleCodec.extractGradientPathType(gradFill),
38766
41317
  extractGradientFocalPoint: (gradFill) => this.colorStyleCodec.extractGradientFocalPoint(gradFill),
38767
41318
  extractGradientFillToRect: (gradFill) => this.colorStyleCodec.extractGradientFillToRect(gradFill),
41319
+ extractGradientFlip: (gradFill) => this.colorStyleCodec.extractGradientFlip(gradFill),
41320
+ extractGradientRotWithShape: (gradFill) => this.colorStyleCodec.extractGradientRotWithShape(gradFill),
41321
+ extractGradientScaled: (gradFill) => this.colorStyleCodec.extractGradientScaled(gradFill),
38768
41322
  normalizeStrokeDashType: (value) => this.normalizeStrokeDashType(value),
38769
41323
  normalizeConnectorArrowType: (value) => this.normalizeConnectorArrowType(value),
38770
41324
  ensureArray: (value) => this.ensureArray(value),
@@ -38838,11 +41392,11 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38838
41392
  };
38839
41393
 
38840
41394
  // src/core/core/PptxHandlerRuntime.ts
38841
- var PptxHandlerRuntime75 = class extends PptxHandlerRuntime74 {
41395
+ var PptxHandlerRuntime76 = class extends PptxHandlerRuntime75 {
38842
41396
  };
38843
41397
 
38844
41398
  // src/core/core/PptxHandlerRuntimeFactory.ts
38845
- var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime75();
41399
+ var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime76();
38846
41400
 
38847
41401
  // src/core/PptxHandlerCore.ts
38848
41402
  var PptxHandlerCore = class {