pptx-react-viewer 1.1.3 → 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-BMjoxMDV.d.ts → SvgExporter-0TxiiorD.d.ts} +1 -1
  9. package/node_modules/pptx-viewer-core/dist/{SvgExporter-z6AbXRQg.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 +2711 -304
  13. package/node_modules/pptx-viewer-core/dist/cli/index.mjs +2711 -304
  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 +692 -44
  17. package/node_modules/pptx-viewer-core/dist/index.d.ts +692 -44
  18. package/node_modules/pptx-viewer-core/dist/index.js +2794 -373
  19. package/node_modules/pptx-viewer-core/dist/index.mjs +2785 -373
  20. package/node_modules/pptx-viewer-core/dist/{presentation-CchuDGfU.d.mts → presentation-ArhfImJ5.d.mts} +225 -8
  21. package/node_modules/pptx-viewer-core/dist/{presentation-CchuDGfU.d.ts → presentation-ArhfImJ5.d.ts} +225 -8
  22. package/node_modules/pptx-viewer-core/dist/{text-operations-CeukUztU.d.mts → text-operations-CLj-sJyk.d.mts} +1 -1
  23. package/node_modules/pptx-viewer-core/dist/{text-operations-e7JxgI5l.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
@@ -5250,6 +5264,9 @@ ${Array.from(
5250
5264
  <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
5251
5265
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
5252
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"
5253
5270
  saveSubsetFonts="1">
5254
5271
  <p:sldMasterIdLst>
5255
5272
  <p:sldMasterId id="2147483648" r:id="rId1"/>
@@ -6671,6 +6688,22 @@ var PptxPresentationSlidesReconciler = class {
6671
6688
  };
6672
6689
 
6673
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
+ }
6674
6707
  var PptxSlideRelationshipRegistry = class {
6675
6708
  relationships;
6676
6709
  usedRelationshipIds = /* @__PURE__ */ new Set();
@@ -6741,7 +6774,7 @@ var PptxSlideRelationshipRegistry = class {
6741
6774
  return existingRelationshipId;
6742
6775
  }
6743
6776
  const relationshipId = this.nextRelationshipId();
6744
- const targetMode = /^(https?:|mailto:|ftp:|file:)/i.test(normalizedTarget) ? "External" : void 0;
6777
+ const targetMode = isExternalTarget(normalizedTarget) ? "External" : void 0;
6745
6778
  this.upsertRelationship(
6746
6779
  relationshipId,
6747
6780
  this.hyperlinkRelationshipType,
@@ -6991,6 +7024,82 @@ var PptxSlideNotesPartUpdater = class {
6991
7024
  }
6992
7025
  };
6993
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
+
6994
7103
  // src/core/core/builders/PptxSlideBackgroundBuilder.ts
6995
7104
  var PptxSlideBackgroundBuilder = class {
6996
7105
  applyBackground(init) {
@@ -7026,10 +7135,13 @@ var PptxSlideBackgroundBuilder = class {
7026
7135
  init.slideImageRelationshipType,
7027
7136
  relativeBackgroundImagePath
7028
7137
  );
7029
- backgroundProperties["a:blipFill"] = {
7030
- "a:blip": { "@_r:embed": backgroundRelationshipId },
7031
- "a:stretch": { "a:fillRect": {} }
7032
- };
7138
+ backgroundProperties["a:blipFill"] = reorderObjectKeys(
7139
+ {
7140
+ "a:blip": { "@_r:embed": backgroundRelationshipId },
7141
+ "a:stretch": { "a:fillRect": {} }
7142
+ },
7143
+ BLIP_FILL_ORDER
7144
+ );
7033
7145
  }
7034
7146
  } else if (hasBackgroundColor && init.slide.backgroundColor) {
7035
7147
  backgroundProperties["a:solidFill"] = {
@@ -7773,6 +7885,45 @@ var PptxGradientStyleCodec = class {
7773
7885
  b: Number.isFinite(b) ? this.context.clampUnitInterval(b / 1e5) : 0
7774
7886
  };
7775
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
+ }
7776
7927
  extractGradientAngle(gradFill) {
7777
7928
  const angleRaw = Number.parseInt(
7778
7929
  String(gradFill["a:lin"]?.["@_ang"] || ""),
@@ -7859,11 +8010,14 @@ var PptxGradientStyleCodec = class {
7859
8010
  return void 0;
7860
8011
  }
7861
8012
  const gradientType = shapeStyle.fillGradientType || "linear";
7862
- const gradientXml = {
7863
- "a:gsLst": {
7864
- "a:gs": stops
7865
- }
7866
- };
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 };
7867
8021
  if (gradientType === "radial") {
7868
8022
  const pathType = shapeStyle.fillGradientPathType || "circle";
7869
8023
  const pathXml = {
@@ -7893,10 +8047,15 @@ var PptxGradientStyleCodec = class {
7893
8047
  gradientXml["a:path"] = pathXml;
7894
8048
  } else {
7895
8049
  const normalizedAngle = typeof shapeStyle.fillGradientAngle === "number" && Number.isFinite(shapeStyle.fillGradientAngle) ? shapeStyle.fillGradientAngle : 90;
7896
- gradientXml["a:lin"] = {
7897
- "@_ang": String(Math.round(normalizedAngle * 6e4)),
7898
- "@_scaled": "1"
8050
+ const linNode = {
8051
+ "@_ang": String(Math.round(normalizedAngle * 6e4))
7899
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;
7900
8059
  }
7901
8060
  return gradientXml;
7902
8061
  }
@@ -8293,6 +8452,30 @@ var PRESET_SHADOW_OPACITY_MAP = {
8293
8452
  };
8294
8453
 
8295
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
+ }
8296
8479
  var PptxShapeEffectStyleExtractor = class {
8297
8480
  context;
8298
8481
  constructor(context) {
@@ -8316,7 +8499,12 @@ var PptxShapeEffectStyleExtractor = class {
8316
8499
  const shadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8317
8500
  const shadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8318
8501
  const rotateWithShape = outerShadow["@_rotWithShape"];
8319
- 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"]);
8320
8508
  return {
8321
8509
  shadowColor,
8322
8510
  shadowOpacity,
@@ -8325,7 +8513,12 @@ var PptxShapeEffectStyleExtractor = class {
8325
8513
  shadowOffsetY,
8326
8514
  shadowAngle: directionDegrees,
8327
8515
  shadowDistance: distance,
8328
- shadowRotateWithShape
8516
+ shadowRotateWithShape,
8517
+ shadowScaleX,
8518
+ shadowScaleY,
8519
+ shadowSkewX,
8520
+ shadowSkewY,
8521
+ shadowAlignment
8329
8522
  };
8330
8523
  }
8331
8524
  extractPresetShadowStyle(effectLstParent) {
@@ -8371,12 +8564,14 @@ var PptxShapeEffectStyleExtractor = class {
8371
8564
  const directionRadians = directionDegrees * Math.PI / 180;
8372
8565
  const innerShadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8373
8566
  const innerShadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8567
+ const innerShadowRotateWithShape = parseBoolAttr(innerShadow["@_rotWithShape"]);
8374
8568
  return {
8375
8569
  innerShadowColor,
8376
8570
  innerShadowOpacity,
8377
8571
  innerShadowBlur,
8378
8572
  innerShadowOffsetX,
8379
- innerShadowOffsetY
8573
+ innerShadowOffsetY,
8574
+ innerShadowRotateWithShape
8380
8575
  };
8381
8576
  }
8382
8577
  extractGlowStyle(shapeProps) {
@@ -8425,6 +8620,16 @@ var PptxShapeEffectStyleExtractor = class {
8425
8620
  const reflectionRotation = Number.isFinite(rotationRaw) ? rotationRaw / 6e4 : void 0;
8426
8621
  const distanceRaw = Number.parseInt(String(reflectionNode["@_dist"] || ""), 10);
8427
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;
8428
8633
  return {
8429
8634
  reflectionBlurRadius,
8430
8635
  reflectionStartOpacity,
@@ -8432,7 +8637,15 @@ var PptxShapeEffectStyleExtractor = class {
8432
8637
  reflectionEndPosition,
8433
8638
  reflectionDirection,
8434
8639
  reflectionRotation,
8435
- reflectionDistance
8640
+ reflectionDistance,
8641
+ reflectionFadeDirection,
8642
+ reflectionScaleX,
8643
+ reflectionScaleY,
8644
+ reflectionSkewX,
8645
+ reflectionSkewY,
8646
+ reflectionAlignment,
8647
+ reflectionRotateWithShape,
8648
+ reflectionStartPosition
8436
8649
  };
8437
8650
  }
8438
8651
  extractBlurStyle(shapeProps) {
@@ -8484,11 +8697,56 @@ var PptxShapeEffectXmlBuilder = class {
8484
8697
  }
8485
8698
  }
8486
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
+ }
8487
8715
  if (typeof shapeStyle.shadowRotateWithShape === "boolean") {
8488
8716
  xmlObj["@_rotWithShape"] = shapeStyle.shadowRotateWithShape ? "1" : "0";
8489
8717
  }
8490
8718
  return xmlObj;
8491
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
+ }
8492
8750
  buildInnerShadowXml(shapeStyle) {
8493
8751
  const innerColor = String(shapeStyle.innerShadowColor || "").trim();
8494
8752
  if (innerColor.length === 0 || innerColor === "transparent") {
@@ -8500,7 +8758,7 @@ var PptxShapeEffectXmlBuilder = class {
8500
8758
  const opacity = typeof shapeStyle.innerShadowOpacity === "number" && Number.isFinite(shapeStyle.innerShadowOpacity) ? this.context.clampUnitInterval(shapeStyle.innerShadowOpacity) : 0.5;
8501
8759
  const distance = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
8502
8760
  const directionDegrees = (Math.atan2(offsetY, offsetX) * 180 / Math.PI + 360) % 360;
8503
- return {
8761
+ const xmlObj = {
8504
8762
  "@_blurRad": String(Math.round(blurValue * this.context.emuPerPx)),
8505
8763
  "@_dist": String(Math.round(distance * this.context.emuPerPx)),
8506
8764
  "@_dir": String(Math.round(directionDegrees * 6e4)),
@@ -8511,6 +8769,10 @@ var PptxShapeEffectXmlBuilder = class {
8511
8769
  }
8512
8770
  }
8513
8771
  };
8772
+ if (typeof shapeStyle.innerShadowRotateWithShape === "boolean") {
8773
+ xmlObj["@_rotWithShape"] = shapeStyle.innerShadowRotateWithShape ? "1" : "0";
8774
+ }
8775
+ return xmlObj;
8514
8776
  }
8515
8777
  buildGlowXml(shapeStyle) {
8516
8778
  const glowColor = String(shapeStyle.glowColor || "").trim();
@@ -8572,6 +8834,30 @@ var PptxShapeEffectXmlBuilder = class {
8572
8834
  Math.round(shapeStyle.reflectionDistance * this.context.emuPerPx)
8573
8835
  );
8574
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
+ }
8575
8861
  return reflectionXml;
8576
8862
  }
8577
8863
  buildBlurXml(shapeStyle) {
@@ -8679,6 +8965,9 @@ var PptxShapeEffectXmlCodec = class {
8679
8965
  buildOuterShadowXml(shapeStyle) {
8680
8966
  return this.builder.buildOuterShadowXml(shapeStyle);
8681
8967
  }
8968
+ buildPresetShadowXml(shapeStyle) {
8969
+ return this.builder.buildPresetShadowXml(shapeStyle);
8970
+ }
8682
8971
  buildInnerShadowXml(shapeStyle) {
8683
8972
  return this.builder.buildInnerShadowXml(shapeStyle);
8684
8973
  }
@@ -8839,6 +9128,9 @@ var PptxColorStyleCodec = class {
8839
9128
  buildOuterShadowXml(shapeStyle) {
8840
9129
  return this.shapeEffectXmlCodec.buildOuterShadowXml(shapeStyle);
8841
9130
  }
9131
+ buildPresetShadowXml(shapeStyle) {
9132
+ return this.shapeEffectXmlCodec.buildPresetShadowXml(shapeStyle);
9133
+ }
8842
9134
  buildInnerShadowXml(shapeStyle) {
8843
9135
  return this.shapeEffectXmlCodec.buildInnerShadowXml(shapeStyle);
8844
9136
  }
@@ -8860,6 +9152,15 @@ var PptxColorStyleCodec = class {
8860
9152
  extractGradientFillColor(gradFill) {
8861
9153
  return this.gradientStyleCodec.extractGradientFillColor(gradFill);
8862
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
+ }
8863
9164
  extractGradientPathType(gradFill) {
8864
9165
  return this.gradientStyleCodec.extractGradientPathType(gradFill);
8865
9166
  }
@@ -8871,6 +9172,61 @@ var PptxColorStyleCodec = class {
8871
9172
  }
8872
9173
  };
8873
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
+
8874
9230
  // src/core/core/builders/shape-style-3d-helpers.ts
8875
9231
  function applyScene3dStyle(shapeProps, style) {
8876
9232
  const scene3dNode = shapeProps["a:scene3d"];
@@ -8956,6 +9312,10 @@ function applyStrokeColor(lineNode, style, context) {
8956
9312
  const lineFill = lineNode["a:solidFill"];
8957
9313
  style.strokeColor = context.parseColor(lineFill);
8958
9314
  style.strokeOpacity = context.extractColorOpacity(lineFill);
9315
+ const strokeColorXml = extractColorChoiceXml(lineFill);
9316
+ if (strokeColorXml) {
9317
+ style.strokeColorXml = strokeColorXml;
9318
+ }
8959
9319
  } else if (lineNode["a:gradFill"]) {
8960
9320
  style.strokeColor = context.extractGradientFillColor(lineNode["a:gradFill"]);
8961
9321
  style.strokeOpacity = context.extractGradientOpacity(lineNode["a:gradFill"]);
@@ -9023,6 +9383,14 @@ function applyJoinCapCompound(lineNode, style) {
9023
9383
  style.lineJoin = "bevel";
9024
9384
  } else if ("a:miter" in lineNode) {
9025
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
+ }
9026
9394
  }
9027
9395
  const capValue = String(lineNode["@_cap"] || "").trim().toLowerCase();
9028
9396
  if (capValue === "rnd" || capValue === "sq" || capValue === "flat") {
@@ -9079,6 +9447,10 @@ var PptxShapeStyleExtractor = class {
9079
9447
  style.fillMode = "solid";
9080
9448
  style.fillColor = this.context.parseColor(solidFill);
9081
9449
  style.fillOpacity = this.context.extractColorOpacity(solidFill);
9450
+ const solidFillColorXml = extractColorChoiceXml(solidFill);
9451
+ if (solidFillColorXml) {
9452
+ style.fillColorXml = solidFillColorXml;
9453
+ }
9082
9454
  } else if (gradFill) {
9083
9455
  style.fillMode = "gradient";
9084
9456
  style.fillColor = this.context.extractGradientFillColor(gradFill);
@@ -9090,6 +9462,18 @@ var PptxShapeStyleExtractor = class {
9090
9462
  style.fillGradientPathType = this.context.extractGradientPathType(gradFill);
9091
9463
  style.fillGradientFocalPoint = this.context.extractGradientFocalPoint(gradFill);
9092
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
+ }
9093
9477
  } else if (pattFill) {
9094
9478
  style.fillMode = "pattern";
9095
9479
  style.fillColor = this.context.parseColor(pattFill["a:fgClr"]) || this.context.parseColor(pattFill["a:bgClr"]);
@@ -9171,10 +9555,45 @@ var PptxShapeStyleExtractor = class {
9171
9555
  if (styleNode?.["a:effectRef"]) {
9172
9556
  this.context.resolveThemeEffectRef(styleNode["a:effectRef"], style);
9173
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
+ }
9174
9569
  applyScene3dStyle(shapeProps, style);
9175
9570
  applyShape3dStyle(shapeProps, style, this.context);
9176
9571
  return style;
9177
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
+ }
9178
9597
  /**
9179
9598
  * Extract p14:hiddenFill from the shape properties extension list.
9180
9599
  * URI: {AF507438-7753-43E0-B8FC-AC1667EBCBE1}
@@ -9225,10 +9644,15 @@ var PptxShapeStyleExtractor = class {
9225
9644
  function applyCellFillStyle(cellProperties, style, context) {
9226
9645
  let hasStyle = false;
9227
9646
  if (cellProperties?.["a:solidFill"]) {
9228
- const fillColor = context.parseColor(cellProperties["a:solidFill"]);
9647
+ const solidFillNode = cellProperties["a:solidFill"];
9648
+ const fillColor = context.parseColor(solidFillNode);
9229
9649
  if (fillColor) {
9230
9650
  style.fillMode = "solid";
9231
9651
  style.backgroundColor = fillColor;
9652
+ const bgColorXml = extractColorChoiceXml(solidFillNode);
9653
+ if (bgColorXml) {
9654
+ style.backgroundColorXml = bgColorXml;
9655
+ }
9232
9656
  hasStyle = true;
9233
9657
  }
9234
9658
  }
@@ -9265,6 +9689,10 @@ function applyCellFillStyle(cellProperties, style, context) {
9265
9689
  }
9266
9690
  }
9267
9691
  }
9692
+ if (cellProperties?.["a:noFill"] !== void 0) {
9693
+ style.fillMode = "none";
9694
+ hasStyle = true;
9695
+ }
9268
9696
  if (cellProperties?.["a:pattFill"]) {
9269
9697
  const pattFill = cellProperties["a:pattFill"];
9270
9698
  const fgColor = context.parseColor(pattFill["a:fgClr"]);
@@ -9571,8 +9999,7 @@ var PptxTableDataParser = class {
9571
9999
  return width / totalWidthEmu;
9572
10000
  }) : gridColumns.map(() => 1 / Math.max(gridColumns.length, 1));
9573
10001
  const tableProperties = tableNode["a:tblPr"] || {};
9574
- const tableStyleNode = tableProperties["a:tblStyle"];
9575
- const tableStyleId = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim() || void 0;
10002
+ const tableStyleId = this.extractTableStyleId(tableProperties);
9576
10003
  const xmlRows = this.context.ensureArray(tableNode["a:tr"]);
9577
10004
  const rows = xmlRows.map((rowNode) => {
9578
10005
  const rowHeightEmu = parseInt(String(rowNode?.["@_h"] || "0"), 10) || 0;
@@ -9607,6 +10034,28 @@ var PptxTableDataParser = class {
9607
10034
  return void 0;
9608
10035
  }
9609
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
+ }
9610
10059
  extractTableCellText(tableCell) {
9611
10060
  const paragraphs = this.context.ensureArray(tableCell?.["a:txBody"]?.["a:p"]);
9612
10061
  const lines = [];
@@ -9885,6 +10334,19 @@ var PptxGraphicFrameParser = class {
9885
10334
  if (graphicData["a:videoFile"] || graphicData["a:audioFile"] || uri.includes("/drawingml/2006/media")) {
9886
10335
  return "media";
9887
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
+ }
9888
10350
  return "unknown";
9889
10351
  }
9890
10352
  };
@@ -10786,6 +11248,7 @@ var PptxDocumentPropertiesUpdater = class {
10786
11248
  }));
10787
11249
  if (sanitized.length === 0) {
10788
11250
  this.context.zip.remove("docProps/custom.xml");
11251
+ await this.removeCustomPropertiesPackagingArtifacts();
10789
11252
  return;
10790
11253
  }
10791
11254
  const customXml = {
@@ -10805,6 +11268,116 @@ var PptxDocumentPropertiesUpdater = class {
10805
11268
  }
10806
11269
  };
10807
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
+ }
10808
11381
  }
10809
11382
  normalizeCustomPropertyType(type) {
10810
11383
  const supportedTypes = /* @__PURE__ */ new Set([
@@ -11148,6 +11721,7 @@ var PptxSlideLoaderService = class {
11148
11721
  await params.loadSlideRelationships(path2, slideRelsPath);
11149
11722
  const clrMapOverride = params.parseSlideClrMapOverride(slideXmlObj);
11150
11723
  params.setCurrentSlideClrMapOverride(clrMapOverride);
11724
+ await params.setActiveMasterForSlide?.(path2);
11151
11725
  let restoreThemeOverride;
11152
11726
  try {
11153
11727
  const layoutPathForOverride = params.findLayoutPathForSlide(path2);
@@ -15899,6 +16473,20 @@ var AXIS_TYPE_MAP = {
15899
16473
  dateAx: "dateAx",
15900
16474
  serAx: "serAx"
15901
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
+ }
15902
16490
  function parseChartAxes(plotArea, xmlLookup, colorParser, getLocalName) {
15903
16491
  const result = [];
15904
16492
  for (const key of Object.keys(plotArea)) {
@@ -16019,6 +16607,27 @@ function parseSingleAxis(axisNode, axisType, xmlLookup, colorParser) {
16019
16607
  if (dispUnitsNode) {
16020
16608
  parseDisplayUnits(dispUnitsNode, xmlLookup, result);
16021
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
+ }
16022
16631
  return result;
16023
16632
  }
16024
16633
  var VALID_DISPLAY_UNITS = /* @__PURE__ */ new Set([
@@ -17330,23 +17939,55 @@ var SHAPE_TREE_ELEMENT_TAGS = /* @__PURE__ */ new Set([
17330
17939
  "p16:model3D",
17331
17940
  ...VML_SHAPE_TAGS
17332
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
+ }
17333
17958
  function unwrapAlternateContent(container) {
17334
17959
  const altContents = ensureArray2(container["mc:AlternateContent"]);
17335
17960
  if (altContents.length === 0) {
17336
- return;
17961
+ return [];
17337
17962
  }
17963
+ const blocks = [];
17338
17964
  for (const ac of altContents) {
17339
- const branch = selectAlternateContentBranch(ac);
17340
- if (!branch) {
17965
+ const diagnosis = diagnoseSelection(ac);
17966
+ if (!diagnosis) {
17341
17967
  continue;
17342
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;
17343
17976
  for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
17344
17977
  const children = ensureArray2(branch[tag]);
17345
17978
  if (children.length > 0) {
17346
17979
  container[tag] = [...ensureArray2(container[tag]), ...children];
17980
+ for (const child of children) {
17981
+ block.childRefs.push({ tag, node: child });
17982
+ }
17347
17983
  }
17348
17984
  }
17985
+ if (block.childRefs.length > 0) {
17986
+ blocks.push(block);
17987
+ }
17349
17988
  }
17989
+ delete container["mc:AlternateContent"];
17990
+ return blocks;
17350
17991
  }
17351
17992
  function ensureArray2(val) {
17352
17993
  if (!val) {
@@ -17358,7 +17999,7 @@ function ensureArray2(val) {
17358
17999
 
17359
18000
  // src/core/utils/body-properties-parser.ts
17360
18001
  function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17361
- const parseBoolAttr = (attr) => {
18002
+ const parseBoolAttr2 = (attr) => {
17362
18003
  const raw = bodyPr[attr];
17363
18004
  if (raw === void 0) {
17364
18005
  return void 0;
@@ -17366,19 +18007,19 @@ function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17366
18007
  const val = String(raw).trim().toLowerCase();
17367
18008
  return val === "1" || val === "true";
17368
18009
  };
17369
- const compatLnSpc = parseBoolAttr("@_compatLnSpc");
18010
+ const compatLnSpc = parseBoolAttr2("@_compatLnSpc");
17370
18011
  if (compatLnSpc !== void 0) {
17371
18012
  textStyle.compatibleLineSpacing = compatLnSpc;
17372
18013
  }
17373
- const forceAA = parseBoolAttr("@_forceAA");
18014
+ const forceAA = parseBoolAttr2("@_forceAA");
17374
18015
  if (forceAA !== void 0) {
17375
18016
  textStyle.forceAntiAlias = forceAA;
17376
18017
  }
17377
- const upright = parseBoolAttr("@_upright");
18018
+ const upright = parseBoolAttr2("@_upright");
17378
18019
  if (upright !== void 0) {
17379
18020
  textStyle.upright = upright;
17380
18021
  }
17381
- const fromWordArt = parseBoolAttr("@_fromWordArt");
18022
+ const fromWordArt = parseBoolAttr2("@_fromWordArt");
17382
18023
  if (fromWordArt !== void 0) {
17383
18024
  textStyle.fromWordArt = fromWordArt;
17384
18025
  }
@@ -17401,46 +18042,6 @@ function writeBodyPrBooleanAttrs(bodyPr, textStyle) {
17401
18042
  }
17402
18043
  }
17403
18044
 
17404
- // src/core/utils/theme-override-utils.ts
17405
- var COLOR_MAP_ALIAS_KEYS = [
17406
- "bg1",
17407
- "tx1",
17408
- "bg2",
17409
- "tx2",
17410
- "accent1",
17411
- "accent2",
17412
- "accent3",
17413
- "accent4",
17414
- "accent5",
17415
- "accent6",
17416
- "hlink",
17417
- "folHlink"
17418
- ];
17419
- var DEFAULT_COLOR_MAP = {
17420
- bg1: "lt1",
17421
- tx1: "dk1",
17422
- bg2: "lt2",
17423
- tx2: "dk2",
17424
- accent1: "accent1",
17425
- accent2: "accent2",
17426
- accent3: "accent3",
17427
- accent4: "accent4",
17428
- accent5: "accent5",
17429
- accent6: "accent6",
17430
- hlink: "hlink",
17431
- folHlink: "folHlink"
17432
- };
17433
- function buildClrMapOverrideXml(override) {
17434
- if (!override || Object.keys(override).length === 0) {
17435
- return { "a:masterClrMapping": {} };
17436
- }
17437
- const attrs2 = {};
17438
- for (const key of COLOR_MAP_ALIAS_KEYS) {
17439
- attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
17440
- }
17441
- return { "a:overrideClrMapping": attrs2 };
17442
- }
17443
-
17444
18045
  // src/core/utils/data-url-utils.ts
17445
18046
  function parseDataUrlToBytes(dataUrl) {
17446
18047
  const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
@@ -17538,6 +18139,46 @@ async function fetchUrlToBytes(url) {
17538
18139
  }
17539
18140
  }
17540
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
+
17541
18182
  // src/core/utils/encryption-detection.ts
17542
18183
  var OLE_MAGIC = new Uint8Array([208, 207, 17, 224, 161, 27, 26, 225]);
17543
18184
  var ZIP_MAGIC = new Uint8Array([80, 75]);
@@ -23300,7 +23941,7 @@ function parseActiveXControlsFromSlide(slideXml2) {
23300
23941
  }
23301
23942
 
23302
23943
  // src/core/utils/theme-switching.ts
23303
- function normalizeHex(hex) {
23944
+ function normalizeHex2(hex) {
23304
23945
  if (!hex) {
23305
23946
  return "";
23306
23947
  }
@@ -23310,8 +23951,8 @@ function buildColorRemapTable(oldColorMap, newColorMap) {
23310
23951
  const remap = /* @__PURE__ */ new Map();
23311
23952
  const allKeys = [...THEME_COLOR_SCHEME_KEYS, "tx1", "bg1", "tx2", "bg2"];
23312
23953
  for (const key of allKeys) {
23313
- const oldVal = normalizeHex(oldColorMap[key]);
23314
- const newVal = normalizeHex(newColorMap[key]);
23954
+ const oldVal = normalizeHex2(oldColorMap[key]);
23955
+ const newVal = normalizeHex2(newColorMap[key]);
23315
23956
  if (oldVal && newVal && oldVal !== newVal) {
23316
23957
  remap.set(oldVal, `#${newVal}`);
23317
23958
  remap.set(`#${oldVal}`, `#${newVal}`);
@@ -23323,7 +23964,7 @@ function remapColor(color, remap) {
23323
23964
  if (!color) {
23324
23965
  return color;
23325
23966
  }
23326
- const normalized = normalizeHex(color);
23967
+ const normalized = normalizeHex2(color);
23327
23968
  const remapped = remap.get(normalized) ?? remap.get(`#${normalized}`);
23328
23969
  return remapped ?? color;
23329
23970
  }
@@ -23463,7 +24104,7 @@ function remapSlideColors(slide, remap) {
23463
24104
  function buildThemeColorMap(colorScheme) {
23464
24105
  const map = {};
23465
24106
  for (const key of THEME_COLOR_SCHEME_KEYS) {
23466
- map[key] = normalizeHex(colorScheme[key]);
24107
+ map[key] = normalizeHex2(colorScheme[key]);
23467
24108
  }
23468
24109
  map.tx1 = map.dk1;
23469
24110
  map.bg1 = map.lt1;
@@ -23499,8 +24140,11 @@ function applyThemeToData(data, newColorScheme, newFontScheme, themeName) {
23499
24140
 
23500
24141
  // src/core/core/runtime/PptxHandlerRuntimeSaveParagraphHelpers.ts
23501
24142
  var EMU_PER_PX4 = 9525;
23502
- function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing) {
24143
+ function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing, level) {
23503
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
+ }
23504
24148
  if (paragraphAlign) {
23505
24149
  paragraphProps["@_algn"] = paragraphAlign;
23506
24150
  }
@@ -23572,23 +24216,28 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23572
24216
  paragraphProps["a:buNone"] = {};
23573
24217
  return;
23574
24218
  }
23575
- if (bulletInfo.color) {
24219
+ if (bulletInfo.colorInherit) {
24220
+ paragraphProps["a:buClrTx"] = {};
24221
+ } else if (bulletInfo.color) {
23576
24222
  const colorHex = bulletInfo.color.replace("#", "");
23577
24223
  paragraphProps["a:buClr"] = {
23578
24224
  "a:srgbClr": { "@_val": colorHex }
23579
24225
  };
23580
24226
  }
23581
- if (bulletInfo.sizePercent !== void 0) {
24227
+ if (bulletInfo.sizeInherit) {
24228
+ paragraphProps["a:buSzTx"] = {};
24229
+ } else if (bulletInfo.sizePercent !== void 0) {
23582
24230
  paragraphProps["a:buSzPct"] = {
23583
24231
  "@_val": String(Math.round(bulletInfo.sizePercent * 1e3))
23584
24232
  };
23585
- }
23586
- if (bulletInfo.sizePts !== void 0) {
24233
+ } else if (bulletInfo.sizePts !== void 0) {
23587
24234
  paragraphProps["a:buSzPts"] = {
23588
24235
  "@_val": String(Math.round(bulletInfo.sizePts * 100))
23589
24236
  };
23590
24237
  }
23591
- if (bulletInfo.fontFamily) {
24238
+ if (bulletInfo.fontInherit) {
24239
+ paragraphProps["a:buFontTx"] = {};
24240
+ } else if (bulletInfo.fontFamily) {
23592
24241
  paragraphProps["a:buFont"] = {
23593
24242
  "@_typeface": bulletInfo.fontFamily
23594
24243
  };
@@ -23611,7 +24260,7 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23611
24260
  };
23612
24261
  }
23613
24262
  }
23614
- function assembleParagraphXml(runs, paragraphProps) {
24263
+ function assembleParagraphXml(runs, paragraphProps, endParaRunProperties) {
23615
24264
  const paragraph = {
23616
24265
  "a:pPr": paragraphProps
23617
24266
  };
@@ -23633,7 +24282,11 @@ function assembleParagraphXml(runs, paragraphProps) {
23633
24282
  if (cleanRegularRuns.length === 0 && fieldRuns.length === 0) {
23634
24283
  paragraph["a:r"] = runs.length > 1 ? runs : runs[0];
23635
24284
  }
23636
- 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
+ }
23637
24290
  return paragraph;
23638
24291
  }
23639
24292
  function computeUniformSegmentOverrides(textStyle, textSegments) {
@@ -23937,6 +24590,140 @@ var PptxHandlerRuntime = class {
23937
24590
  * `p:clrMapOvr / a:overrideClrMapping`.
23938
24591
  */
23939
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;
23940
24727
  /** Thumbnail image data from `docProps/thumbnail.jpeg` preserved for round-trip. */
23941
24728
  thumbnailData = null;
23942
24729
  /** Raw VBA project binary preserved for macro-enabled (.pptm) round-trip. */
@@ -23947,6 +24734,19 @@ var PptxHandlerRuntime = class {
23947
24734
  signatureDetection = null;
23948
24735
  /** Custom XML data parts parsed from `customXml/` in the OPC package. */
23949
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();
23950
24750
  /** Embedded fonts extracted during load, preserved for automatic re-embedding on save. */
23951
24751
  loadedEmbeddedFonts = [];
23952
24752
  /** Map of comment author IDs to display names (from `ppt/commentAuthors.xml`). */
@@ -25760,7 +26560,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25760
26560
  const bgColor = this.parseBackgroundColor(bg);
25761
26561
  const spTree = master["p:cSld"]?.["p:spTree"];
25762
26562
  const placeholders = this.extractPlaceholderList(spTree);
25763
- 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;
25764
26569
  } catch (e) {
25765
26570
  console.warn("Failed to parse handout master:", e);
25766
26571
  return void 0;
@@ -25786,7 +26591,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25786
26591
  const bgColor = this.parseBackgroundColor(bg);
25787
26592
  const spTree = master["p:cSld"]?.["p:spTree"];
25788
26593
  const placeholders = this.extractPlaceholderList(spTree);
25789
- 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;
25790
26600
  } catch (e) {
25791
26601
  console.warn("Failed to parse notes master:", e);
25792
26602
  return void 0;
@@ -25831,6 +26641,10 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25831
26641
  const uVal = String(userDrawn).trim().toLowerCase();
25832
26642
  layout.userDrawn = uVal === "1" || uVal === "true";
25833
26643
  }
26644
+ const hf = parseHeaderFooterFlags(sldLayout["p:hf"]);
26645
+ if (hf) {
26646
+ layout.headerFooter = hf;
26647
+ }
25834
26648
  const clrMapOvr = sldLayout["p:clrMapOvr"];
25835
26649
  if (clrMapOvr && clrMapOvr["a:masterClrMapping"] === void 0) {
25836
26650
  const overrideNode = clrMapOvr["a:overrideClrMapping"];
@@ -26436,6 +27250,18 @@ function buildClrChangeNode(style) {
26436
27250
  }
26437
27251
 
26438
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
+ }
26439
27265
  var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime11 {
26440
27266
  createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId) {
26441
27267
  const runProps = {
@@ -26493,6 +27319,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26493
27319
  if (style.bookmark) {
26494
27320
  runProps["@_bmk"] = style.bookmark;
26495
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
+ }
26496
27328
  if (style.textOutlineWidth || style.textOutlineColor) {
26497
27329
  const lnObj = {};
26498
27330
  if (typeof style.textOutlineWidth === "number" && style.textOutlineWidth > 0) {
@@ -26508,11 +27340,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26508
27340
  runProps["a:ln"] = lnObj;
26509
27341
  }
26510
27342
  if (style.color) {
26511
- runProps["a:solidFill"] = {
26512
- "a:srgbClr": {
26513
- "@_val": style.color.replace("#", "")
26514
- }
26515
- };
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
+ );
26516
27349
  } else if (style.textFillGradientStops && style.textFillGradientStops.length > 0) {
26517
27350
  const gradStops = style.textFillGradientStops.filter((stop) => Boolean(stop?.color)).map((stop) => {
26518
27351
  const rawPos = (stop.position ?? 0) / 100;
@@ -26585,16 +27418,32 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26585
27418
  };
26586
27419
  }
26587
27420
  if (style.fontFamily) {
26588
- runProps["a:latin"] = { "@_typeface": style.fontFamily };
26589
- runProps["a:ea"] = {
26590
- "@_typeface": style.eastAsiaFont || style.fontFamily
26591
- };
26592
- runProps["a:cs"] = {
26593
- "@_typeface": style.complexScriptFont || style.fontFamily
26594
- };
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
+ );
26595
27439
  }
26596
27440
  if (style.symbolFont) {
26597
- 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
+ );
26598
27447
  }
26599
27448
  if (style.hyperlink && resolveHyperlinkRelationshipId) {
26600
27449
  const hyperlinkTarget = String(style.hyperlink).trim();
@@ -26669,14 +27518,15 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26669
27518
  lineSpacing: this.createLineSpacingXmlFromMultiplier(textStyle?.lineSpacing),
26670
27519
  lineSpacingExactPt: textStyle?.lineSpacingExactPt
26671
27520
  };
26672
- const createParagraph = (runs, bulletInfo) => {
27521
+ const createParagraph = (runs, bulletInfo, level, endParaRunProperties) => {
26673
27522
  const paragraphProps = buildParagraphPropertiesXml(
26674
27523
  textStyle,
26675
27524
  paragraphAlign,
26676
27525
  bulletInfo,
26677
- spacing
27526
+ spacing,
27527
+ level
26678
27528
  );
26679
- return assembleParagraphXml(runs, paragraphProps);
27529
+ return assembleParagraphXml(runs, paragraphProps, endParaRunProperties);
26680
27530
  };
26681
27531
  const createRun = (runText, style) => ({
26682
27532
  "a:rPr": this.createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId),
@@ -26724,13 +27574,19 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26724
27574
  const paragraphs = [];
26725
27575
  let currentRuns = [];
26726
27576
  let currentBulletInfo;
27577
+ let currentLevel;
27578
+ let currentEndParaRunProperties;
26727
27579
  const pushParagraph = () => {
26728
27580
  if (currentRuns.length === 0) {
26729
27581
  currentRuns.push(createRun("", textStyle));
26730
27582
  }
26731
- paragraphs.push(createParagraph(currentRuns, currentBulletInfo));
27583
+ paragraphs.push(
27584
+ createParagraph(currentRuns, currentBulletInfo, currentLevel, currentEndParaRunProperties)
27585
+ );
26732
27586
  currentRuns = [];
26733
27587
  currentBulletInfo = void 0;
27588
+ currentLevel = void 0;
27589
+ currentEndParaRunProperties = void 0;
26734
27590
  };
26735
27591
  if (textSegments && textSegments.length > 0) {
26736
27592
  const uniformSegmentOverrides = computeUniformSegmentOverrides(textStyle, textSegments);
@@ -26742,8 +27598,16 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26742
27598
  };
26743
27599
  const segmentText = String(segment.text ?? "");
26744
27600
  const lineParts = segmentText.split("\n");
26745
- if (currentRuns.length === 0 && segment.bulletInfo) {
26746
- 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
+ }
26747
27611
  }
26748
27612
  lineParts.forEach((linePart, lineIndex) => {
26749
27613
  if (segment.rubyText !== void 0) {
@@ -26841,6 +27705,9 @@ var PptxHandlerRuntime14 = class extends PptxHandlerRuntime13 {
26841
27705
  };
26842
27706
 
26843
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";
26844
27711
  var PptxHandlerRuntime15 = class _PptxHandlerRuntime extends PptxHandlerRuntime14 {
26845
27712
  /**
26846
27713
  * Build a `p:graphicFrame` XML skeleton for an SDK-created table.
@@ -26891,6 +27758,148 @@ var PptxHandlerRuntime15 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26891
27758
  }
26892
27759
  };
26893
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;
26894
27903
  /**
26895
27904
  * Build a p:sp XML object for an ink annotation element.
26896
27905
  * Each ink path becomes a separate a:path within a:pathLst,
@@ -27207,6 +28216,9 @@ var PptxHandlerRuntime16 = class extends PptxHandlerRuntime15 {
27207
28216
  buildOuterShadowXml(shapeStyle) {
27208
28217
  return this.colorStyleCodec.buildOuterShadowXml(shapeStyle);
27209
28218
  }
28219
+ buildPresetShadowXml(shapeStyle) {
28220
+ return this.colorStyleCodec.buildPresetShadowXml(shapeStyle);
28221
+ }
27210
28222
  buildInnerShadowXml(shapeStyle) {
27211
28223
  return this.colorStyleCodec.buildInnerShadowXml(shapeStyle);
27212
28224
  }
@@ -27757,6 +28769,10 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27757
28769
  const solidFill = runProperties["a:solidFill"];
27758
28770
  if (solidFill) {
27759
28771
  style.color = this.parseColor(solidFill);
28772
+ const colorXml = extractColorChoiceXml(solidFill);
28773
+ if (colorXml) {
28774
+ style.colorXml = colorXml;
28775
+ }
27760
28776
  }
27761
28777
  this.applyHyperlinkStyle(style, runProperties, relationshipMap);
27762
28778
  const capAttr = String(runProperties["@_cap"] || "").trim().toLowerCase();
@@ -27804,12 +28820,86 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27804
28820
  if (bmk) {
27805
28821
  style.bookmark = bmk;
27806
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");
27807
28837
  const runEffectList = runProperties["a:effectLst"];
27808
28838
  if (runEffectList) {
27809
28839
  this.applyTextRunEffects(style, runEffectList);
27810
28840
  }
27811
28841
  return style;
27812
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
+ }
27813
28903
  };
27814
28904
 
27815
28905
  // src/core/core/runtime/PptxHandlerRuntimeTextEditing.ts
@@ -28245,7 +29335,7 @@ var PptxHandlerRuntime21 = class extends PptxHandlerRuntime20 {
28245
29335
  };
28246
29336
 
28247
29337
  // src/core/core/runtime/table-cell-save-helpers.ts
28248
- function writeCellFill(tcPr, style) {
29338
+ function writeCellFill(tcPr, style, resolveColorXml) {
28249
29339
  if (style.fillMode === "gradient" && style.gradientFillStops && style.gradientFillStops.length > 0) {
28250
29340
  delete tcPr["a:solidFill"];
28251
29341
  delete tcPr["a:pattFill"];
@@ -28324,11 +29414,12 @@ function writeCellFill(tcPr, style) {
28324
29414
  } else if (style.backgroundColor) {
28325
29415
  delete tcPr["a:gradFill"];
28326
29416
  delete tcPr["a:pattFill"];
28327
- tcPr["a:solidFill"] = {
28328
- "a:srgbClr": {
28329
- "@_val": style.backgroundColor.replace("#", "")
28330
- }
28331
- };
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
+ );
28332
29423
  }
28333
29424
  }
28334
29425
  function writeDiagonalBorders(tcPr, style, emuPerPx) {
@@ -28459,7 +29550,7 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28459
29550
  xmlCell["a:tcPr"] = {};
28460
29551
  }
28461
29552
  const tcPr = xmlCell["a:tcPr"];
28462
- writeCellFill(tcPr, style);
29553
+ writeCellFill(tcPr, style, (colorXml) => this.parseColor(colorXml));
28463
29554
  if (style.vAlign) {
28464
29555
  const vAlignMap = {
28465
29556
  top: "t",
@@ -28541,35 +29632,29 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28541
29632
  }
28542
29633
  }
28543
29634
  }
28544
- if (style.marginLeft !== void 0 || style.marginRight !== void 0 || style.marginTop !== void 0 || style.marginBottom !== void 0) {
28545
- const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
28546
- if (!tcPr["a:tcMar"]) {
28547
- tcPr["a:tcMar"] = {};
28548
- }
28549
- const tcMar = tcPr["a:tcMar"];
28550
- if (style.marginLeft !== void 0) {
28551
- tcMar["a:marL"] = {
28552
- "@_w": String(Math.round(style.marginLeft * emuPerPx))
28553
- };
28554
- }
28555
- if (style.marginRight !== void 0) {
28556
- tcMar["a:marR"] = {
28557
- "@_w": String(Math.round(style.marginRight * emuPerPx))
28558
- };
28559
- }
28560
- if (style.marginTop !== void 0) {
28561
- tcMar["a:marT"] = {
28562
- "@_w": String(Math.round(style.marginTop * emuPerPx))
28563
- };
28564
- }
28565
- if (style.marginBottom !== void 0) {
28566
- tcMar["a:marB"] = {
28567
- "@_w": String(Math.round(style.marginBottom * emuPerPx))
28568
- };
28569
- }
29635
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
29636
+ if (style.marginLeft !== void 0) {
29637
+ tcPr["@_marL"] = String(Math.round(style.marginLeft * emuPerPx));
28570
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));
29647
+ }
29648
+ delete tcPr["a:tcMar"];
28571
29649
  writeDiagonalBorders(tcPr, style, _PptxHandlerRuntime.EMU_PER_PX);
28572
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
+ }
28573
29658
  }
28574
29659
  };
28575
29660
 
@@ -28673,6 +29758,49 @@ function createDefaultXmlCell() {
28673
29758
  // src/core/core/runtime/table-xml-rebuild.ts
28674
29759
  var GRID_COL_ID_EXT_URI = "{9D8B030D-6E8A-4147-A177-3AD203B41FA5}";
28675
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
+ }
28676
29804
  function randomColumnId() {
28677
29805
  return String(Math.floor(Math.random() * 4294967295));
28678
29806
  }
@@ -28703,8 +29831,10 @@ function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
28703
29831
  "a:extLst": {
28704
29832
  "a:ext": {
28705
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.
28706
29837
  "a16:colId": {
28707
- "@_xmlns:a16": A16_NAMESPACE,
28708
29838
  "@_val": existingColIds[i] || randomColumnId()
28709
29839
  }
28710
29840
  }
@@ -29063,26 +30193,64 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
29063
30193
  continue;
29064
30194
  }
29065
30195
  const scalingNode = this.xmlLookupService.getChildByLocalName(axisNode, "scaling");
29066
- if (!scalingNode) {
29067
- 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
+ );
29068
30226
  }
29069
- if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
29070
- const logBaseKey = Object.keys(scalingNode).find(
29071
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30227
+ if (matchingAxis.numFmt) {
30228
+ const numFmtKey = Object.keys(axisNode).find(
30229
+ (k) => this.compatibilityService.getXmlLocalName(k) === "numFmt"
29072
30230
  );
29073
- if (logBaseKey) {
29074
- 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;
29075
30237
  } else {
29076
- scalingNode["c:logBase"] = {
29077
- "@_val": String(matchingAxis.logBase)
29078
- };
30238
+ axisNode["c:numFmt"] = numFmtAttrs;
29079
30239
  }
29080
- } else if (matchingAxis.logScale === false) {
29081
- const logBaseKey = Object.keys(scalingNode).find(
29082
- (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"
29083
30249
  );
29084
- if (logBaseKey) {
29085
- delete scalingNode[logBaseKey];
30250
+ if (tickLblKey) {
30251
+ axisNode[tickLblKey]["@_val"] = matchingAxis.tickLblPos;
30252
+ } else {
30253
+ axisNode["c:tickLblPos"] = { "@_val": matchingAxis.tickLblPos };
29086
30254
  }
29087
30255
  }
29088
30256
  }
@@ -29095,6 +30263,18 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
29095
30263
  }
29096
30264
  this.pendingChartUpdates = void 0;
29097
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
+ }
29098
30278
  /**
29099
30279
  * Update the cached point values in a chart reference node
29100
30280
  * (numRef/strRef or numLit/strLit).
@@ -30374,17 +31554,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30374
31554
  delete spPr["a:noFill"];
30375
31555
  delete spPr["a:gradFill"];
30376
31556
  delete spPr["a:blipFill"];
30377
- const solidFill = {
30378
- "a:srgbClr": {
30379
- "@_val": fillColor.replace("#", "")
30380
- }
30381
- };
30382
- if (typeof shapeStyle.fillOpacity === "number" && shapeStyle.fillOpacity >= 0 && shapeStyle.fillOpacity < 1) {
30383
- solidFill["a:srgbClr"]["a:alpha"] = {
30384
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.fillOpacity) * 1e5))
30385
- };
30386
- }
30387
- 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
+ );
30388
31564
  }
30389
31565
  }
30390
31566
  if (shapeStyle.strokeColor !== void 0) {
@@ -30399,17 +31575,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30399
31575
  delete lineNode["a:solidFill"];
30400
31576
  } else {
30401
31577
  delete lineNode["a:noFill"];
30402
- const lineFill = {
30403
- "a:srgbClr": {
30404
- "@_val": shapeStyle.strokeColor.replace("#", "")
30405
- }
30406
- };
30407
- if (typeof shapeStyle.strokeOpacity === "number" && shapeStyle.strokeOpacity >= 0 && shapeStyle.strokeOpacity < 1) {
30408
- lineFill["a:srgbClr"]["a:alpha"] = {
30409
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.strokeOpacity) * 1e5))
30410
- };
30411
- }
30412
- 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
+ );
30413
31585
  }
30414
31586
  }
30415
31587
  if (shapeStyle.strokeDash !== void 0) {
@@ -30490,7 +31662,11 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30490
31662
  } else if (shapeStyle.lineJoin === "bevel") {
30491
31663
  lineNode["a:bevel"] = {};
30492
31664
  } else if (shapeStyle.lineJoin === "miter") {
30493
- 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;
30494
31670
  }
30495
31671
  }
30496
31672
  if (shapeStyle.lineCap !== void 0) {
@@ -30510,6 +31686,82 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30510
31686
  spPr["a:ln"]["a:effectLst"] = lineEffectListXml;
30511
31687
  }
30512
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
+ }
30513
31765
  };
30514
31766
 
30515
31767
  // src/core/core/runtime/PptxHandlerRuntimeSaveEffectsWriter.ts
@@ -30519,17 +31771,22 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30519
31771
  * effectDag, 3D scene, and 3D shape properties to the given spPr XML object.
30520
31772
  */
30521
31773
  applyEffectsAndThreeD(spPr, shapeStyle) {
30522
- const outerShadowXml = this.buildOuterShadowXml(shapeStyle);
31774
+ const presetShadowXml = shapeStyle.presetShadowName ? this.buildPresetShadowXml(shapeStyle) : void 0;
31775
+ const outerShadowXml = presetShadowXml ? void 0 : this.buildOuterShadowXml(shapeStyle);
30523
31776
  const innerShadowXml = this.buildInnerShadowXml(shapeStyle);
30524
31777
  const glowXml = this.buildGlowXml(shapeStyle);
30525
31778
  const softEdgeXml = this.buildSoftEdgeXml(shapeStyle);
30526
31779
  const reflectionXml = this.buildReflectionXml(shapeStyle);
30527
31780
  const blurXml = this.buildBlurXml(shapeStyle);
30528
- const hasAnyEffect = outerShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
31781
+ const hasAnyEffect = outerShadowXml || presetShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
30529
31782
  if (hasAnyEffect) {
30530
31783
  const effectList = spPr["a:effectLst"] || {};
30531
- if (outerShadowXml) {
31784
+ if (presetShadowXml) {
31785
+ effectList["a:prstShdw"] = presetShadowXml;
31786
+ delete effectList["a:outerShdw"];
31787
+ } else if (outerShadowXml) {
30532
31788
  effectList["a:outerShdw"] = outerShadowXml;
31789
+ delete effectList["a:prstShdw"];
30533
31790
  }
30534
31791
  if (innerShadowXml) {
30535
31792
  effectList["a:innerShdw"] = innerShadowXml;
@@ -30546,12 +31803,13 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30546
31803
  if (blurXml) {
30547
31804
  effectList["a:blur"] = blurXml;
30548
31805
  }
30549
- spPr["a:effectLst"] = effectList;
31806
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30550
31807
  } else {
30551
31808
  const effectList = spPr["a:effectLst"];
30552
31809
  if (effectList) {
30553
- if (shapeStyle.shadowColor !== void 0 && !outerShadowXml) {
31810
+ if (shapeStyle.shadowColor !== void 0 && !outerShadowXml && !presetShadowXml) {
30554
31811
  delete effectList["a:outerShdw"];
31812
+ delete effectList["a:prstShdw"];
30555
31813
  }
30556
31814
  if (shapeStyle.innerShadowColor !== void 0 && !innerShadowXml) {
30557
31815
  delete effectList["a:innerShdw"];
@@ -30570,6 +31828,8 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30570
31828
  }
30571
31829
  if (Object.keys(effectList).length === 0) {
30572
31830
  delete spPr["a:effectLst"];
31831
+ } else {
31832
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30573
31833
  }
30574
31834
  }
30575
31835
  }
@@ -30730,7 +31990,8 @@ var PptxHandlerRuntime30 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30730
31990
  );
30731
31991
  }
30732
31992
  if (el.textStyle?.hOverflow) {
30733
- bodyPr["@_hOverflow"] = el.textStyle.hOverflow;
31993
+ bodyPr["@_horzOverflow"] = el.textStyle.hOverflow;
31994
+ delete bodyPr["@_hOverflow"];
30734
31995
  }
30735
31996
  if (el.textStyle?.vertOverflow) {
30736
31997
  bodyPr["@_vertOverflow"] = el.textStyle.vertOverflow;
@@ -30961,7 +32222,10 @@ var PptxHandlerRuntime31 = class extends PptxHandlerRuntime30 {
30961
32222
  const elWithPaths = el;
30962
32223
  if (elWithPaths.customGeometryPaths && elWithPaths.customGeometryPaths.length > 0) {
30963
32224
  delete spPr["a:prstGeom"];
30964
- spPr["a:custGeom"] = customGeometryPathsToXml(elWithPaths.customGeometryPaths);
32225
+ spPr["a:custGeom"] = customGeometryPathsToXml(
32226
+ elWithPaths.customGeometryPaths,
32227
+ elWithPaths.customGeometryRawData
32228
+ );
30965
32229
  } else if (spPr["a:prstGeom"]) {
30966
32230
  const presetGeometry = el.type === "connector" ? this.normalizePresetGeometry(el.shapeType || "straightConnector1") : this.normalizePresetGeometry(el.shapeType);
30967
32231
  const prstGeom = spPr["a:prstGeom"];
@@ -31086,6 +32350,42 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31086
32350
  isGraphicFrameShape(shape) {
31087
32351
  return Boolean(shape["p:nvGraphicFramePr"] || shape["a:graphic"] && shape["p:xfrm"]);
31088
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
+ }
31089
32389
  /** Whether an element ID indicates a template (layout/master) element. */
31090
32390
  isTemplateElementId(elementId) {
31091
32391
  return elementId.startsWith("layout-") || elementId.startsWith("master-");
@@ -31109,18 +32409,51 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31109
32409
  }
31110
32410
  return;
31111
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
+ }
31112
32427
  if (!shape && (el.type === "text" || el.type === "shape")) {
31113
32428
  shape = this.createElementXml(el);
31114
32429
  }
31115
32430
  if (!shape && el.type === "connector") {
31116
32431
  shape = this.createConnectorXml(el);
31117
32432
  }
31118
- if (!shape && el.type === "ink") {
31119
- 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
+ }
31120
32444
  }
31121
32445
  if (!shape && el.type === "table") {
31122
32446
  shape = this.createTableGraphicFrameXml(el);
31123
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
+ }
32456
+ }
31124
32457
  if (!shape) {
31125
32458
  this.compatibilityService.reportWarning({
31126
32459
  code: "SAVE_ELEMENT_SKIPPED",
@@ -31133,11 +32466,14 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31133
32466
  }
31134
32467
  this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
31135
32468
  this.applyImageProperties(shape, el);
32469
+ this.finalizePictureBlipFillOrder(shape);
31136
32470
  this.applyGeometryUpdate(shape, el);
31137
32471
  if (hasShapeProperties(el) && el.shapeStyle && shape["p:spPr"]) {
31138
32472
  const spPr = shape["p:spPr"];
31139
32473
  this.applyFillAndStroke(spPr, el.shapeStyle);
31140
32474
  this.applyEffectsAndThreeD(spPr, el.shapeStyle);
32475
+ this.finalizeSpPrSchemaOrder(shape);
32476
+ this.applyShapeStyleRefs(shape, el.shapeStyle);
31141
32477
  }
31142
32478
  if (hasTextProperties(el)) {
31143
32479
  this.applyTextBodyContent(
@@ -31295,7 +32631,8 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31295
32631
  connectors: [],
31296
32632
  graphicFrames: [],
31297
32633
  groups: [],
31298
- model3ds: []
32634
+ model3ds: [],
32635
+ contentParts: []
31299
32636
  };
31300
32637
  const ctx = {
31301
32638
  slide,
@@ -31327,6 +32664,12 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31327
32664
  } else {
31328
32665
  delete spTree["p16:model3D"];
31329
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);
31330
32673
  const reassigned = shapeIdValidator.validateAndDeduplicateIds(
31331
32674
  spTree,
31332
32675
  (v) => this.ensureArray(v)
@@ -31344,12 +32687,426 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31344
32687
  this.zip.file(slideRelsPath, this.builder.build(slideRelsData));
31345
32688
  this.applySlideDrawingGuides(slideNode, slide);
31346
32689
  this.deduplicateExtensionLists(xmlObj);
32690
+ if (slideContainsA16Element(slideNode)) {
32691
+ ensureA16NamespaceOnSlideRoot(slideNode);
32692
+ }
31347
32693
  this.zip.file(slide.id, this.builder.build(xmlObj));
31348
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
+ }
31349
33106
  };
31350
33107
 
31351
33108
  // src/core/core/runtime/PptxHandlerRuntimeSavePipeline.ts
31352
- var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime33 {
33109
+ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
31353
33110
  /**
31354
33111
  * Resolve the effective conformance class for this save operation.
31355
33112
  *
@@ -31436,6 +33193,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31436
33193
  for (const [masterPath, masterXmlObj] of this.masterXmlMap.entries()) {
31437
33194
  this.zip.file(masterPath, this.builder.build(masterXmlObj));
31438
33195
  }
33196
+ await this.persistThemeParts();
31439
33197
  await this.applyEmbeddedFontPreservation(options?.embeddedFonts);
31440
33198
  if (this.presentationData) {
31441
33199
  this.presentationSaveBuilder.applySaveOptions({
@@ -31525,7 +33283,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31525
33283
  };
31526
33284
 
31527
33285
  // src/core/core/runtime/PptxHandlerRuntimeElementParsing.ts
31528
- var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
33286
+ var PptxHandlerRuntime36 = class _PptxHandlerRuntime extends PptxHandlerRuntime35 {
31529
33287
  /**
31530
33288
  * Parse media data (video/audio path and MIME type) from graphic frame data.
31531
33289
  */
@@ -31747,7 +33505,7 @@ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31747
33505
  };
31748
33506
 
31749
33507
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderLookup.ts
31750
- var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
33508
+ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31751
33509
  findPlaceholderInShapeTree(spTree, expected) {
31752
33510
  if (!spTree) {
31753
33511
  return void 0;
@@ -31900,7 +33658,7 @@ var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
31900
33658
  };
31901
33659
 
31902
33660
  // src/core/core/runtime/PptxHandlerRuntimeGeometryParsing.ts
31903
- var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
33661
+ var PptxHandlerRuntime38 = class extends PptxHandlerRuntime37 {
31904
33662
  parseGeometryAdjustments(prstGeom) {
31905
33663
  if (!prstGeom) {
31906
33664
  return void 0;
@@ -32009,6 +33767,103 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
32009
33767
  }
32010
33768
  return handles.length > 0 ? handles : void 0;
32011
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
+ }
32012
33867
  parseCustomGeometry(custGeom, shapeWidth, shapeHeight) {
32013
33868
  if (!custGeom || !custGeom["a:pathLst"] || !custGeom["a:pathLst"]?.["a:path"]) {
32014
33869
  return null;
@@ -32077,7 +33932,7 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
32077
33932
  };
32078
33933
 
32079
33934
  // src/core/core/runtime/PptxHandlerRuntimeShapeImageFill.ts
32080
- var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime37 {
33935
+ var PptxHandlerRuntime39 = class _PptxHandlerRuntime extends PptxHandlerRuntime38 {
32081
33936
  /**
32082
33937
  * Parse a shape that has an image fill (a:blipFill inside spPr)
32083
33938
  * This handles shapes like rectangles filled with images (e.g., wood texture backgrounds)
@@ -32277,7 +34132,7 @@ var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
32277
34132
  };
32278
34133
 
32279
34134
  // src/core/core/runtime/PptxHandlerRuntimeTextDefaults.ts
32280
- var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
34135
+ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32281
34136
  /**
32282
34137
  * Apply {@link PlaceholderDefaults} body-level properties to a
32283
34138
  * {@link TextStyle} as fallback values (only sets fields that are
@@ -32401,7 +34256,7 @@ var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
32401
34256
  };
32402
34257
 
32403
34258
  // src/core/core/runtime/PptxHandlerRuntimeBulletParsing.ts
32404
- var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34259
+ var PptxHandlerRuntime41 = class extends PptxHandlerRuntime40 {
32405
34260
  resolveParagraphBulletInfo(paragraph, paragraphIndex, txBody, inheritedTxBody, isBodyPlaceholder = false, slidePath) {
32406
34261
  if (!paragraph) {
32407
34262
  return null;
@@ -32432,7 +34287,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32432
34287
  if (candidate["a:buNone"]) {
32433
34288
  return { none: true };
32434
34289
  }
32435
- 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) {
32436
34291
  resolvedBulletProps = candidate;
32437
34292
  break;
32438
34293
  }
@@ -32446,6 +34301,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32446
34301
  }
32447
34302
  const buFont = resolvedBulletProps["a:buFont"];
32448
34303
  const fontFamily = buFont?.["@_typeface"] ? String(buFont["@_typeface"]) : void 0;
34304
+ const fontInherit = resolvedBulletProps["a:buFontTx"] !== void 0;
32449
34305
  const buSzPct = resolvedBulletProps["a:buSzPct"];
32450
34306
  let sizePercent;
32451
34307
  if (buSzPct?.["@_val"] !== void 0) {
@@ -32462,6 +34318,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32462
34318
  sizePts = ptsRaw / 100;
32463
34319
  }
32464
34320
  }
34321
+ const sizeInherit = resolvedBulletProps["a:buSzTx"] !== void 0;
32465
34322
  const buClr = resolvedBulletProps["a:buClr"];
32466
34323
  let color;
32467
34324
  if (buClr) {
@@ -32470,6 +34327,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32470
34327
  color = String(srgb["@_val"]);
32471
34328
  }
32472
34329
  }
34330
+ const colorInherit = resolvedBulletProps["a:buClrTx"] !== void 0;
32473
34331
  const bulletChar = String(
32474
34332
  resolvedBulletProps["a:buChar"]?.["@_char"] || ""
32475
34333
  );
@@ -32479,7 +34337,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32479
34337
  fontFamily,
32480
34338
  sizePercent,
32481
34339
  sizePts,
32482
- color
34340
+ color,
34341
+ ...fontInherit ? { fontInherit: true } : {},
34342
+ ...colorInherit ? { colorInherit: true } : {},
34343
+ ...sizeInherit ? { sizeInherit: true } : {}
32483
34344
  };
32484
34345
  }
32485
34346
  const autoNum = resolvedBulletProps["a:buAutoNum"];
@@ -32494,7 +34355,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32494
34355
  fontFamily,
32495
34356
  sizePercent,
32496
34357
  sizePts,
32497
- color
34358
+ color,
34359
+ ...fontInherit ? { fontInherit: true } : {},
34360
+ ...colorInherit ? { colorInherit: true } : {},
34361
+ ...sizeInherit ? { sizeInherit: true } : {}
32498
34362
  };
32499
34363
  }
32500
34364
  const buBlip = resolvedBulletProps["a:buBlip"];
@@ -32606,7 +34470,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32606
34470
  };
32607
34471
 
32608
34472
  // src/core/core/runtime/PptxHandlerRuntimeShapeBodyParsing.ts
32609
- var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime40 {
34473
+ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
32610
34474
  /**
32611
34475
  * Parse `a:spLocks` attributes into a structured {@link PptxShapeLocks} object.
32612
34476
  * Returns `undefined` when the node is absent or contains no lock attributes.
@@ -32701,7 +34565,8 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32701
34565
  if (Number.isFinite(spcColRaw) && spcColRaw > 0) {
32702
34566
  textStyle.columnSpacing = spcColRaw / _PptxHandlerRuntime.EMU_PER_PX;
32703
34567
  }
32704
- const hOverflow = String(bodyPr["@_hOverflow"] || "").trim();
34568
+ const hOverflowRaw = bodyPr["@_horzOverflow"] ?? bodyPr["@_hOverflow"];
34569
+ const hOverflow = String(hOverflowRaw || "").trim();
32705
34570
  if (hOverflow === "overflow" || hOverflow === "clip") {
32706
34571
  textStyle.hOverflow = hOverflow;
32707
34572
  }
@@ -32884,7 +34749,7 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32884
34749
  };
32885
34750
 
32886
34751
  // src/core/core/runtime/PptxHandlerRuntimeShapeTextParsing.ts
32887
- var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
34752
+ var PptxHandlerRuntime43 = class _PptxHandlerRuntime extends PptxHandlerRuntime42 {
32888
34753
  /**
32889
34754
  * Resolve paragraph-level styles (alignment, spacing, margins, tabs,
32890
34755
  * level styles) for a single paragraph. Modifies `textStyle` in place
@@ -33066,7 +34931,7 @@ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33066
34931
  };
33067
34932
 
33068
34933
  // src/core/core/runtime/PptxHandlerRuntimeShapeParagraphContentParsing.ts
33069
- var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
34934
+ var PptxHandlerRuntime44 = class extends PptxHandlerRuntime43 {
33070
34935
  /**
33071
34936
  * Collect text content (runs, fields, equations, bullets) for a single
33072
34937
  * paragraph and return text parts + segments. The returned `seedStyle`
@@ -33249,6 +35114,23 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33249
35114
  parts.push("\n");
33250
35115
  segments.push({ text: "\n", style: { ...mergedDefaultRunStyle } });
33251
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
+ }
33252
35134
  return { parts, segments, seedStyle };
33253
35135
  }
33254
35136
  /**
@@ -33358,7 +35240,7 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33358
35240
  };
33359
35241
 
33360
35242
  // src/core/core/runtime/PptxHandlerRuntimeShapeParsing.ts
33361
- var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime43 {
35243
+ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
33362
35244
  parseShape(shape, id, slidePath) {
33363
35245
  try {
33364
35246
  const spPr = shape["p:spPr"];
@@ -33394,6 +35276,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33394
35276
  let pathData;
33395
35277
  let pathWidth;
33396
35278
  let pathHeight;
35279
+ let customGeometryRawData;
33397
35280
  const custGeom = effectiveSpPr?.["a:custGeom"];
33398
35281
  if (custGeom) {
33399
35282
  const customPath = this.parseCustomGeometry(
@@ -33406,6 +35289,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33406
35289
  pathData = customPath.pathData;
33407
35290
  pathWidth = customPath.pathWidth;
33408
35291
  pathHeight = customPath.pathHeight;
35292
+ customGeometryRawData = this.extractCustomGeometryRawData(custGeom);
33409
35293
  }
33410
35294
  }
33411
35295
  const geomNode = custGeom ?? effectiveSpPr?.["a:prstGeom"];
@@ -33563,7 +35447,8 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33563
35447
  type: "shape",
33564
35448
  pathData,
33565
35449
  pathWidth,
33566
- pathHeight
35450
+ pathHeight,
35451
+ customGeometryRawData
33567
35452
  };
33568
35453
  } catch (e) {
33569
35454
  console.warn(`[pptx] Skipping shape element (${id}):`, e);
@@ -33573,7 +35458,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33573
35458
  };
33574
35459
 
33575
35460
  // src/core/core/runtime/PptxHandlerRuntimePictureParsing.ts
33576
- var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
35461
+ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
33577
35462
  async parsePicture(pic, id, slidePath) {
33578
35463
  try {
33579
35464
  const spPr = pic["p:spPr"];
@@ -33812,7 +35697,7 @@ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33812
35697
  };
33813
35698
 
33814
35699
  // src/core/core/runtime/PptxHandlerRuntimeSpTreeParsing.ts
33815
- var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
35700
+ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
33816
35701
  /**
33817
35702
  * Known element tag names that appear as direct children of `p:spTree`
33818
35703
  * (or `p:grpSp`) and represent renderable shapes/objects.
@@ -34157,9 +36042,18 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34157
36042
  * Unwrap mc:AlternateContent elements within a shape tree (or group),
34158
36043
  * merging selected branch children into the parent element arrays.
34159
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).
34160
36049
  */
34161
36050
  unwrapAlternateContent(container) {
34162
- 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
+ }
34163
36057
  }
34164
36058
  /**
34165
36059
  * Forward declaration – implemented in PptxHandlerRuntimeGroupParsing.
@@ -34170,7 +36064,7 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34170
36064
  };
34171
36065
 
34172
36066
  // src/core/core/runtime/PptxHandlerRuntimeGroupParsing.ts
34173
- var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
36067
+ var PptxHandlerRuntime48 = class _PptxHandlerRuntime extends PptxHandlerRuntime47 {
34174
36068
  async parseGroupShape(group, baseId, slidePath, rawXmlStr) {
34175
36069
  const grpSpPr = group["p:grpSpPr"];
34176
36070
  const xfrm = grpSpPr?.["a:xfrm"];
@@ -34343,7 +36237,7 @@ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34343
36237
  };
34344
36238
 
34345
36239
  // src/core/core/runtime/PptxHandlerRuntimeSlideParsing.ts
34346
- var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
36240
+ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34347
36241
  /**
34348
36242
  * Parse text body from a connector shape (p:cxnSp > p:txBody).
34349
36243
  * Uses the same text run extraction logic as regular shapes but
@@ -34455,7 +36349,7 @@ var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
34455
36349
  };
34456
36350
 
34457
36351
  // src/core/core/runtime/PptxHandlerRuntimeColorAndEffects.ts
34458
- var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
36352
+ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34459
36353
  /**
34460
36354
  * Forward declaration – implemented in PptxHandlerRuntimeThemeProcessing.
34461
36355
  * Re-resolves gradient stops by substituting `phClr` with the given colour.
@@ -34496,8 +36390,9 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34496
36390
  return void 0;
34497
36391
  }
34498
36392
  const resolvedKey = normalized === "phclr" ? "accent1" : normalized;
34499
- if (this.currentSlideClrMapOverride) {
34500
- const remapped = this.currentSlideClrMapOverride[resolvedKey];
36393
+ const overrideMap = this.currentSlideClrMapOverride ?? this.currentMasterClrMap;
36394
+ if (overrideMap) {
36395
+ const remapped = overrideMap[resolvedKey];
34501
36396
  if (remapped) {
34502
36397
  return this.themeColorMap[remapped] || this.getDefaultSchemeColorMap()[remapped];
34503
36398
  }
@@ -34580,7 +36475,7 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34580
36475
  };
34581
36476
 
34582
36477
  // src/core/core/runtime/PptxHandlerRuntimeBackgroundParsing.ts
34583
- var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
36478
+ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34584
36479
  async extractBackgroundImage(slideXml2, slidePath, rootElement = "p:sld") {
34585
36480
  try {
34586
36481
  const bg = slideXml2[rootElement]?.["p:cSld"]?.["p:bg"];
@@ -34791,7 +36686,7 @@ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34791
36686
  };
34792
36687
 
34793
36688
  // src/core/core/runtime/PptxHandlerRuntimeSlideUtils.ts
34794
- var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
36689
+ var PptxHandlerRuntime52 = class extends PptxHandlerRuntime51 {
34795
36690
  /**
34796
36691
  * Retrieve the background gradient from a layout, falling back to master.
34797
36692
  */
@@ -34862,6 +36757,64 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34862
36757
  }
34863
36758
  return void 0;
34864
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
+ }
34865
36818
  /**
34866
36819
  * Extract the `p:bg/@showAnimation` flag from a slide's XML.
34867
36820
  * Returns `true` when the background should animate, `false` when
@@ -35002,7 +36955,7 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
35002
36955
  };
35003
36956
 
35004
36957
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderStyles.ts
35005
- var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime51 {
36958
+ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
35006
36959
  /**
35007
36960
  * Parse a single `a:lvlXpPr` node into a structured
35008
36961
  * {@link PlaceholderTextLevelStyle}.
@@ -35123,7 +37076,7 @@ var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35123
37076
  };
35124
37077
 
35125
37078
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderDefaults.ts
35126
- var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
37079
+ var PptxHandlerRuntime54 = class _PptxHandlerRuntime extends PptxHandlerRuntime53 {
35127
37080
  /**
35128
37081
  * Extract structured {@link PlaceholderDefaults} from a layout or master
35129
37082
  * shape that carries a `p:ph` element.
@@ -35266,7 +37219,118 @@ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35266
37219
  };
35267
37220
 
35268
37221
  // src/core/core/runtime/PptxHandlerRuntimeMasterElements.ts
35269
- 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
+ }
35270
37334
  parsePresentationDefaultTextStyle() {
35271
37335
  const presentation = this.presentationData?.["p:presentation"];
35272
37336
  const defaultTextStyle = presentation?.["p:defaultTextStyle"];
@@ -35422,7 +37486,7 @@ var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
35422
37486
  };
35423
37487
 
35424
37488
  // src/core/core/runtime/PptxHandlerRuntimeLayoutElements.ts
35425
- var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37489
+ var PptxHandlerRuntime56 = class extends PptxHandlerRuntime55 {
35426
37490
  async getLayoutElements(slidePath) {
35427
37491
  const slideRels = this.slideRelsMap.get(slidePath);
35428
37492
  if (!slideRels) {
@@ -35546,10 +37610,10 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35546
37610
  }
35547
37611
  }
35548
37612
  }
35549
- this.currentSlideClrMapOverride = prevClrMapOverride;
35550
37613
  const layoutShowMasterSp = layoutXmlObj["p:sldLayout"]?.["@_showMasterSp"];
35551
37614
  const showMasterSp = layoutShowMasterSp === void 0 || String(layoutShowMasterSp).trim().toLowerCase() !== "0" && String(layoutShowMasterSp).trim().toLowerCase() !== "false";
35552
37615
  const masterElements = showMasterSp ? await this.getMasterElements(layoutPath) : [];
37616
+ this.currentSlideClrMapOverride = prevClrMapOverride;
35553
37617
  const allElements = [...masterElements, ...elements];
35554
37618
  this.layoutCache.set(layoutPath, allElements);
35555
37619
  return allElements;
@@ -35561,7 +37625,7 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35561
37625
  };
35562
37626
 
35563
37627
  // src/core/core/runtime/PptxHandlerRuntimeThemeFormatScheme.ts
35564
- var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime55 {
37628
+ var PptxHandlerRuntime57 = class _PptxHandlerRuntime extends PptxHandlerRuntime56 {
35565
37629
  /**
35566
37630
  * Collect fill-style children from a style list node, preserving
35567
37631
  * document order. Handles `a:solidFill`, `a:gradFill`, `a:pattFill`,
@@ -35803,7 +37867,7 @@ var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35803
37867
  };
35804
37868
 
35805
37869
  // src/core/core/runtime/PptxHandlerRuntimeThemeOverrides.ts
35806
- var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
37870
+ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
35807
37871
  /**
35808
37872
  * Parse the `a:fmtScheme` element from the theme into a structured
35809
37873
  * {@link PptxThemeFormatScheme}. Each sub-list (fillStyleLst, lnStyleLst,
@@ -36000,8 +38064,10 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
36000
38064
  }
36001
38065
  const fontScheme = root["a:fontScheme"];
36002
38066
  if (fontScheme) {
36003
- const majorLatin = fontScheme["a:majorFont"]?.["a:latin"];
36004
- 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"];
36005
38071
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
36006
38072
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
36007
38073
  if (!result.colorOverrides) {
@@ -36024,13 +38090,46 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
36024
38090
  };
36025
38091
 
36026
38092
  // src/core/core/runtime/PptxHandlerRuntimeThemeRefResolution.ts
36027
- 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
+ }
36028
38120
  /**
36029
38121
  * Resolve a `a:effectRef` element into concrete effect properties
36030
38122
  * by looking up `@_idx` in the theme format scheme's effect style list.
36031
38123
  */
36032
38124
  resolveThemeEffectRef(refNode, style) {
36033
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
+ }
36034
38133
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.effectStyles.length) {
36035
38134
  return;
36036
38135
  }
@@ -36083,6 +38182,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36083
38182
  resolveThemeLineRef(refNode, style) {
36084
38183
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
36085
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
+ }
36086
38192
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.lineStyles.length) {
36087
38193
  style.strokeColor = overrideColor;
36088
38194
  if (overrideColor) {
@@ -36154,6 +38260,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36154
38260
  */
36155
38261
  resolveThemeFillRef(refNode, style) {
36156
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
+ }
36157
38270
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme) {
36158
38271
  style.fillMode = "theme";
36159
38272
  style.fillColor = this.parseColor(refNode);
@@ -36219,7 +38332,7 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36219
38332
  };
36220
38333
 
36221
38334
  // src/core/core/runtime/PptxHandlerRuntimeThemeLoading.ts
36222
- var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38335
+ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36223
38336
  async resolvePrimaryThemePath() {
36224
38337
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36225
38338
  if (!masterFiles || masterFiles.length === 0) {
@@ -36329,62 +38442,97 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36329
38442
  formatScheme: this.themeFormatScheme
36330
38443
  };
36331
38444
  }
36332
- 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) {
36333
38461
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36334
38462
  if (!masterFiles || masterFiles.length === 0) {
36335
38463
  return;
36336
38464
  }
36337
- try {
36338
- const masterXml = await masterFiles[0].async("string");
36339
- const masterData = this.parser.parse(masterXml);
36340
- const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
36341
- if (!clrMap) {
36342
- return;
36343
- }
36344
- const aliasKeys = [
36345
- "bg1",
36346
- "tx1",
36347
- "bg2",
36348
- "tx2",
36349
- "accent1",
36350
- "accent2",
36351
- "accent3",
36352
- "accent4",
36353
- "accent5",
36354
- "accent6",
36355
- "hlink",
36356
- "folHlink"
36357
- ];
36358
- for (const aliasKey of aliasKeys) {
36359
- const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
36360
- 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) {
36361
38485
  continue;
36362
38486
  }
36363
- const mappedColor = this.themeColorMap[mappedKey] || defaultMap[mappedKey];
36364
- if (mappedColor) {
36365
- 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);
36366
38496
  }
38497
+ } catch (error) {
38498
+ console.warn(`Failed to parse slide master color map for ${file.name}:`, error);
36367
38499
  }
36368
- } catch (error) {
36369
- console.warn("Failed to parse slide master color map:", error);
36370
38500
  }
36371
38501
  }
36372
- async loadThemeData() {
36373
- const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
36374
- if (!themeFiles || themeFiles.length === 0) {
36375
- 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;
36376
38513
  }
36377
- const preferredThemePath = await this.resolvePrimaryThemePath();
36378
- const preferredThemeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) : void 0;
36379
- const themeFile = preferredThemeFile ?? themeFiles[0];
36380
38514
  const themeXml2 = await themeFile.async("string");
38515
+ this.originalThemeXmlByPath.set(themePath, themeXml2);
36381
38516
  const themeData = this.parser.parse(themeXml2);
36382
38517
  const themeRoot = themeData["a:theme"];
36383
38518
  const themeElements = themeRoot?.["a:themeElements"];
36384
38519
  const colorScheme = themeElements?.["a:clrScheme"];
36385
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
+ }
36386
38534
  const defaultMap = this.getDefaultSchemeColorMap();
36387
- this.themeColorMap = { ...defaultMap };
38535
+ const colorMap = { ...defaultMap };
36388
38536
  if (colorScheme) {
36389
38537
  const schemeKeys = [
36390
38538
  "dk1",
@@ -36404,39 +38552,196 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36404
38552
  const colorNode = colorScheme[`a:${key}`];
36405
38553
  const parsed = this.parseColorChoice(colorNode);
36406
38554
  if (parsed) {
36407
- this.themeColorMap[key] = parsed;
38555
+ colorMap[key] = parsed;
36408
38556
  }
36409
38557
  }
36410
38558
  }
36411
- this.themeColorMap["tx1"] = this.themeColorMap["dk1"] || defaultMap["dk1"];
36412
- this.themeColorMap["bg1"] = this.themeColorMap["lt1"] || defaultMap["lt1"];
36413
- this.themeColorMap["tx2"] = this.themeColorMap["dk2"] || defaultMap["dk2"];
36414
- this.themeColorMap["bg2"] = this.themeColorMap["lt2"] || defaultMap["lt2"];
36415
- await this.applySlideMasterColorMap(defaultMap);
36416
- const majorLatin = fontScheme?.["a:majorFont"]?.["a:latin"];
36417
- 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"];
36418
38567
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
36419
38568
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
36420
- this.themeFontMap = {};
38569
+ const fontMap = {};
36421
38570
  if (majorFont) {
36422
- this.themeFontMap["mj-lt"] = majorFont;
36423
- this.themeFontMap["mj-ea"] = majorFont;
36424
- this.themeFontMap["mj-cs"] = majorFont;
38571
+ fontMap["mj-lt"] = majorFont;
38572
+ fontMap["mj-ea"] = majorFont;
38573
+ fontMap["mj-cs"] = majorFont;
36425
38574
  }
36426
38575
  if (minorFont) {
36427
- this.themeFontMap["mn-lt"] = minorFont;
36428
- this.themeFontMap["mn-ea"] = minorFont;
36429
- this.themeFontMap["mn-cs"] = minorFont;
38576
+ fontMap["mn-lt"] = minorFont;
38577
+ fontMap["mn-ea"] = minorFont;
38578
+ fontMap["mn-cs"] = minorFont;
36430
38579
  }
36431
- const fmtScheme = themeElements?.["a:fmtScheme"];
36432
- if (fmtScheme) {
36433
- 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;
36434
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;
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;
36435
38740
  }
36436
38741
  };
36437
38742
 
36438
38743
  // src/core/core/runtime/PptxHandlerRuntimeThemeProcessing.ts
36439
- var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
38744
+ var PptxHandlerRuntime61 = class extends PptxHandlerRuntime60 {
36440
38745
  // ---------------------------------------------------------------------------
36441
38746
  // Theme editing — update colour scheme, font scheme, and name in the zip
36442
38747
  // ---------------------------------------------------------------------------
@@ -36586,7 +38891,7 @@ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36586
38891
  };
36587
38892
 
36588
38893
  // src/core/core/runtime/PptxHandlerRuntimeComments.ts
36589
- var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime60 {
38894
+ var PptxHandlerRuntime62 = class _PptxHandlerRuntime extends PptxHandlerRuntime61 {
36590
38895
  /**
36591
38896
  * Parse modern threaded comments (PowerPoint 2019+ / Office 365).
36592
38897
  * Modern comments use `p188:cmLst`/`p15:cmLst` roots within
@@ -36819,7 +39124,7 @@ var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
36819
39124
  };
36820
39125
 
36821
39126
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtXmlUtils.ts
36822
- var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
39127
+ var PptxHandlerRuntime63 = class extends PptxHandlerRuntime62 {
36823
39128
  async readXmlPartByRelationshipId(slidePath, relationshipId) {
36824
39129
  const normalizedRelationshipId = String(relationshipId || "").trim();
36825
39130
  if (normalizedRelationshipId.length === 0) {
@@ -36974,7 +39279,7 @@ var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
36974
39279
  };
36975
39280
 
36976
39281
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtParsing.ts
36977
- var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime62 {
39282
+ var PptxHandlerRuntime64 = class _PptxHandlerRuntime extends PptxHandlerRuntime63 {
36978
39283
  /**
36979
39284
  * Parse quick style from `ppt/diagrams/quickStyles*.xml`.
36980
39285
  */
@@ -37133,7 +39438,7 @@ var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
37133
39438
  };
37134
39439
 
37135
39440
  // src/core/core/runtime/PptxHandlerRuntimeSmartArt.ts
37136
- var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
39441
+ var PptxHandlerRuntime65 = class _PptxHandlerRuntime extends PptxHandlerRuntime64 {
37137
39442
  async getSmartArtDataForGraphicFrame(slidePath, graphicFrame) {
37138
39443
  const graphicData = this.xmlLookupService.getChildByLocalName(
37139
39444
  this.xmlLookupService.getChildByLocalName(graphicFrame, "graphic"),
@@ -37184,10 +39489,14 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37184
39489
  const layoutPart = layoutRelationshipId.length > 0 ? await this.readXmlPartByRelationshipId(slidePath, layoutRelationshipId) : void 0;
37185
39490
  const layoutType = layoutPart?.partPath?.split("/").pop()?.replace(/\.[^.]+$/, "") || void 0;
37186
39491
  const chrome = this.parseSmartArtChrome(dataModel);
37187
- const drawingRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37188
- const drawingShapes = await this.parseSmartArtDrawingShapes(slidePath, drawingRelationshipId);
37189
39492
  const colorsRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37190
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;
37191
39500
  const styleRelationshipId = String(relationshipIds["@_r:qs"] || "").trim();
37192
39501
  const quickStyle = await this.parseSmartArtQuickStyle(slidePath, styleRelationshipId);
37193
39502
  return {
@@ -37199,11 +39508,87 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37199
39508
  colorTransform,
37200
39509
  quickStyle,
37201
39510
  dataRelId: diagramDataRelationshipId,
37202
- drawingRelId: drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
39511
+ drawingRelId: drawingRelationshipId && drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
37203
39512
  colorsRelId: colorsRelationshipId.length > 0 ? colorsRelationshipId : void 0,
37204
39513
  styleRelId: styleRelationshipId.length > 0 ? styleRelationshipId : void 0
37205
39514
  };
37206
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
+ }
37207
39592
  parseSmartArtConnections(dataModel) {
37208
39593
  const connectionList = this.xmlLookupService.getChildByLocalName(dataModel, "cxnLst");
37209
39594
  const rawConnections = this.xmlLookupService.getChildrenArrayByLocalName(connectionList, "cxn");
@@ -37234,7 +39619,7 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37234
39619
  };
37235
39620
 
37236
39621
  // src/core/core/runtime/PptxHandlerRuntimeChartDetection.ts
37237
- var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
39622
+ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37238
39623
  detectChartType(plotArea) {
37239
39624
  if (!plotArea) {
37240
39625
  return "unknown";
@@ -37343,7 +39728,7 @@ var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
37343
39728
  };
37344
39729
 
37345
39730
  // src/core/core/runtime/PptxHandlerRuntimeChartParsingHelpers.ts
37346
- var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
39731
+ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37347
39732
  /**
37348
39733
  * Parse `c:plotVisOnly` from the chart root element.
37349
39734
  *
@@ -37404,7 +39789,7 @@ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37404
39789
  };
37405
39790
 
37406
39791
  // src/core/core/runtime/PptxHandlerRuntimeChartExternalData.ts
37407
- var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
39792
+ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37408
39793
  /**
37409
39794
  * Parse `c:externalData` from the chart's `c:chartSpace` and resolve
37410
39795
  * the external relationship target from the chart part's .rels file.
@@ -37520,7 +39905,7 @@ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37520
39905
  };
37521
39906
 
37522
39907
  // src/core/core/runtime/PptxHandlerRuntimeChartColorStyle.ts
37523
- var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
39908
+ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37524
39909
  /**
37525
39910
  * Parse the Office 2013+ chart color style part (`chartColorStyle*.xml`)
37526
39911
  * referenced from the chart's relationships.
@@ -37627,7 +40012,7 @@ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37627
40012
  };
37628
40013
 
37629
40014
  // src/core/core/runtime/PptxHandlerRuntimeChartParsing.ts
37630
- var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
40015
+ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
37631
40016
  /**
37632
40017
  * Parse chart data from a graphic frame element on a slide.
37633
40018
  *
@@ -37874,7 +40259,7 @@ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37874
40259
  };
37875
40260
 
37876
40261
  // src/core/core/runtime/PptxHandlerRuntimePresentationStructure.ts
37877
- var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
40262
+ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
37878
40263
  parseEditorAnimations(slideXml2) {
37879
40264
  return this.editorAnimationService.parseEditorAnimations(slideXml2);
37880
40265
  }
@@ -38119,7 +40504,7 @@ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
38119
40504
  };
38120
40505
 
38121
40506
  // src/core/core/runtime/PptxHandlerRuntimeEmbeddedFonts.ts
38122
- var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
40507
+ var PptxHandlerRuntime72 = class extends PptxHandlerRuntime71 {
38123
40508
  async getEmbeddedFonts() {
38124
40509
  const embeddedFontEntries = this.ensureArray(
38125
40510
  this.presentationData?.["p:presentation"]?.["p:embeddedFontLst"]?.["p:embeddedFont"]
@@ -38291,7 +40676,7 @@ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
38291
40676
  };
38292
40677
 
38293
40678
  // src/core/core/runtime/PptxHandlerRuntimeLoadSession.ts
38294
- var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime71 {
40679
+ var PptxHandlerRuntime73 = class _PptxHandlerRuntime extends PptxHandlerRuntime72 {
38295
40680
  isZipContainer(data) {
38296
40681
  const bytes = new Uint8Array(data);
38297
40682
  if (bytes.byteLength < 4) {
@@ -38334,6 +40719,23 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38334
40719
  this.imageDataCache.clear();
38335
40720
  this.themeColorMap = {};
38336
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;
38337
40739
  this.presentationDefaultTextStyle = void 0;
38338
40740
  this.commentAuthorMap.clear();
38339
40741
  this.commentAuthorDetails.clear();
@@ -38488,6 +40890,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38488
40890
  setCurrentSlideClrMapOverride: (override) => {
38489
40891
  this.currentSlideClrMapOverride = override;
38490
40892
  },
40893
+ setActiveMasterForSlide: (slidePath) => this.setActiveMasterForSlide(slidePath),
38491
40894
  findLayoutPathForSlide: (slidePath) => this.findLayoutPathForSlide(slidePath),
38492
40895
  loadThemeOverride: (partBasePath) => this.loadThemeOverride(partBasePath),
38493
40896
  applyThemeOverrideState: (override) => this.applyThemeOverrideState(override),
@@ -38518,7 +40921,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38518
40921
  };
38519
40922
 
38520
40923
  // src/core/core/runtime/PptxHandlerRuntimeLoadPipeline.ts
38521
- var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
40924
+ var PptxHandlerRuntime74 = class extends PptxHandlerRuntime73 {
38522
40925
  async buildLoadData(presentationState, slidesWithWarnings) {
38523
40926
  const headerFooter = this.extractHeaderFooter();
38524
40927
  const presentationProperties = await this.parsePresentationProperties();
@@ -38530,6 +40933,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38530
40933
  const notesMaster = await this.parseNotesMaster();
38531
40934
  const handoutMaster = await this.parseHandoutMaster();
38532
40935
  const slideMasters = await this.parseSlideMasters();
40936
+ await this.enrichSlideMastersWithTxStyles(slideMasters);
38533
40937
  const tags = await this.parseTags();
38534
40938
  const customProperties = await this.parseCustomProperties();
38535
40939
  const coreProperties = await this.parseCoreProperties();
@@ -38864,7 +41268,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38864
41268
  };
38865
41269
 
38866
41270
  // src/core/core/runtime/PptxHandlerRuntimeImplementation.ts
38867
- var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime73 {
41271
+ var PptxHandlerRuntime75 = class _PptxHandlerRuntime extends PptxHandlerRuntime74 {
38868
41272
  constructor(dependencyFactory = new PptxRuntimeDependencyFactory()) {
38869
41273
  super();
38870
41274
  this.dependencyFactory = dependencyFactory;
@@ -38912,6 +41316,9 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38912
41316
  extractGradientPathType: (gradFill) => this.colorStyleCodec.extractGradientPathType(gradFill),
38913
41317
  extractGradientFocalPoint: (gradFill) => this.colorStyleCodec.extractGradientFocalPoint(gradFill),
38914
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),
38915
41322
  normalizeStrokeDashType: (value) => this.normalizeStrokeDashType(value),
38916
41323
  normalizeConnectorArrowType: (value) => this.normalizeConnectorArrowType(value),
38917
41324
  ensureArray: (value) => this.ensureArray(value),
@@ -38985,11 +41392,11 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38985
41392
  };
38986
41393
 
38987
41394
  // src/core/core/PptxHandlerRuntime.ts
38988
- var PptxHandlerRuntime75 = class extends PptxHandlerRuntime74 {
41395
+ var PptxHandlerRuntime76 = class extends PptxHandlerRuntime75 {
38989
41396
  };
38990
41397
 
38991
41398
  // src/core/core/PptxHandlerRuntimeFactory.ts
38992
- var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime75();
41399
+ var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime76();
38993
41400
 
38994
41401
  // src/core/PptxHandlerCore.ts
38995
41402
  var PptxHandlerCore = class {