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
@@ -4400,12 +4400,25 @@ function svgToCustomGeometryPaths(pathData, width, height) {
4400
4400
  function pointToXml(pt) {
4401
4401
  return { "@_x": String(Math.round(pt.x)), "@_y": String(Math.round(pt.y)) };
4402
4402
  }
4403
- function customGeometryPathsToXml(paths) {
4403
+ function customGeometryPathsToXml(paths, rawData) {
4404
4404
  const xmlPaths = paths.map((path2) => {
4405
4405
  const pathXml = {
4406
4406
  "@_w": String(Math.round(path2.width)),
4407
4407
  "@_h": String(Math.round(path2.height))
4408
4408
  };
4409
+ if (path2.fillMode) {
4410
+ pathXml["@_fill"] = path2.fillMode;
4411
+ }
4412
+ if (path2.stroke === false) {
4413
+ pathXml["@_stroke"] = "0";
4414
+ } else if (path2.stroke === true) {
4415
+ pathXml["@_stroke"] = "1";
4416
+ }
4417
+ if (path2.extrusionOk === true) {
4418
+ pathXml["@_extrusionOk"] = "1";
4419
+ } else if (path2.extrusionOk === false) {
4420
+ pathXml["@_extrusionOk"] = "0";
4421
+ }
4409
4422
  const moveToList = [];
4410
4423
  const lnToList = [];
4411
4424
  const cubicBezToList = [];
@@ -4463,12 +4476,12 @@ function customGeometryPathsToXml(paths) {
4463
4476
  }
4464
4477
  return pathXml;
4465
4478
  });
4466
- return {
4479
+ const result = {
4467
4480
  "a:avLst": {},
4468
- "a:gdLst": {},
4469
- "a:ahLst": {},
4470
- "a:cxnLst": {},
4471
- "a:rect": {
4481
+ "a:gdLst": rawData?.gdLstXml ?? {},
4482
+ "a:ahLst": rawData?.ahLstXml ?? {},
4483
+ "a:cxnLst": rawData?.cxnLstXml ?? {},
4484
+ "a:rect": rawData?.rectXml ?? {
4472
4485
  "@_l": "l",
4473
4486
  "@_t": "t",
4474
4487
  "@_r": "r",
@@ -4478,6 +4491,7 @@ function customGeometryPathsToXml(paths) {
4478
4491
  "a:path": xmlPaths.length === 1 ? xmlPaths[0] : xmlPaths
4479
4492
  }
4480
4493
  };
4494
+ return result;
4481
4495
  }
4482
4496
 
4483
4497
  // src/core/builders/sdk/ElementFactory.ts
@@ -5224,6 +5238,9 @@ ${Array.from(
5224
5238
  <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
5225
5239
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
5226
5240
  xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
5241
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5242
+ xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main"
5243
+ xmlns:p15="http://schemas.microsoft.com/office/powerpoint/2012/main"
5227
5244
  saveSubsetFonts="1">
5228
5245
  <p:sldMasterIdLst>
5229
5246
  <p:sldMasterId id="2147483648" r:id="rId1"/>
@@ -6645,6 +6662,22 @@ var PptxPresentationSlidesReconciler = class {
6645
6662
  };
6646
6663
 
6647
6664
  // src/core/core/builders/PptxSlideRelationshipRegistry.ts
6665
+ function isExternalTarget(target) {
6666
+ const normalized = target.trim();
6667
+ if (normalized.length === 0) {
6668
+ return false;
6669
+ }
6670
+ const colonIdx = normalized.indexOf(":");
6671
+ if (colonIdx <= 0) {
6672
+ return false;
6673
+ }
6674
+ const slashIdx = normalized.indexOf("/");
6675
+ if (slashIdx !== -1 && slashIdx < colonIdx) {
6676
+ return false;
6677
+ }
6678
+ const scheme = normalized.slice(0, colonIdx);
6679
+ return /^[A-Za-z][A-Za-z0-9+\-.]*$/.test(scheme);
6680
+ }
6648
6681
  var PptxSlideRelationshipRegistry = class {
6649
6682
  relationships;
6650
6683
  usedRelationshipIds = /* @__PURE__ */ new Set();
@@ -6715,7 +6748,7 @@ var PptxSlideRelationshipRegistry = class {
6715
6748
  return existingRelationshipId;
6716
6749
  }
6717
6750
  const relationshipId = this.nextRelationshipId();
6718
- const targetMode = /^(https?:|mailto:|ftp:|file:)/i.test(normalizedTarget) ? "External" : void 0;
6751
+ const targetMode = isExternalTarget(normalizedTarget) ? "External" : void 0;
6719
6752
  this.upsertRelationship(
6720
6753
  relationshipId,
6721
6754
  this.hyperlinkRelationshipType,
@@ -6965,6 +6998,82 @@ var PptxSlideNotesPartUpdater = class {
6965
6998
  }
6966
6999
  };
6967
7000
 
7001
+ // src/core/utils/xml-reorder.ts
7002
+ function reorderObjectKeys(obj, schemaOrder) {
7003
+ const result = {};
7004
+ const consumed = /* @__PURE__ */ new Set();
7005
+ for (const key of schemaOrder) {
7006
+ if (Object.hasOwn(obj, key)) {
7007
+ const value = obj[key];
7008
+ if (value !== void 0) {
7009
+ result[key] = value;
7010
+ }
7011
+ consumed.add(key);
7012
+ }
7013
+ }
7014
+ for (const key of Object.keys(obj)) {
7015
+ if (consumed.has(key)) {
7016
+ continue;
7017
+ }
7018
+ const value = obj[key];
7019
+ if (value !== void 0) {
7020
+ result[key] = value;
7021
+ }
7022
+ }
7023
+ return result;
7024
+ }
7025
+ var EFFECT_LST_ORDER = [
7026
+ "a:blur",
7027
+ "a:fillOverlay",
7028
+ "a:glow",
7029
+ "a:innerShdw",
7030
+ "a:outerShdw",
7031
+ "a:prstShdw",
7032
+ "a:reflection",
7033
+ "a:softEdge"
7034
+ ];
7035
+ var SP_PR_ORDER = [
7036
+ "a:xfrm",
7037
+ "a:custGeom",
7038
+ "a:prstGeom",
7039
+ "a:noFill",
7040
+ "a:solidFill",
7041
+ "a:gradFill",
7042
+ "a:blipFill",
7043
+ "a:pattFill",
7044
+ "a:grpFill",
7045
+ "a:ln",
7046
+ "a:effectLst",
7047
+ "a:effectDag",
7048
+ "a:scene3d",
7049
+ "a:sp3d",
7050
+ "a:extLst"
7051
+ ];
7052
+ var TC_PR_BORDERS_ORDER = [
7053
+ "a:lnL",
7054
+ "a:lnR",
7055
+ "a:lnT",
7056
+ "a:lnB",
7057
+ "a:lnTlToBr",
7058
+ "a:lnBlToTr",
7059
+ "a:cell3D",
7060
+ "a:noFill",
7061
+ "a:solidFill",
7062
+ "a:gradFill",
7063
+ "a:blipFill",
7064
+ "a:pattFill",
7065
+ "a:grpFill",
7066
+ "a:headers",
7067
+ "a:extLst"
7068
+ ];
7069
+ var BLIP_FILL_ORDER = ["a:blip", "a:srcRect", "a:tile", "a:stretch"];
7070
+ var SHAPE_STYLE_ORDER = [
7071
+ "a:lnRef",
7072
+ "a:fillRef",
7073
+ "a:effectRef",
7074
+ "a:fontRef"
7075
+ ];
7076
+
6968
7077
  // src/core/core/builders/PptxSlideBackgroundBuilder.ts
6969
7078
  var PptxSlideBackgroundBuilder = class {
6970
7079
  applyBackground(init) {
@@ -7000,10 +7109,13 @@ var PptxSlideBackgroundBuilder = class {
7000
7109
  init.slideImageRelationshipType,
7001
7110
  relativeBackgroundImagePath
7002
7111
  );
7003
- backgroundProperties["a:blipFill"] = {
7004
- "a:blip": { "@_r:embed": backgroundRelationshipId },
7005
- "a:stretch": { "a:fillRect": {} }
7006
- };
7112
+ backgroundProperties["a:blipFill"] = reorderObjectKeys(
7113
+ {
7114
+ "a:blip": { "@_r:embed": backgroundRelationshipId },
7115
+ "a:stretch": { "a:fillRect": {} }
7116
+ },
7117
+ BLIP_FILL_ORDER
7118
+ );
7007
7119
  }
7008
7120
  } else if (hasBackgroundColor && init.slide.backgroundColor) {
7009
7121
  backgroundProperties["a:solidFill"] = {
@@ -7747,6 +7859,45 @@ var PptxGradientStyleCodec = class {
7747
7859
  b: Number.isFinite(b) ? this.context.clampUnitInterval(b / 1e5) : 0
7748
7860
  };
7749
7861
  }
7862
+ extractGradientFlip(gradFill) {
7863
+ const flipRaw = String(gradFill["@_flip"] || "").trim().toLowerCase();
7864
+ if (flipRaw === "x" || flipRaw === "y" || flipRaw === "xy" || flipRaw === "none") {
7865
+ return flipRaw;
7866
+ }
7867
+ return void 0;
7868
+ }
7869
+ extractGradientRotWithShape(gradFill) {
7870
+ const rot = gradFill["@_rotWithShape"];
7871
+ if (rot === void 0 || rot === null) {
7872
+ return void 0;
7873
+ }
7874
+ const token = String(rot).trim().toLowerCase();
7875
+ if (token === "1" || token === "true") {
7876
+ return true;
7877
+ }
7878
+ if (token === "0" || token === "false") {
7879
+ return false;
7880
+ }
7881
+ return void 0;
7882
+ }
7883
+ extractGradientScaled(gradFill) {
7884
+ const lin = gradFill["a:lin"];
7885
+ if (!lin) {
7886
+ return void 0;
7887
+ }
7888
+ const scaled = lin["@_scaled"];
7889
+ if (scaled === void 0 || scaled === null) {
7890
+ return void 0;
7891
+ }
7892
+ const token = String(scaled).trim().toLowerCase();
7893
+ if (token === "1" || token === "true") {
7894
+ return true;
7895
+ }
7896
+ if (token === "0" || token === "false") {
7897
+ return false;
7898
+ }
7899
+ return void 0;
7900
+ }
7750
7901
  extractGradientAngle(gradFill) {
7751
7902
  const angleRaw = Number.parseInt(
7752
7903
  String(gradFill["a:lin"]?.["@_ang"] || ""),
@@ -7833,11 +7984,14 @@ var PptxGradientStyleCodec = class {
7833
7984
  return void 0;
7834
7985
  }
7835
7986
  const gradientType = shapeStyle.fillGradientType || "linear";
7836
- const gradientXml = {
7837
- "a:gsLst": {
7838
- "a:gs": stops
7839
- }
7840
- };
7987
+ const gradientXml = {};
7988
+ if (shapeStyle.fillGradientFlip && shapeStyle.fillGradientFlip !== "none") {
7989
+ gradientXml["@_flip"] = shapeStyle.fillGradientFlip;
7990
+ }
7991
+ if (shapeStyle.fillGradientRotWithShape !== void 0) {
7992
+ gradientXml["@_rotWithShape"] = shapeStyle.fillGradientRotWithShape ? "1" : "0";
7993
+ }
7994
+ gradientXml["a:gsLst"] = { "a:gs": stops };
7841
7995
  if (gradientType === "radial") {
7842
7996
  const pathType = shapeStyle.fillGradientPathType || "circle";
7843
7997
  const pathXml = {
@@ -7867,10 +8021,15 @@ var PptxGradientStyleCodec = class {
7867
8021
  gradientXml["a:path"] = pathXml;
7868
8022
  } else {
7869
8023
  const normalizedAngle = typeof shapeStyle.fillGradientAngle === "number" && Number.isFinite(shapeStyle.fillGradientAngle) ? shapeStyle.fillGradientAngle : 90;
7870
- gradientXml["a:lin"] = {
7871
- "@_ang": String(Math.round(normalizedAngle * 6e4)),
7872
- "@_scaled": "1"
8024
+ const linNode = {
8025
+ "@_ang": String(Math.round(normalizedAngle * 6e4))
7873
8026
  };
8027
+ if (shapeStyle.fillGradientScaled !== void 0) {
8028
+ linNode["@_scaled"] = shapeStyle.fillGradientScaled ? "1" : "0";
8029
+ } else {
8030
+ linNode["@_scaled"] = "1";
8031
+ }
8032
+ gradientXml["a:lin"] = linNode;
7874
8033
  }
7875
8034
  return gradientXml;
7876
8035
  }
@@ -8267,6 +8426,30 @@ var PRESET_SHADOW_OPACITY_MAP = {
8267
8426
  };
8268
8427
 
8269
8428
  // src/core/core/builders/PptxShapeEffectStyleExtractor.ts
8429
+ var VALID_ALIGNMENTS = /* @__PURE__ */ new Set(["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"]);
8430
+ function parseIntAttr(value) {
8431
+ if (value === void 0 || value === null || value === "") {
8432
+ return void 0;
8433
+ }
8434
+ const parsed = Number.parseInt(String(value), 10);
8435
+ return Number.isFinite(parsed) ? parsed : void 0;
8436
+ }
8437
+ function parseAlignmentAttr(value) {
8438
+ const v = String(value ?? "").trim();
8439
+ return VALID_ALIGNMENTS.has(v) ? v : void 0;
8440
+ }
8441
+ function parseBoolAttr(value) {
8442
+ if (typeof value === "boolean") {
8443
+ return value;
8444
+ }
8445
+ if (value === "1" || value === "true") {
8446
+ return true;
8447
+ }
8448
+ if (value === "0" || value === "false") {
8449
+ return false;
8450
+ }
8451
+ return void 0;
8452
+ }
8270
8453
  var PptxShapeEffectStyleExtractor = class {
8271
8454
  context;
8272
8455
  constructor(context) {
@@ -8290,7 +8473,12 @@ var PptxShapeEffectStyleExtractor = class {
8290
8473
  const shadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8291
8474
  const shadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8292
8475
  const rotateWithShape = outerShadow["@_rotWithShape"];
8293
- const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : void 0;
8476
+ const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : rotateWithShape === "0" || rotateWithShape === "false" ? false : void 0;
8477
+ const shadowScaleX = parseIntAttr(outerShadow["@_sx"]);
8478
+ const shadowScaleY = parseIntAttr(outerShadow["@_sy"]);
8479
+ const shadowSkewX = parseIntAttr(outerShadow["@_kx"]);
8480
+ const shadowSkewY = parseIntAttr(outerShadow["@_ky"]);
8481
+ const shadowAlignment = parseAlignmentAttr(outerShadow["@_algn"]);
8294
8482
  return {
8295
8483
  shadowColor,
8296
8484
  shadowOpacity,
@@ -8299,7 +8487,12 @@ var PptxShapeEffectStyleExtractor = class {
8299
8487
  shadowOffsetY,
8300
8488
  shadowAngle: directionDegrees,
8301
8489
  shadowDistance: distance,
8302
- shadowRotateWithShape
8490
+ shadowRotateWithShape,
8491
+ shadowScaleX,
8492
+ shadowScaleY,
8493
+ shadowSkewX,
8494
+ shadowSkewY,
8495
+ shadowAlignment
8303
8496
  };
8304
8497
  }
8305
8498
  extractPresetShadowStyle(effectLstParent) {
@@ -8345,12 +8538,14 @@ var PptxShapeEffectStyleExtractor = class {
8345
8538
  const directionRadians = directionDegrees * Math.PI / 180;
8346
8539
  const innerShadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
8347
8540
  const innerShadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
8541
+ const innerShadowRotateWithShape = parseBoolAttr(innerShadow["@_rotWithShape"]);
8348
8542
  return {
8349
8543
  innerShadowColor,
8350
8544
  innerShadowOpacity,
8351
8545
  innerShadowBlur,
8352
8546
  innerShadowOffsetX,
8353
- innerShadowOffsetY
8547
+ innerShadowOffsetY,
8548
+ innerShadowRotateWithShape
8354
8549
  };
8355
8550
  }
8356
8551
  extractGlowStyle(shapeProps) {
@@ -8399,6 +8594,16 @@ var PptxShapeEffectStyleExtractor = class {
8399
8594
  const reflectionRotation = Number.isFinite(rotationRaw) ? rotationRaw / 6e4 : void 0;
8400
8595
  const distanceRaw = Number.parseInt(String(reflectionNode["@_dist"] || ""), 10);
8401
8596
  const reflectionDistance = Number.isFinite(distanceRaw) && distanceRaw >= 0 ? distanceRaw / this.context.emuPerPx : void 0;
8597
+ const fadeDirRaw = parseIntAttr(reflectionNode["@_fadeDir"]);
8598
+ const reflectionFadeDirection = fadeDirRaw !== void 0 ? fadeDirRaw / 6e4 : void 0;
8599
+ const reflectionScaleX = parseIntAttr(reflectionNode["@_sx"]);
8600
+ const reflectionScaleY = parseIntAttr(reflectionNode["@_sy"]);
8601
+ const reflectionSkewX = parseIntAttr(reflectionNode["@_kx"]);
8602
+ const reflectionSkewY = parseIntAttr(reflectionNode["@_ky"]);
8603
+ const reflectionAlignment = parseAlignmentAttr(reflectionNode["@_algn"]);
8604
+ const reflectionRotateWithShape = parseBoolAttr(reflectionNode["@_rotWithShape"]);
8605
+ const stPosRaw = parseIntAttr(reflectionNode["@_stPos"]);
8606
+ const reflectionStartPosition = stPosRaw !== void 0 ? stPosRaw / 1e5 : void 0;
8402
8607
  return {
8403
8608
  reflectionBlurRadius,
8404
8609
  reflectionStartOpacity,
@@ -8406,7 +8611,15 @@ var PptxShapeEffectStyleExtractor = class {
8406
8611
  reflectionEndPosition,
8407
8612
  reflectionDirection,
8408
8613
  reflectionRotation,
8409
- reflectionDistance
8614
+ reflectionDistance,
8615
+ reflectionFadeDirection,
8616
+ reflectionScaleX,
8617
+ reflectionScaleY,
8618
+ reflectionSkewX,
8619
+ reflectionSkewY,
8620
+ reflectionAlignment,
8621
+ reflectionRotateWithShape,
8622
+ reflectionStartPosition
8410
8623
  };
8411
8624
  }
8412
8625
  extractBlurStyle(shapeProps) {
@@ -8458,11 +8671,56 @@ var PptxShapeEffectXmlBuilder = class {
8458
8671
  }
8459
8672
  }
8460
8673
  };
8674
+ if (typeof shapeStyle.shadowScaleX === "number") {
8675
+ xmlObj["@_sx"] = String(Math.round(shapeStyle.shadowScaleX));
8676
+ }
8677
+ if (typeof shapeStyle.shadowScaleY === "number") {
8678
+ xmlObj["@_sy"] = String(Math.round(shapeStyle.shadowScaleY));
8679
+ }
8680
+ if (typeof shapeStyle.shadowSkewX === "number") {
8681
+ xmlObj["@_kx"] = String(Math.round(shapeStyle.shadowSkewX));
8682
+ }
8683
+ if (typeof shapeStyle.shadowSkewY === "number") {
8684
+ xmlObj["@_ky"] = String(Math.round(shapeStyle.shadowSkewY));
8685
+ }
8686
+ if (shapeStyle.shadowAlignment) {
8687
+ xmlObj["@_algn"] = shapeStyle.shadowAlignment;
8688
+ }
8461
8689
  if (typeof shapeStyle.shadowRotateWithShape === "boolean") {
8462
8690
  xmlObj["@_rotWithShape"] = shapeStyle.shadowRotateWithShape ? "1" : "0";
8463
8691
  }
8464
8692
  return xmlObj;
8465
8693
  }
8694
+ buildPresetShadowXml(shapeStyle) {
8695
+ const preset = shapeStyle.presetShadowName;
8696
+ if (!preset || preset.length === 0) {
8697
+ return void 0;
8698
+ }
8699
+ const shadowColor = String(shapeStyle.shadowColor || "#000000").trim();
8700
+ const shadowOpacity = typeof shapeStyle.shadowOpacity === "number" && Number.isFinite(shapeStyle.shadowOpacity) ? this.context.clampUnitInterval(shapeStyle.shadowOpacity) : 0.5;
8701
+ let distance;
8702
+ let directionDegrees;
8703
+ if (typeof shapeStyle.shadowAngle === "number" && typeof shapeStyle.shadowDistance === "number") {
8704
+ directionDegrees = shapeStyle.shadowAngle;
8705
+ distance = shapeStyle.shadowDistance;
8706
+ } else {
8707
+ const ox = typeof shapeStyle.shadowOffsetX === "number" ? shapeStyle.shadowOffsetX : 0;
8708
+ const oy = typeof shapeStyle.shadowOffsetY === "number" ? shapeStyle.shadowOffsetY : 0;
8709
+ distance = Math.sqrt(ox * ox + oy * oy);
8710
+ directionDegrees = (Math.atan2(oy, ox) * 180 / Math.PI + 360) % 360;
8711
+ }
8712
+ return {
8713
+ "@_prst": preset,
8714
+ "@_dist": String(Math.round(distance * this.context.emuPerPx)),
8715
+ "@_dir": String(Math.round(directionDegrees * 6e4)),
8716
+ "a:srgbClr": {
8717
+ "@_val": shadowColor.replace("#", ""),
8718
+ "a:alpha": {
8719
+ "@_val": String(Math.round(shadowOpacity * 1e5))
8720
+ }
8721
+ }
8722
+ };
8723
+ }
8466
8724
  buildInnerShadowXml(shapeStyle) {
8467
8725
  const innerColor = String(shapeStyle.innerShadowColor || "").trim();
8468
8726
  if (innerColor.length === 0 || innerColor === "transparent") {
@@ -8474,7 +8732,7 @@ var PptxShapeEffectXmlBuilder = class {
8474
8732
  const opacity = typeof shapeStyle.innerShadowOpacity === "number" && Number.isFinite(shapeStyle.innerShadowOpacity) ? this.context.clampUnitInterval(shapeStyle.innerShadowOpacity) : 0.5;
8475
8733
  const distance = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
8476
8734
  const directionDegrees = (Math.atan2(offsetY, offsetX) * 180 / Math.PI + 360) % 360;
8477
- return {
8735
+ const xmlObj = {
8478
8736
  "@_blurRad": String(Math.round(blurValue * this.context.emuPerPx)),
8479
8737
  "@_dist": String(Math.round(distance * this.context.emuPerPx)),
8480
8738
  "@_dir": String(Math.round(directionDegrees * 6e4)),
@@ -8485,6 +8743,10 @@ var PptxShapeEffectXmlBuilder = class {
8485
8743
  }
8486
8744
  }
8487
8745
  };
8746
+ if (typeof shapeStyle.innerShadowRotateWithShape === "boolean") {
8747
+ xmlObj["@_rotWithShape"] = shapeStyle.innerShadowRotateWithShape ? "1" : "0";
8748
+ }
8749
+ return xmlObj;
8488
8750
  }
8489
8751
  buildGlowXml(shapeStyle) {
8490
8752
  const glowColor = String(shapeStyle.glowColor || "").trim();
@@ -8546,6 +8808,30 @@ var PptxShapeEffectXmlBuilder = class {
8546
8808
  Math.round(shapeStyle.reflectionDistance * this.context.emuPerPx)
8547
8809
  );
8548
8810
  }
8811
+ if (typeof shapeStyle.reflectionFadeDirection === "number") {
8812
+ reflectionXml["@_fadeDir"] = String(Math.round(shapeStyle.reflectionFadeDirection * 6e4));
8813
+ }
8814
+ if (typeof shapeStyle.reflectionScaleX === "number") {
8815
+ reflectionXml["@_sx"] = String(Math.round(shapeStyle.reflectionScaleX));
8816
+ }
8817
+ if (typeof shapeStyle.reflectionScaleY === "number") {
8818
+ reflectionXml["@_sy"] = String(Math.round(shapeStyle.reflectionScaleY));
8819
+ }
8820
+ if (typeof shapeStyle.reflectionSkewX === "number") {
8821
+ reflectionXml["@_kx"] = String(Math.round(shapeStyle.reflectionSkewX));
8822
+ }
8823
+ if (typeof shapeStyle.reflectionSkewY === "number") {
8824
+ reflectionXml["@_ky"] = String(Math.round(shapeStyle.reflectionSkewY));
8825
+ }
8826
+ if (shapeStyle.reflectionAlignment) {
8827
+ reflectionXml["@_algn"] = shapeStyle.reflectionAlignment;
8828
+ }
8829
+ if (typeof shapeStyle.reflectionRotateWithShape === "boolean") {
8830
+ reflectionXml["@_rotWithShape"] = shapeStyle.reflectionRotateWithShape ? "1" : "0";
8831
+ }
8832
+ if (typeof shapeStyle.reflectionStartPosition === "number") {
8833
+ reflectionXml["@_stPos"] = String(Math.round(shapeStyle.reflectionStartPosition * 1e5));
8834
+ }
8549
8835
  return reflectionXml;
8550
8836
  }
8551
8837
  buildBlurXml(shapeStyle) {
@@ -8653,6 +8939,9 @@ var PptxShapeEffectXmlCodec = class {
8653
8939
  buildOuterShadowXml(shapeStyle) {
8654
8940
  return this.builder.buildOuterShadowXml(shapeStyle);
8655
8941
  }
8942
+ buildPresetShadowXml(shapeStyle) {
8943
+ return this.builder.buildPresetShadowXml(shapeStyle);
8944
+ }
8656
8945
  buildInnerShadowXml(shapeStyle) {
8657
8946
  return this.builder.buildInnerShadowXml(shapeStyle);
8658
8947
  }
@@ -8813,6 +9102,9 @@ var PptxColorStyleCodec = class {
8813
9102
  buildOuterShadowXml(shapeStyle) {
8814
9103
  return this.shapeEffectXmlCodec.buildOuterShadowXml(shapeStyle);
8815
9104
  }
9105
+ buildPresetShadowXml(shapeStyle) {
9106
+ return this.shapeEffectXmlCodec.buildPresetShadowXml(shapeStyle);
9107
+ }
8816
9108
  buildInnerShadowXml(shapeStyle) {
8817
9109
  return this.shapeEffectXmlCodec.buildInnerShadowXml(shapeStyle);
8818
9110
  }
@@ -8834,6 +9126,15 @@ var PptxColorStyleCodec = class {
8834
9126
  extractGradientFillColor(gradFill) {
8835
9127
  return this.gradientStyleCodec.extractGradientFillColor(gradFill);
8836
9128
  }
9129
+ extractGradientFlip(gradFill) {
9130
+ return this.gradientStyleCodec.extractGradientFlip(gradFill);
9131
+ }
9132
+ extractGradientRotWithShape(gradFill) {
9133
+ return this.gradientStyleCodec.extractGradientRotWithShape(gradFill);
9134
+ }
9135
+ extractGradientScaled(gradFill) {
9136
+ return this.gradientStyleCodec.extractGradientScaled(gradFill);
9137
+ }
8837
9138
  extractGradientPathType(gradFill) {
8838
9139
  return this.gradientStyleCodec.extractGradientPathType(gradFill);
8839
9140
  }
@@ -8845,6 +9146,61 @@ var PptxColorStyleCodec = class {
8845
9146
  }
8846
9147
  };
8847
9148
 
9149
+ // src/core/utils/color-xml-preservation.ts
9150
+ var COLOR_CHOICE_KEYS = [
9151
+ "a:srgbClr",
9152
+ "a:schemeClr",
9153
+ "a:sysClr",
9154
+ "a:prstClr",
9155
+ "a:scrgbClr",
9156
+ "a:hslClr"
9157
+ ];
9158
+ function extractColorChoiceXml(parent) {
9159
+ if (!parent) {
9160
+ return void 0;
9161
+ }
9162
+ for (const key of COLOR_CHOICE_KEYS) {
9163
+ if (parent[key] !== void 0) {
9164
+ return { [key]: parent[key] };
9165
+ }
9166
+ }
9167
+ return void 0;
9168
+ }
9169
+ function normalizeHex(value) {
9170
+ const raw = String(value ?? "").trim();
9171
+ if (raw.length === 0) {
9172
+ return "";
9173
+ }
9174
+ const hex = raw.replace(/^#/, "");
9175
+ if (/^[0-9a-fA-F]{6}$/.test(hex)) {
9176
+ return hex.toUpperCase();
9177
+ }
9178
+ return raw.toLowerCase();
9179
+ }
9180
+ function colorsEqual(left, right) {
9181
+ if (left === void 0 || right === void 0) {
9182
+ return false;
9183
+ }
9184
+ return normalizeHex(left) === normalizeHex(right);
9185
+ }
9186
+ function buildSrgbColorChoice(hex, opacity) {
9187
+ const normalized = String(hex || "").replace(/^#/, "");
9188
+ const srgb = { "@_val": normalized };
9189
+ if (typeof opacity === "number" && Number.isFinite(opacity) && opacity >= 0 && opacity < 1) {
9190
+ const alphaPct = Math.round(Math.max(0, Math.min(1, opacity)) * 1e5);
9191
+ srgb["a:alpha"] = { "@_val": String(alphaPct) };
9192
+ }
9193
+ return { "a:srgbClr": srgb };
9194
+ }
9195
+ function serializeColorChoice(originalColorXml, currentResolvedHex, fallbackHex, opacity, options = {}) {
9196
+ if (originalColorXml && colorsEqual(currentResolvedHex, fallbackHex)) {
9197
+ if (options.preserveAlphaFromOriginal !== false) {
9198
+ return originalColorXml;
9199
+ }
9200
+ }
9201
+ return buildSrgbColorChoice(fallbackHex, opacity);
9202
+ }
9203
+
8848
9204
  // src/core/core/builders/shape-style-3d-helpers.ts
8849
9205
  function applyScene3dStyle(shapeProps, style) {
8850
9206
  const scene3dNode = shapeProps["a:scene3d"];
@@ -8930,6 +9286,10 @@ function applyStrokeColor(lineNode, style, context) {
8930
9286
  const lineFill = lineNode["a:solidFill"];
8931
9287
  style.strokeColor = context.parseColor(lineFill);
8932
9288
  style.strokeOpacity = context.extractColorOpacity(lineFill);
9289
+ const strokeColorXml = extractColorChoiceXml(lineFill);
9290
+ if (strokeColorXml) {
9291
+ style.strokeColorXml = strokeColorXml;
9292
+ }
8933
9293
  } else if (lineNode["a:gradFill"]) {
8934
9294
  style.strokeColor = context.extractGradientFillColor(lineNode["a:gradFill"]);
8935
9295
  style.strokeOpacity = context.extractGradientOpacity(lineNode["a:gradFill"]);
@@ -8997,6 +9357,14 @@ function applyJoinCapCompound(lineNode, style) {
8997
9357
  style.lineJoin = "bevel";
8998
9358
  } else if ("a:miter" in lineNode) {
8999
9359
  style.lineJoin = "miter";
9360
+ const miterNode = lineNode["a:miter"];
9361
+ const limRaw = miterNode?.["@_lim"];
9362
+ if (limRaw !== void 0 && limRaw !== "") {
9363
+ const parsed = parseInt(String(limRaw), 10);
9364
+ if (Number.isFinite(parsed)) {
9365
+ style.miterLimit = parsed;
9366
+ }
9367
+ }
9000
9368
  }
9001
9369
  const capValue = String(lineNode["@_cap"] || "").trim().toLowerCase();
9002
9370
  if (capValue === "rnd" || capValue === "sq" || capValue === "flat") {
@@ -9053,6 +9421,10 @@ var PptxShapeStyleExtractor = class {
9053
9421
  style.fillMode = "solid";
9054
9422
  style.fillColor = this.context.parseColor(solidFill);
9055
9423
  style.fillOpacity = this.context.extractColorOpacity(solidFill);
9424
+ const solidFillColorXml = extractColorChoiceXml(solidFill);
9425
+ if (solidFillColorXml) {
9426
+ style.fillColorXml = solidFillColorXml;
9427
+ }
9056
9428
  } else if (gradFill) {
9057
9429
  style.fillMode = "gradient";
9058
9430
  style.fillColor = this.context.extractGradientFillColor(gradFill);
@@ -9064,6 +9436,18 @@ var PptxShapeStyleExtractor = class {
9064
9436
  style.fillGradientPathType = this.context.extractGradientPathType(gradFill);
9065
9437
  style.fillGradientFocalPoint = this.context.extractGradientFocalPoint(gradFill);
9066
9438
  style.fillGradientFillToRect = this.context.extractGradientFillToRect(gradFill);
9439
+ const gradFlip = this.context.extractGradientFlip(gradFill);
9440
+ if (gradFlip) {
9441
+ style.fillGradientFlip = gradFlip;
9442
+ }
9443
+ const gradRot = this.context.extractGradientRotWithShape(gradFill);
9444
+ if (gradRot !== void 0) {
9445
+ style.fillGradientRotWithShape = gradRot;
9446
+ }
9447
+ const gradScaled = this.context.extractGradientScaled(gradFill);
9448
+ if (gradScaled !== void 0) {
9449
+ style.fillGradientScaled = gradScaled;
9450
+ }
9067
9451
  } else if (pattFill) {
9068
9452
  style.fillMode = "pattern";
9069
9453
  style.fillColor = this.context.parseColor(pattFill["a:fgClr"]) || this.context.parseColor(pattFill["a:bgClr"]);
@@ -9145,10 +9529,45 @@ var PptxShapeStyleExtractor = class {
9145
9529
  if (styleNode?.["a:effectRef"]) {
9146
9530
  this.context.resolveThemeEffectRef(styleNode["a:effectRef"], style);
9147
9531
  }
9532
+ const fontRef = styleNode?.["a:fontRef"];
9533
+ if (fontRef) {
9534
+ const idxAttr = String(fontRef["@_idx"] || "").trim();
9535
+ if (idxAttr.length > 0) {
9536
+ style.fontRefIdx = idxAttr;
9537
+ }
9538
+ const overrideColorXml = this.extractFontRefColorXml(fontRef);
9539
+ if (overrideColorXml) {
9540
+ style.fontRefColorXml = overrideColorXml;
9541
+ }
9542
+ }
9148
9543
  applyScene3dStyle(shapeProps, style);
9149
9544
  applyShape3dStyle(shapeProps, style, this.context);
9150
9545
  return style;
9151
9546
  }
9547
+ /**
9548
+ * Pull the verbatim colour-choice child out of an `a:fontRef` element,
9549
+ * preserving any contained colour transforms for round-trip.
9550
+ */
9551
+ extractFontRefColorXml(refNode) {
9552
+ if (!refNode) {
9553
+ return void 0;
9554
+ }
9555
+ const keys = [
9556
+ "a:scrgbClr",
9557
+ "a:srgbClr",
9558
+ "a:hslClr",
9559
+ "a:sysClr",
9560
+ "a:schemeClr",
9561
+ "a:prstClr"
9562
+ ];
9563
+ for (const key of keys) {
9564
+ const child = refNode[key];
9565
+ if (child !== void 0) {
9566
+ return { [key]: child };
9567
+ }
9568
+ }
9569
+ return void 0;
9570
+ }
9152
9571
  /**
9153
9572
  * Extract p14:hiddenFill from the shape properties extension list.
9154
9573
  * URI: {AF507438-7753-43E0-B8FC-AC1667EBCBE1}
@@ -9199,10 +9618,15 @@ var PptxShapeStyleExtractor = class {
9199
9618
  function applyCellFillStyle(cellProperties, style, context) {
9200
9619
  let hasStyle = false;
9201
9620
  if (cellProperties?.["a:solidFill"]) {
9202
- const fillColor = context.parseColor(cellProperties["a:solidFill"]);
9621
+ const solidFillNode = cellProperties["a:solidFill"];
9622
+ const fillColor = context.parseColor(solidFillNode);
9203
9623
  if (fillColor) {
9204
9624
  style.fillMode = "solid";
9205
9625
  style.backgroundColor = fillColor;
9626
+ const bgColorXml = extractColorChoiceXml(solidFillNode);
9627
+ if (bgColorXml) {
9628
+ style.backgroundColorXml = bgColorXml;
9629
+ }
9206
9630
  hasStyle = true;
9207
9631
  }
9208
9632
  }
@@ -9239,6 +9663,10 @@ function applyCellFillStyle(cellProperties, style, context) {
9239
9663
  }
9240
9664
  }
9241
9665
  }
9666
+ if (cellProperties?.["a:noFill"] !== void 0) {
9667
+ style.fillMode = "none";
9668
+ hasStyle = true;
9669
+ }
9242
9670
  if (cellProperties?.["a:pattFill"]) {
9243
9671
  const pattFill = cellProperties["a:pattFill"];
9244
9672
  const fgColor = context.parseColor(pattFill["a:fgClr"]);
@@ -9545,8 +9973,7 @@ var PptxTableDataParser = class {
9545
9973
  return width / totalWidthEmu;
9546
9974
  }) : gridColumns.map(() => 1 / Math.max(gridColumns.length, 1));
9547
9975
  const tableProperties = tableNode["a:tblPr"] || {};
9548
- const tableStyleNode = tableProperties["a:tblStyle"];
9549
- const tableStyleId = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim() || void 0;
9976
+ const tableStyleId = this.extractTableStyleId(tableProperties);
9550
9977
  const xmlRows = this.context.ensureArray(tableNode["a:tr"]);
9551
9978
  const rows = xmlRows.map((rowNode) => {
9552
9979
  const rowHeightEmu = parseInt(String(rowNode?.["@_h"] || "0"), 10) || 0;
@@ -9581,6 +10008,28 @@ var PptxTableDataParser = class {
9581
10008
  return void 0;
9582
10009
  }
9583
10010
  }
10011
+ /**
10012
+ * Read the table style ID from `a:tblPr`.
10013
+ *
10014
+ * ECMA-376 §21.1.3.13 defines `<a:tableStyleId>{GUID}</a:tableStyleId>` as
10015
+ * a child element of `a:tblPr` carrying the GUID as element text. Older
10016
+ * inputs (and earlier versions of this library) used the legacy
10017
+ * `<a:tblStyle val="{GUID}"/>` child element or a `@_tblStyle` attribute.
10018
+ * Accept all three; the spec form takes precedence.
10019
+ */
10020
+ extractTableStyleId(tableProperties) {
10021
+ const tableStyleIdNode = tableProperties["a:tableStyleId"];
10022
+ if (tableStyleIdNode !== void 0 && tableStyleIdNode !== null) {
10023
+ const direct = typeof tableStyleIdNode === "string" || typeof tableStyleIdNode === "number" ? String(tableStyleIdNode) : String(tableStyleIdNode["#text"] ?? "");
10024
+ const trimmed = direct.trim();
10025
+ if (trimmed.length > 0) {
10026
+ return trimmed;
10027
+ }
10028
+ }
10029
+ const tableStyleNode = tableProperties["a:tblStyle"];
10030
+ const legacy = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim();
10031
+ return legacy.length > 0 ? legacy : void 0;
10032
+ }
9584
10033
  extractTableCellText(tableCell) {
9585
10034
  const paragraphs = this.context.ensureArray(tableCell?.["a:txBody"]?.["a:p"]);
9586
10035
  const lines = [];
@@ -9859,6 +10308,19 @@ var PptxGraphicFrameParser = class {
9859
10308
  if (graphicData["a:videoFile"] || graphicData["a:audioFile"] || uri.includes("/drawingml/2006/media")) {
9860
10309
  return "media";
9861
10310
  }
10311
+ if (graphicData["aink:ink"] || uri.includes("/2010/ink") || uri.includes("drawing/2010/ink")) {
10312
+ return "ink";
10313
+ }
10314
+ const alternateContent = graphicData["mc:AlternateContent"];
10315
+ if (alternateContent) {
10316
+ const choices = Array.isArray(alternateContent["mc:Choice"]) ? alternateContent["mc:Choice"] : alternateContent["mc:Choice"] ? [alternateContent["mc:Choice"]] : [];
10317
+ for (const choice of choices) {
10318
+ const requires = String(choice?.["@_Requires"] || "").toLowerCase();
10319
+ if (requires.includes("aink") || choice?.["aink:ink"]) {
10320
+ return "ink";
10321
+ }
10322
+ }
10323
+ }
9862
10324
  return "unknown";
9863
10325
  }
9864
10326
  };
@@ -10760,6 +11222,7 @@ var PptxDocumentPropertiesUpdater = class {
10760
11222
  }));
10761
11223
  if (sanitized.length === 0) {
10762
11224
  this.context.zip.remove("docProps/custom.xml");
11225
+ await this.removeCustomPropertiesPackagingArtifacts();
10763
11226
  return;
10764
11227
  }
10765
11228
  const customXml = {
@@ -10779,6 +11242,116 @@ var PptxDocumentPropertiesUpdater = class {
10779
11242
  }
10780
11243
  };
10781
11244
  this.context.zip.file("docProps/custom.xml", this.context.builder.build(customXml));
11245
+ await this.ensureCustomPropertiesPackagingArtifacts();
11246
+ }
11247
+ /**
11248
+ * Ensure `[Content_Types].xml` has an `Override` for `docProps/custom.xml`
11249
+ * and the root `_rels/.rels` references it (ECMA-376 §15.2.12.2 +
11250
+ * Part 2 §10.1.2.5). Without these, the package fails OPC validation
11251
+ * and Office strips the custom properties on next save.
11252
+ */
11253
+ async ensureCustomPropertiesPackagingArtifacts() {
11254
+ const customContentType = "application/vnd.openxmlformats-officedocument.custom-properties+xml";
11255
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
11256
+ const ctFile = this.context.zip.file("[Content_Types].xml");
11257
+ if (ctFile) {
11258
+ try {
11259
+ const ctXml = await ctFile.async("string");
11260
+ const ctData = this.context.parser.parse(ctXml);
11261
+ const types = ctData["Types"];
11262
+ if (types) {
11263
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
11264
+ const hasCustomOverride = overrides.some(
11265
+ (o) => String(o?.["@_PartName"] || "") === "/docProps/custom.xml"
11266
+ );
11267
+ if (!hasCustomOverride) {
11268
+ overrides.push({
11269
+ "@_PartName": "/docProps/custom.xml",
11270
+ "@_ContentType": customContentType
11271
+ });
11272
+ types["Override"] = overrides.length === 1 ? overrides[0] : overrides;
11273
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
11274
+ }
11275
+ }
11276
+ } catch (error) {
11277
+ console.warn("Failed to update [Content_Types].xml for custom properties:", error);
11278
+ }
11279
+ }
11280
+ const relsFile = this.context.zip.file("_rels/.rels");
11281
+ if (relsFile) {
11282
+ try {
11283
+ const relsXml = await relsFile.async("string");
11284
+ const relsData = this.context.parser.parse(relsXml);
11285
+ const relationships = relsData["Relationships"];
11286
+ if (relationships) {
11287
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
11288
+ const hasCustomRel = rels.some((r) => String(r?.["@_Type"] || "") === customRelType);
11289
+ if (!hasCustomRel) {
11290
+ let maxId = 0;
11291
+ for (const rel of rels) {
11292
+ const id = String(rel?.["@_Id"] || "");
11293
+ const num = Number.parseInt(id.replace(/^rId/, ""), 10);
11294
+ if (Number.isFinite(num) && num > maxId) {
11295
+ maxId = num;
11296
+ }
11297
+ }
11298
+ rels.push({
11299
+ "@_Id": `rId${maxId + 1}`,
11300
+ "@_Type": customRelType,
11301
+ "@_Target": "docProps/custom.xml"
11302
+ });
11303
+ relationships["Relationship"] = rels;
11304
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
11305
+ }
11306
+ }
11307
+ } catch (error) {
11308
+ console.warn("Failed to update _rels/.rels for custom properties:", error);
11309
+ }
11310
+ }
11311
+ }
11312
+ /**
11313
+ * Remove the Override + root rel for `docProps/custom.xml` when the
11314
+ * caller has emptied custom properties so the package doesn't keep an
11315
+ * orphan content-type entry referencing a deleted part.
11316
+ */
11317
+ async removeCustomPropertiesPackagingArtifacts() {
11318
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
11319
+ const ctFile = this.context.zip.file("[Content_Types].xml");
11320
+ if (ctFile) {
11321
+ try {
11322
+ const ctXml = await ctFile.async("string");
11323
+ const ctData = this.context.parser.parse(ctXml);
11324
+ const types = ctData["Types"];
11325
+ if (types) {
11326
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
11327
+ const filtered = overrides.filter(
11328
+ (o) => String(o?.["@_PartName"] || "") !== "/docProps/custom.xml"
11329
+ );
11330
+ if (filtered.length !== overrides.length) {
11331
+ types["Override"] = filtered.length === 1 ? filtered[0] : filtered;
11332
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
11333
+ }
11334
+ }
11335
+ } catch {
11336
+ }
11337
+ }
11338
+ const relsFile = this.context.zip.file("_rels/.rels");
11339
+ if (relsFile) {
11340
+ try {
11341
+ const relsXml = await relsFile.async("string");
11342
+ const relsData = this.context.parser.parse(relsXml);
11343
+ const relationships = relsData["Relationships"];
11344
+ if (relationships) {
11345
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
11346
+ const filtered = rels.filter((r) => String(r?.["@_Type"] || "") !== customRelType);
11347
+ if (filtered.length !== rels.length) {
11348
+ relationships["Relationship"] = filtered;
11349
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
11350
+ }
11351
+ }
11352
+ } catch {
11353
+ }
11354
+ }
10782
11355
  }
10783
11356
  normalizeCustomPropertyType(type) {
10784
11357
  const supportedTypes = /* @__PURE__ */ new Set([
@@ -11122,6 +11695,7 @@ var PptxSlideLoaderService = class {
11122
11695
  await params.loadSlideRelationships(path2, slideRelsPath);
11123
11696
  const clrMapOverride = params.parseSlideClrMapOverride(slideXmlObj);
11124
11697
  params.setCurrentSlideClrMapOverride(clrMapOverride);
11698
+ await params.setActiveMasterForSlide?.(path2);
11125
11699
  let restoreThemeOverride;
11126
11700
  try {
11127
11701
  const layoutPathForOverride = params.findLayoutPathForSlide(path2);
@@ -15873,6 +16447,20 @@ var AXIS_TYPE_MAP = {
15873
16447
  dateAx: "dateAx",
15874
16448
  serAx: "serAx"
15875
16449
  };
16450
+ function upsertChartAxisChild(parent, localName, value, getLocalName) {
16451
+ const existingKey = Object.keys(parent).find((k) => getLocalName(k) === localName);
16452
+ if (value === void 0) {
16453
+ if (existingKey) {
16454
+ delete parent[existingKey];
16455
+ }
16456
+ return;
16457
+ }
16458
+ if (existingKey) {
16459
+ parent[existingKey]["@_val"] = value;
16460
+ } else {
16461
+ parent[`c:${localName}`] = { "@_val": value };
16462
+ }
16463
+ }
15876
16464
  function parseChartAxes(plotArea, xmlLookup, colorParser, getLocalName) {
15877
16465
  const result = [];
15878
16466
  for (const key of Object.keys(plotArea)) {
@@ -15993,6 +16581,27 @@ function parseSingleAxis(axisNode, axisType, xmlLookup, colorParser) {
15993
16581
  if (dispUnitsNode) {
15994
16582
  parseDisplayUnits(dispUnitsNode, xmlLookup, result);
15995
16583
  }
16584
+ const majorUnitNode = xmlLookup.getChildByLocalName(axisNode, "majorUnit");
16585
+ if (majorUnitNode) {
16586
+ const majorVal = parseFloat(String(majorUnitNode["@_val"]));
16587
+ if (Number.isFinite(majorVal)) {
16588
+ result.majorUnit = majorVal;
16589
+ }
16590
+ }
16591
+ const minorUnitNode = xmlLookup.getChildByLocalName(axisNode, "minorUnit");
16592
+ if (minorUnitNode) {
16593
+ const minorVal = parseFloat(String(minorUnitNode["@_val"]));
16594
+ if (Number.isFinite(minorVal)) {
16595
+ result.minorUnit = minorVal;
16596
+ }
16597
+ }
16598
+ const tickLblPosNode = xmlLookup.getChildByLocalName(axisNode, "tickLblPos");
16599
+ if (tickLblPosNode) {
16600
+ const v = String(tickLblPosNode["@_val"] || "").trim();
16601
+ if (v === "high" || v === "low" || v === "nextTo" || v === "none") {
16602
+ result.tickLblPos = v;
16603
+ }
16604
+ }
15996
16605
  return result;
15997
16606
  }
15998
16607
  var VALID_DISPLAY_UNITS = /* @__PURE__ */ new Set([
@@ -17304,23 +17913,55 @@ var SHAPE_TREE_ELEMENT_TAGS = /* @__PURE__ */ new Set([
17304
17913
  "p16:model3D",
17305
17914
  ...VML_SHAPE_TAGS
17306
17915
  ]);
17916
+ function diagnoseSelection(ac) {
17917
+ const choices = ensureArray2(ac["mc:Choice"]);
17918
+ for (let i = 0; i < choices.length; i++) {
17919
+ const choice = choices[i];
17920
+ const requires = String(choice?.["@_Requires"] ?? "").trim();
17921
+ if (requires.length === 0 || areNamespacesSupported(requires)) {
17922
+ const resolved = resolveNestedAlternateContent(choice);
17923
+ return { branch: "choice", choiceIndex: i, resolved };
17924
+ }
17925
+ }
17926
+ const fallback = ac["mc:Fallback"];
17927
+ if (fallback) {
17928
+ return { branch: "fallback", resolved: resolveNestedAlternateContent(fallback) };
17929
+ }
17930
+ return void 0;
17931
+ }
17307
17932
  function unwrapAlternateContent(container) {
17308
17933
  const altContents = ensureArray2(container["mc:AlternateContent"]);
17309
17934
  if (altContents.length === 0) {
17310
- return;
17935
+ return [];
17311
17936
  }
17937
+ const blocks = [];
17312
17938
  for (const ac of altContents) {
17313
- const branch = selectAlternateContentBranch(ac);
17314
- if (!branch) {
17939
+ const diagnosis = diagnoseSelection(ac);
17940
+ if (!diagnosis) {
17315
17941
  continue;
17316
17942
  }
17943
+ const block = {
17944
+ rawAc: ac,
17945
+ selectedBranch: diagnosis.branch,
17946
+ choiceIndex: diagnosis.branch === "choice" ? diagnosis.choiceIndex : void 0,
17947
+ childRefs: []
17948
+ };
17949
+ const branch = diagnosis.resolved;
17317
17950
  for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
17318
17951
  const children = ensureArray2(branch[tag]);
17319
17952
  if (children.length > 0) {
17320
17953
  container[tag] = [...ensureArray2(container[tag]), ...children];
17954
+ for (const child of children) {
17955
+ block.childRefs.push({ tag, node: child });
17956
+ }
17321
17957
  }
17322
17958
  }
17959
+ if (block.childRefs.length > 0) {
17960
+ blocks.push(block);
17961
+ }
17323
17962
  }
17963
+ delete container["mc:AlternateContent"];
17964
+ return blocks;
17324
17965
  }
17325
17966
  function ensureArray2(val) {
17326
17967
  if (!val) {
@@ -17332,7 +17973,7 @@ function ensureArray2(val) {
17332
17973
 
17333
17974
  // src/core/utils/body-properties-parser.ts
17334
17975
  function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17335
- const parseBoolAttr = (attr) => {
17976
+ const parseBoolAttr2 = (attr) => {
17336
17977
  const raw = bodyPr[attr];
17337
17978
  if (raw === void 0) {
17338
17979
  return void 0;
@@ -17340,19 +17981,19 @@ function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
17340
17981
  const val = String(raw).trim().toLowerCase();
17341
17982
  return val === "1" || val === "true";
17342
17983
  };
17343
- const compatLnSpc = parseBoolAttr("@_compatLnSpc");
17984
+ const compatLnSpc = parseBoolAttr2("@_compatLnSpc");
17344
17985
  if (compatLnSpc !== void 0) {
17345
17986
  textStyle.compatibleLineSpacing = compatLnSpc;
17346
17987
  }
17347
- const forceAA = parseBoolAttr("@_forceAA");
17988
+ const forceAA = parseBoolAttr2("@_forceAA");
17348
17989
  if (forceAA !== void 0) {
17349
17990
  textStyle.forceAntiAlias = forceAA;
17350
17991
  }
17351
- const upright = parseBoolAttr("@_upright");
17992
+ const upright = parseBoolAttr2("@_upright");
17352
17993
  if (upright !== void 0) {
17353
17994
  textStyle.upright = upright;
17354
17995
  }
17355
- const fromWordArt = parseBoolAttr("@_fromWordArt");
17996
+ const fromWordArt = parseBoolAttr2("@_fromWordArt");
17356
17997
  if (fromWordArt !== void 0) {
17357
17998
  textStyle.fromWordArt = fromWordArt;
17358
17999
  }
@@ -17375,46 +18016,6 @@ function writeBodyPrBooleanAttrs(bodyPr, textStyle) {
17375
18016
  }
17376
18017
  }
17377
18018
 
17378
- // src/core/utils/theme-override-utils.ts
17379
- var COLOR_MAP_ALIAS_KEYS = [
17380
- "bg1",
17381
- "tx1",
17382
- "bg2",
17383
- "tx2",
17384
- "accent1",
17385
- "accent2",
17386
- "accent3",
17387
- "accent4",
17388
- "accent5",
17389
- "accent6",
17390
- "hlink",
17391
- "folHlink"
17392
- ];
17393
- var DEFAULT_COLOR_MAP = {
17394
- bg1: "lt1",
17395
- tx1: "dk1",
17396
- bg2: "lt2",
17397
- tx2: "dk2",
17398
- accent1: "accent1",
17399
- accent2: "accent2",
17400
- accent3: "accent3",
17401
- accent4: "accent4",
17402
- accent5: "accent5",
17403
- accent6: "accent6",
17404
- hlink: "hlink",
17405
- folHlink: "folHlink"
17406
- };
17407
- function buildClrMapOverrideXml(override) {
17408
- if (!override || Object.keys(override).length === 0) {
17409
- return { "a:masterClrMapping": {} };
17410
- }
17411
- const attrs2 = {};
17412
- for (const key of COLOR_MAP_ALIAS_KEYS) {
17413
- attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
17414
- }
17415
- return { "a:overrideClrMapping": attrs2 };
17416
- }
17417
-
17418
18019
  // src/core/utils/data-url-utils.ts
17419
18020
  function parseDataUrlToBytes(dataUrl) {
17420
18021
  const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
@@ -17512,6 +18113,46 @@ async function fetchUrlToBytes(url) {
17512
18113
  }
17513
18114
  }
17514
18115
 
18116
+ // src/core/utils/theme-override-utils.ts
18117
+ var COLOR_MAP_ALIAS_KEYS = [
18118
+ "bg1",
18119
+ "tx1",
18120
+ "bg2",
18121
+ "tx2",
18122
+ "accent1",
18123
+ "accent2",
18124
+ "accent3",
18125
+ "accent4",
18126
+ "accent5",
18127
+ "accent6",
18128
+ "hlink",
18129
+ "folHlink"
18130
+ ];
18131
+ var DEFAULT_COLOR_MAP = {
18132
+ bg1: "lt1",
18133
+ tx1: "dk1",
18134
+ bg2: "lt2",
18135
+ tx2: "dk2",
18136
+ accent1: "accent1",
18137
+ accent2: "accent2",
18138
+ accent3: "accent3",
18139
+ accent4: "accent4",
18140
+ accent5: "accent5",
18141
+ accent6: "accent6",
18142
+ hlink: "hlink",
18143
+ folHlink: "folHlink"
18144
+ };
18145
+ function buildClrMapOverrideXml(override) {
18146
+ if (!override || Object.keys(override).length === 0) {
18147
+ return { "a:masterClrMapping": {} };
18148
+ }
18149
+ const attrs2 = {};
18150
+ for (const key of COLOR_MAP_ALIAS_KEYS) {
18151
+ attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
18152
+ }
18153
+ return { "a:overrideClrMapping": attrs2 };
18154
+ }
18155
+
17515
18156
  // src/core/utils/encryption-detection.ts
17516
18157
  var OLE_MAGIC = new Uint8Array([208, 207, 17, 224, 161, 27, 26, 225]);
17517
18158
  var ZIP_MAGIC = new Uint8Array([80, 75]);
@@ -23274,7 +23915,7 @@ function parseActiveXControlsFromSlide(slideXml2) {
23274
23915
  }
23275
23916
 
23276
23917
  // src/core/utils/theme-switching.ts
23277
- function normalizeHex(hex) {
23918
+ function normalizeHex2(hex) {
23278
23919
  if (!hex) {
23279
23920
  return "";
23280
23921
  }
@@ -23284,8 +23925,8 @@ function buildColorRemapTable(oldColorMap, newColorMap) {
23284
23925
  const remap = /* @__PURE__ */ new Map();
23285
23926
  const allKeys = [...THEME_COLOR_SCHEME_KEYS, "tx1", "bg1", "tx2", "bg2"];
23286
23927
  for (const key of allKeys) {
23287
- const oldVal = normalizeHex(oldColorMap[key]);
23288
- const newVal = normalizeHex(newColorMap[key]);
23928
+ const oldVal = normalizeHex2(oldColorMap[key]);
23929
+ const newVal = normalizeHex2(newColorMap[key]);
23289
23930
  if (oldVal && newVal && oldVal !== newVal) {
23290
23931
  remap.set(oldVal, `#${newVal}`);
23291
23932
  remap.set(`#${oldVal}`, `#${newVal}`);
@@ -23297,7 +23938,7 @@ function remapColor(color, remap) {
23297
23938
  if (!color) {
23298
23939
  return color;
23299
23940
  }
23300
- const normalized = normalizeHex(color);
23941
+ const normalized = normalizeHex2(color);
23301
23942
  const remapped = remap.get(normalized) ?? remap.get(`#${normalized}`);
23302
23943
  return remapped ?? color;
23303
23944
  }
@@ -23437,7 +24078,7 @@ function remapSlideColors(slide, remap) {
23437
24078
  function buildThemeColorMap(colorScheme) {
23438
24079
  const map = {};
23439
24080
  for (const key of THEME_COLOR_SCHEME_KEYS) {
23440
- map[key] = normalizeHex(colorScheme[key]);
24081
+ map[key] = normalizeHex2(colorScheme[key]);
23441
24082
  }
23442
24083
  map.tx1 = map.dk1;
23443
24084
  map.bg1 = map.lt1;
@@ -23473,8 +24114,11 @@ function applyThemeToData(data, newColorScheme, newFontScheme, themeName) {
23473
24114
 
23474
24115
  // src/core/core/runtime/PptxHandlerRuntimeSaveParagraphHelpers.ts
23475
24116
  var EMU_PER_PX4 = 9525;
23476
- function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing) {
24117
+ function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing, level) {
23477
24118
  const paragraphProps = {};
24119
+ if (typeof level === "number" && Number.isFinite(level) && level > 0) {
24120
+ paragraphProps["@_lvl"] = String(Math.min(Math.max(Math.round(level), 0), 8));
24121
+ }
23478
24122
  if (paragraphAlign) {
23479
24123
  paragraphProps["@_algn"] = paragraphAlign;
23480
24124
  }
@@ -23546,23 +24190,28 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23546
24190
  paragraphProps["a:buNone"] = {};
23547
24191
  return;
23548
24192
  }
23549
- if (bulletInfo.color) {
24193
+ if (bulletInfo.colorInherit) {
24194
+ paragraphProps["a:buClrTx"] = {};
24195
+ } else if (bulletInfo.color) {
23550
24196
  const colorHex = bulletInfo.color.replace("#", "");
23551
24197
  paragraphProps["a:buClr"] = {
23552
24198
  "a:srgbClr": { "@_val": colorHex }
23553
24199
  };
23554
24200
  }
23555
- if (bulletInfo.sizePercent !== void 0) {
24201
+ if (bulletInfo.sizeInherit) {
24202
+ paragraphProps["a:buSzTx"] = {};
24203
+ } else if (bulletInfo.sizePercent !== void 0) {
23556
24204
  paragraphProps["a:buSzPct"] = {
23557
24205
  "@_val": String(Math.round(bulletInfo.sizePercent * 1e3))
23558
24206
  };
23559
- }
23560
- if (bulletInfo.sizePts !== void 0) {
24207
+ } else if (bulletInfo.sizePts !== void 0) {
23561
24208
  paragraphProps["a:buSzPts"] = {
23562
24209
  "@_val": String(Math.round(bulletInfo.sizePts * 100))
23563
24210
  };
23564
24211
  }
23565
- if (bulletInfo.fontFamily) {
24212
+ if (bulletInfo.fontInherit) {
24213
+ paragraphProps["a:buFontTx"] = {};
24214
+ } else if (bulletInfo.fontFamily) {
23566
24215
  paragraphProps["a:buFont"] = {
23567
24216
  "@_typeface": bulletInfo.fontFamily
23568
24217
  };
@@ -23585,7 +24234,7 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
23585
24234
  };
23586
24235
  }
23587
24236
  }
23588
- function assembleParagraphXml(runs, paragraphProps) {
24237
+ function assembleParagraphXml(runs, paragraphProps, endParaRunProperties) {
23589
24238
  const paragraph = {
23590
24239
  "a:pPr": paragraphProps
23591
24240
  };
@@ -23607,7 +24256,11 @@ function assembleParagraphXml(runs, paragraphProps) {
23607
24256
  if (cleanRegularRuns.length === 0 && fieldRuns.length === 0) {
23608
24257
  paragraph["a:r"] = runs.length > 1 ? runs : runs[0];
23609
24258
  }
23610
- paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
24259
+ if (endParaRunProperties && typeof endParaRunProperties === "object") {
24260
+ paragraph["a:endParaRPr"] = endParaRunProperties;
24261
+ } else {
24262
+ paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
24263
+ }
23611
24264
  return paragraph;
23612
24265
  }
23613
24266
  function computeUniformSegmentOverrides(textStyle, textSegments) {
@@ -23911,6 +24564,140 @@ var PptxHandlerRuntime = class {
23911
24564
  * `p:clrMapOvr / a:overrideClrMapping`.
23912
24565
  */
23913
24566
  currentSlideClrMapOverride = null;
24567
+ /**
24568
+ * Per-master colour map alias dictionaries parsed from each master's
24569
+ * `<p:clrMap>` element (e.g. `bg1 → lt1`, `tx1 → dk1`, `accent1 → accent1`).
24570
+ *
24571
+ * `clrMap` is the *aliasing* layer between logical colour names used in
24572
+ * DrawingML and the raw theme scheme slots. Per ECMA-376 §19.3.1.7 it
24573
+ * lives on each `p:sldMaster`, and slide layouts/slides may further
24574
+ * override it via `p:clrMapOvr`. Resolution must happen at colour-lookup
24575
+ * time, *not* by baking it into {@link themeColorMap}.
24576
+ *
24577
+ * Phase 2 Stream B / C-H4.
24578
+ */
24579
+ masterClrMaps = /* @__PURE__ */ new Map();
24580
+ /**
24581
+ * Per-master theme color maps. Each master may reference its own theme
24582
+ * file via `_rels/slideMasterN.xml.rels`. For multi-master decks, slides
24583
+ * must resolve scheme colours against their *own* master's theme.
24584
+ *
24585
+ * Falls back to {@link themeColorMap} when a master entry is missing.
24586
+ *
24587
+ * Phase 2 Stream B / C-H4.
24588
+ */
24589
+ masterThemeColorMaps = /* @__PURE__ */ new Map();
24590
+ /**
24591
+ * Per-master theme font maps. Same rationale as
24592
+ * {@link masterThemeColorMaps}: multi-master decks may have one font
24593
+ * scheme per theme.
24594
+ */
24595
+ masterThemeFontMaps = /* @__PURE__ */ new Map();
24596
+ /**
24597
+ * Per-master format schemes (fmtScheme). For multi-master decks each
24598
+ * master's slides should resolve fill/line/effect refs against the
24599
+ * matrix from that master's theme.
24600
+ */
24601
+ masterThemeFormatSchemes = /* @__PURE__ */ new Map();
24602
+ /**
24603
+ * Per-master mapping from slide-master path to the theme path it
24604
+ * references via `_rels/slideMasterN.xml.rels`. Populated by
24605
+ * {@link loadPerMasterThemes} during load. Used by the save-side
24606
+ * theme writer to know which themeN.xml to (re)emit for each master.
24607
+ *
24608
+ * Phase 4 Stream A / C-H3.
24609
+ */
24610
+ masterThemePaths = /* @__PURE__ */ new Map();
24611
+ /**
24612
+ * Per-script font tables for major and minor fonts. Captured per master
24613
+ * theme. Keys are master paths; values map `mj`/`mn` -> script tag (e.g.
24614
+ * `Hans`, `Hant`, `Arab`, `Hebr`, `Thai`, `Beng`, …) -> typeface name.
24615
+ *
24616
+ * Phase 4 Stream A / M4.
24617
+ */
24618
+ masterThemeMajorFontScripts = /* @__PURE__ */ new Map();
24619
+ masterThemeMinorFontScripts = /* @__PURE__ */ new Map();
24620
+ /**
24621
+ * Theme name attribute (`<a:theme @name>`) per master theme path.
24622
+ * Captured for byte-stable round-trip.
24623
+ */
24624
+ masterThemeNames = /* @__PURE__ */ new Map();
24625
+ /**
24626
+ * `<a:fontScheme @name>` per master theme path.
24627
+ */
24628
+ masterThemeFontSchemeNames = /* @__PURE__ */ new Map();
24629
+ /**
24630
+ * `<a:clrScheme @name>` per master theme path.
24631
+ */
24632
+ masterThemeColorSchemeNames = /* @__PURE__ */ new Map();
24633
+ /**
24634
+ * Raw original theme XML keyed by theme path. Captured at load-time.
24635
+ * Used by the save pipeline to passthrough the full theme XML when no
24636
+ * in-memory mutation has occurred — preserving fillStyleLst /
24637
+ * lnStyleLst / effectStyleLst / bgFillStyleLst /
24638
+ * extraClrSchemeLst / objectDefaults / extLst exactly as written.
24639
+ *
24640
+ * Phase 4 Stream A / C-H3.
24641
+ */
24642
+ originalThemeXmlByPath = /* @__PURE__ */ new Map();
24643
+ /**
24644
+ * Set of theme paths whose in-memory state has been mutated since
24645
+ * load. Saving a theme path that's NOT dirty is a no-op (the original
24646
+ * file already exists in the ZIP). Saving a dirty theme path
24647
+ * regenerates the part from in-memory state.
24648
+ *
24649
+ * Phase 4 Stream A / C-H3.
24650
+ */
24651
+ dirtyThemePaths = /* @__PURE__ */ new Set();
24652
+ /**
24653
+ * Per-master parsed `<p:txStyles>` (titleStyle/bodyStyle/otherStyle).
24654
+ * Populated by {@link enrichSlideMastersWithTxStyles} during load so the
24655
+ * inheritance chain can find the master text-style cascade without
24656
+ * re-parsing master XML on every lookup. Phase 4 Stream B / P-H1.
24657
+ */
24658
+ masterTxStylesCache = /* @__PURE__ */ new Map();
24659
+ /**
24660
+ * Captured `<a:objectDefaults>` snapshot per master theme path. The
24661
+ * full ECMA-376 inheritance chain (master / layout / placeholder /
24662
+ * objectDefaults) is non-trivial; we store the raw spDef/lnDef/txDef
24663
+ * subtrees and re-emit them verbatim for round-trip.
24664
+ *
24665
+ * Phase 4 Stream A / M5.
24666
+ */
24667
+ masterThemeObjectDefaults = /* @__PURE__ */ new Map();
24668
+ /**
24669
+ * Captured `<a:extraClrSchemeLst>` raw subtree per master theme path
24670
+ * for verbatim round-trip.
24671
+ *
24672
+ * Phase 4 Stream A.
24673
+ */
24674
+ masterThemeExtraClrSchemeLst = /* @__PURE__ */ new Map();
24675
+ /**
24676
+ * Captured `<a:custClrLst>` raw subtree per master theme path
24677
+ * for verbatim round-trip.
24678
+ *
24679
+ * Phase 4 Stream A.
24680
+ */
24681
+ masterThemeCustClrLst = /* @__PURE__ */ new Map();
24682
+ /**
24683
+ * Captured theme-level `<a:extLst>` raw subtree per master theme path.
24684
+ */
24685
+ masterThemeExtLst = /* @__PURE__ */ new Map();
24686
+ /**
24687
+ * Active master's clrMap for the slide currently being parsed. Walked
24688
+ * after `currentSlideClrMapOverride` (slide and layout overrides take
24689
+ * precedence). `null` means "fall through to themeColorMap directly".
24690
+ */
24691
+ currentMasterClrMap = null;
24692
+ /**
24693
+ * Snapshot of the global theme state taken right after
24694
+ * {@link loadThemeData} completes. Used as the fallback when a slide's
24695
+ * master has no per-master theme entry, so per-slide multi-master
24696
+ * switching does not leak the previous slide's master state.
24697
+ */
24698
+ globalThemeColorMapSnapshot = {};
24699
+ globalThemeFontMapSnapshot = {};
24700
+ globalThemeFormatSchemeSnapshot;
23914
24701
  /** Thumbnail image data from `docProps/thumbnail.jpeg` preserved for round-trip. */
23915
24702
  thumbnailData = null;
23916
24703
  /** Raw VBA project binary preserved for macro-enabled (.pptm) round-trip. */
@@ -23921,6 +24708,19 @@ var PptxHandlerRuntime = class {
23921
24708
  signatureDetection = null;
23922
24709
  /** Custom XML data parts parsed from `customXml/` in the OPC package. */
23923
24710
  customXmlParts = [];
24711
+ /**
24712
+ * Maps an element's `rawXml` reference to the `mc:AlternateContent`
24713
+ * envelope that originally wrapped it (CC-4). Populated during slide
24714
+ * (and `p:grpSp`) parsing; consulted at save time to re-emit the
24715
+ * original `<mc:Choice>` / `<mc:Fallback>` shape so legacy renderers
24716
+ * keep their fallback content.
24717
+ *
24718
+ * Multiple sibling elements may share the same `AlternateContentBlock`
24719
+ * value (a single AC envelope often wraps several child shapes — e.g.
24720
+ * `p14:media` + its `p:pic` fallback nest one each). WeakMap so AC
24721
+ * envelopes are GC'd if the parsed XmlObject is dropped.
24722
+ */
24723
+ alternateContentBlockByRawXml = /* @__PURE__ */ new WeakMap();
23924
24724
  /** Embedded fonts extracted during load, preserved for automatic re-embedding on save. */
23925
24725
  loadedEmbeddedFonts = [];
23926
24726
  /** Map of comment author IDs to display names (from `ppt/commentAuthors.xml`). */
@@ -25734,7 +26534,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25734
26534
  const bgColor = this.parseBackgroundColor(bg);
25735
26535
  const spTree = master["p:cSld"]?.["p:spTree"];
25736
26536
  const placeholders = this.extractPlaceholderList(spTree);
25737
- return { path: path2, backgroundColor: bgColor, placeholders };
26537
+ const result = { path: path2, backgroundColor: bgColor, placeholders };
26538
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
26539
+ if (hf) {
26540
+ result.headerFooter = hf;
26541
+ }
26542
+ return result;
25738
26543
  } catch (e) {
25739
26544
  console.warn("Failed to parse handout master:", e);
25740
26545
  return void 0;
@@ -25760,7 +26565,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25760
26565
  const bgColor = this.parseBackgroundColor(bg);
25761
26566
  const spTree = master["p:cSld"]?.["p:spTree"];
25762
26567
  const placeholders = this.extractPlaceholderList(spTree);
25763
- return { path: path2, backgroundColor: bgColor, placeholders };
26568
+ const result = { path: path2, backgroundColor: bgColor, placeholders };
26569
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
26570
+ if (hf) {
26571
+ result.headerFooter = hf;
26572
+ }
26573
+ return result;
25764
26574
  } catch (e) {
25765
26575
  console.warn("Failed to parse notes master:", e);
25766
26576
  return void 0;
@@ -25805,6 +26615,10 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
25805
26615
  const uVal = String(userDrawn).trim().toLowerCase();
25806
26616
  layout.userDrawn = uVal === "1" || uVal === "true";
25807
26617
  }
26618
+ const hf = parseHeaderFooterFlags(sldLayout["p:hf"]);
26619
+ if (hf) {
26620
+ layout.headerFooter = hf;
26621
+ }
25808
26622
  const clrMapOvr = sldLayout["p:clrMapOvr"];
25809
26623
  if (clrMapOvr && clrMapOvr["a:masterClrMapping"] === void 0) {
25810
26624
  const overrideNode = clrMapOvr["a:overrideClrMapping"];
@@ -26410,6 +27224,18 @@ function buildClrChangeNode(style) {
26410
27224
  }
26411
27225
 
26412
27226
  // src/core/core/runtime/PptxHandlerRuntimeSaveRunProperties.ts
27227
+ function applyFontMetadata(fontNode, panose, pitchFamily, charset) {
27228
+ if (panose && panose.length > 0) {
27229
+ fontNode["@_panose"] = panose;
27230
+ }
27231
+ if (typeof pitchFamily === "number" && Number.isFinite(pitchFamily)) {
27232
+ fontNode["@_pitchFamily"] = String(pitchFamily);
27233
+ }
27234
+ if (typeof charset === "number" && Number.isFinite(charset)) {
27235
+ fontNode["@_charset"] = String(charset);
27236
+ }
27237
+ return fontNode;
27238
+ }
26413
27239
  var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime11 {
26414
27240
  createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId) {
26415
27241
  const runProps = {
@@ -26467,6 +27293,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26467
27293
  if (style.bookmark) {
26468
27294
  runProps["@_bmk"] = style.bookmark;
26469
27295
  }
27296
+ if (style.altLanguage) {
27297
+ runProps["@_altLang"] = style.altLanguage;
27298
+ }
27299
+ if (typeof style.smartTagId === "number" && Number.isFinite(style.smartTagId)) {
27300
+ runProps["@_smtId"] = String(style.smartTagId);
27301
+ }
26470
27302
  if (style.textOutlineWidth || style.textOutlineColor) {
26471
27303
  const lnObj = {};
26472
27304
  if (typeof style.textOutlineWidth === "number" && style.textOutlineWidth > 0) {
@@ -26482,11 +27314,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26482
27314
  runProps["a:ln"] = lnObj;
26483
27315
  }
26484
27316
  if (style.color) {
26485
- runProps["a:solidFill"] = {
26486
- "a:srgbClr": {
26487
- "@_val": style.color.replace("#", "")
26488
- }
26489
- };
27317
+ const resolvedOriginalColor = style.colorXml ? this.parseColor(style.colorXml) : void 0;
27318
+ runProps["a:solidFill"] = serializeColorChoice(
27319
+ style.colorXml,
27320
+ resolvedOriginalColor,
27321
+ style.color
27322
+ );
26490
27323
  } else if (style.textFillGradientStops && style.textFillGradientStops.length > 0) {
26491
27324
  const gradStops = style.textFillGradientStops.filter((stop) => Boolean(stop?.color)).map((stop) => {
26492
27325
  const rawPos = (stop.position ?? 0) / 100;
@@ -26559,16 +27392,32 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26559
27392
  };
26560
27393
  }
26561
27394
  if (style.fontFamily) {
26562
- runProps["a:latin"] = { "@_typeface": style.fontFamily };
26563
- runProps["a:ea"] = {
26564
- "@_typeface": style.eastAsiaFont || style.fontFamily
26565
- };
26566
- runProps["a:cs"] = {
26567
- "@_typeface": style.complexScriptFont || style.fontFamily
26568
- };
27395
+ runProps["a:latin"] = applyFontMetadata(
27396
+ { "@_typeface": style.fontFamily },
27397
+ style.latinFontPanose,
27398
+ style.latinFontPitchFamily,
27399
+ style.latinFontCharset
27400
+ );
27401
+ runProps["a:ea"] = applyFontMetadata(
27402
+ { "@_typeface": style.eastAsiaFont || style.fontFamily },
27403
+ style.eastAsiaFontPanose,
27404
+ style.eastAsiaFontPitchFamily,
27405
+ style.eastAsiaFontCharset
27406
+ );
27407
+ runProps["a:cs"] = applyFontMetadata(
27408
+ { "@_typeface": style.complexScriptFont || style.fontFamily },
27409
+ style.complexScriptFontPanose,
27410
+ style.complexScriptFontPitchFamily,
27411
+ style.complexScriptFontCharset
27412
+ );
26569
27413
  }
26570
27414
  if (style.symbolFont) {
26571
- runProps["a:sym"] = { "@_typeface": style.symbolFont };
27415
+ runProps["a:sym"] = applyFontMetadata(
27416
+ { "@_typeface": style.symbolFont },
27417
+ style.symbolFontPanose,
27418
+ style.symbolFontPitchFamily,
27419
+ style.symbolFontCharset
27420
+ );
26572
27421
  }
26573
27422
  if (style.hyperlink && resolveHyperlinkRelationshipId) {
26574
27423
  const hyperlinkTarget = String(style.hyperlink).trim();
@@ -26643,14 +27492,15 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26643
27492
  lineSpacing: this.createLineSpacingXmlFromMultiplier(textStyle?.lineSpacing),
26644
27493
  lineSpacingExactPt: textStyle?.lineSpacingExactPt
26645
27494
  };
26646
- const createParagraph = (runs, bulletInfo) => {
27495
+ const createParagraph = (runs, bulletInfo, level, endParaRunProperties) => {
26647
27496
  const paragraphProps = buildParagraphPropertiesXml(
26648
27497
  textStyle,
26649
27498
  paragraphAlign,
26650
27499
  bulletInfo,
26651
- spacing
27500
+ spacing,
27501
+ level
26652
27502
  );
26653
- return assembleParagraphXml(runs, paragraphProps);
27503
+ return assembleParagraphXml(runs, paragraphProps, endParaRunProperties);
26654
27504
  };
26655
27505
  const createRun = (runText, style) => ({
26656
27506
  "a:rPr": this.createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId),
@@ -26698,13 +27548,19 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26698
27548
  const paragraphs = [];
26699
27549
  let currentRuns = [];
26700
27550
  let currentBulletInfo;
27551
+ let currentLevel;
27552
+ let currentEndParaRunProperties;
26701
27553
  const pushParagraph = () => {
26702
27554
  if (currentRuns.length === 0) {
26703
27555
  currentRuns.push(createRun("", textStyle));
26704
27556
  }
26705
- paragraphs.push(createParagraph(currentRuns, currentBulletInfo));
27557
+ paragraphs.push(
27558
+ createParagraph(currentRuns, currentBulletInfo, currentLevel, currentEndParaRunProperties)
27559
+ );
26706
27560
  currentRuns = [];
26707
27561
  currentBulletInfo = void 0;
27562
+ currentLevel = void 0;
27563
+ currentEndParaRunProperties = void 0;
26708
27564
  };
26709
27565
  if (textSegments && textSegments.length > 0) {
26710
27566
  const uniformSegmentOverrides = computeUniformSegmentOverrides(textStyle, textSegments);
@@ -26716,8 +27572,16 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
26716
27572
  };
26717
27573
  const segmentText = String(segment.text ?? "");
26718
27574
  const lineParts = segmentText.split("\n");
26719
- if (currentRuns.length === 0 && segment.bulletInfo) {
26720
- currentBulletInfo = segment.bulletInfo;
27575
+ if (currentRuns.length === 0) {
27576
+ if (segment.bulletInfo) {
27577
+ currentBulletInfo = segment.bulletInfo;
27578
+ }
27579
+ if (segment.paragraphLevel !== void 0) {
27580
+ currentLevel = segment.paragraphLevel;
27581
+ }
27582
+ if (segment.endParaRunProperties) {
27583
+ currentEndParaRunProperties = segment.endParaRunProperties;
27584
+ }
26721
27585
  }
26722
27586
  lineParts.forEach((linePart, lineIndex) => {
26723
27587
  if (segment.rubyText !== void 0) {
@@ -26815,6 +27679,9 @@ var PptxHandlerRuntime14 = class extends PptxHandlerRuntime13 {
26815
27679
  };
26816
27680
 
26817
27681
  // src/core/core/runtime/PptxHandlerRuntimeSaveShapeXml.ts
27682
+ var OLE_OBJECT_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject";
27683
+ var IMAGE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
27684
+ var OLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/presentationml/2006/ole";
26818
27685
  var PptxHandlerRuntime15 = class _PptxHandlerRuntime extends PptxHandlerRuntime14 {
26819
27686
  /**
26820
27687
  * Build a `p:graphicFrame` XML skeleton for an SDK-created table.
@@ -26865,6 +27732,148 @@ var PptxHandlerRuntime15 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
26865
27732
  }
26866
27733
  };
26867
27734
  }
27735
+ /**
27736
+ * Build a `p:graphicFrame` XML skeleton for an OLE object element.
27737
+ *
27738
+ * Used both for SDK-created OLE elements (no `rawXml`) and to refresh
27739
+ * a few key attributes on a loaded element when the typed fields have
27740
+ * been mutated. The output is the canonical
27741
+ * `p:graphicFrame > a:graphic > a:graphicData uri="…/ole" > p:oleObj`
27742
+ * shape per ECMA-376 §19.3.1.34 / §13.3.4.
27743
+ *
27744
+ * The caller (`processSlideElement`) is responsible for ensuring the
27745
+ * embed / preview-image relationships referenced from `r:id` / `r:embed`
27746
+ * exist in the slide's rels file. This method does not register them
27747
+ * itself because the typed model does not currently carry the binary
27748
+ * payload — the binary part must already be in the package (loaded from
27749
+ * the original file). A fully-fabricated SDK OLE element therefore
27750
+ * still requires the consumer to attach the binary out-of-band; this
27751
+ * method simply emits a schema-valid envelope referencing the
27752
+ * specified relationship ID.
27753
+ */
27754
+ createOleGraphicFrameXml(el, embedRelationshipId) {
27755
+ const EMU = _PptxHandlerRuntime.EMU_PER_PX;
27756
+ const offX = String(Math.round(el.x * EMU));
27757
+ const offY = String(Math.round(el.y * EMU));
27758
+ const extCx = String(Math.round(Math.max(el.width, 1) * EMU));
27759
+ const extCy = String(Math.round(Math.max(el.height, 1) * EMU));
27760
+ const oleObj = {
27761
+ "@_showAsIcon": "0",
27762
+ "@_imgW": extCx,
27763
+ "@_imgH": extCy
27764
+ };
27765
+ if (el.oleProgId) {
27766
+ oleObj["@_progId"] = el.oleProgId;
27767
+ }
27768
+ if (el.oleName) {
27769
+ oleObj["@_name"] = el.oleName;
27770
+ }
27771
+ if (el.oleClsId) {
27772
+ oleObj["@_classid"] = el.oleClsId;
27773
+ }
27774
+ if (embedRelationshipId) {
27775
+ oleObj["@_r:id"] = embedRelationshipId;
27776
+ }
27777
+ if (el.isLinked) {
27778
+ oleObj["p:link"] = {
27779
+ "@_r:id": embedRelationshipId,
27780
+ "@_updateAutomatic": "1"
27781
+ };
27782
+ } else {
27783
+ oleObj["p:embed"] = {};
27784
+ }
27785
+ oleObj["p:pic"] = {
27786
+ "p:nvPicPr": {
27787
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || "OleObject" },
27788
+ "p:cNvPicPr": {},
27789
+ "p:nvPr": {}
27790
+ },
27791
+ "p:blipFill": {
27792
+ "a:blip": {},
27793
+ "a:stretch": { "a:fillRect": {} }
27794
+ },
27795
+ "p:spPr": {
27796
+ "a:xfrm": {
27797
+ "a:off": { "@_x": offX, "@_y": offY },
27798
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27799
+ },
27800
+ "a:prstGeom": { "@_prst": "rect", "a:avLst": {} }
27801
+ }
27802
+ };
27803
+ return {
27804
+ "p:nvGraphicFramePr": {
27805
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || el.fileName || "OleObject" },
27806
+ "p:cNvGraphicFramePr": {
27807
+ "a:graphicFrameLocks": { "@_noChangeAspect": "1" }
27808
+ },
27809
+ "p:nvPr": {}
27810
+ },
27811
+ "p:xfrm": {
27812
+ "a:off": { "@_x": offX, "@_y": offY },
27813
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
27814
+ },
27815
+ "a:graphic": {
27816
+ "a:graphicData": {
27817
+ "@_uri": OLE_GRAPHIC_DATA_URI,
27818
+ "p:oleObj": oleObj
27819
+ }
27820
+ }
27821
+ };
27822
+ }
27823
+ /**
27824
+ * Refresh editable typed-field attributes on a loaded OLE graphicFrame's
27825
+ * raw XML. Only attributes that round-trip through the typed model
27826
+ * (`progId`, `name`, `classid`) are touched so unknown extension data
27827
+ * passes through verbatim.
27828
+ */
27829
+ applyOleTypedFieldUpdates(shape, el) {
27830
+ const oleObj = shape["a:graphic"]?.["a:graphicData"]?.["p:oleObj"];
27831
+ if (!oleObj) {
27832
+ return;
27833
+ }
27834
+ if (el.oleProgId) {
27835
+ oleObj["@_progId"] = el.oleProgId;
27836
+ }
27837
+ if (el.oleName !== void 0) {
27838
+ if (el.oleName.length > 0) {
27839
+ oleObj["@_name"] = el.oleName;
27840
+ } else {
27841
+ delete oleObj["@_name"];
27842
+ }
27843
+ }
27844
+ if (el.oleClsId) {
27845
+ oleObj["@_classid"] = el.oleClsId;
27846
+ }
27847
+ }
27848
+ /** Look up the existing OLE binary relationship ID for this slide, if any. */
27849
+ resolveOleEmbedRelationshipId(slideRelationships, oleTarget) {
27850
+ if (!oleTarget) {
27851
+ return void 0;
27852
+ }
27853
+ const normalisedTarget = oleTarget.replace(/^ppt\//, "../").replace(/^\/+/, "");
27854
+ const lowerTarget = normalisedTarget.toLowerCase();
27855
+ for (const rel of slideRelationships) {
27856
+ const relType = String(rel?.["@_Type"] || "");
27857
+ if (relType !== OLE_OBJECT_RELATIONSHIP_TYPE) {
27858
+ continue;
27859
+ }
27860
+ const target = String(rel?.["@_Target"] || "").toLowerCase().trim();
27861
+ if (target === lowerTarget || target.endsWith(lowerTarget) || lowerTarget.endsWith(target)) {
27862
+ const relId = String(rel?.["@_Id"] || "").trim();
27863
+ if (relId.length > 0) {
27864
+ return relId;
27865
+ }
27866
+ }
27867
+ }
27868
+ const fallback = slideRelationships.find(
27869
+ (rel) => String(rel?.["@_Type"] || "") === OLE_OBJECT_RELATIONSHIP_TYPE
27870
+ );
27871
+ const fallbackId = String(fallback?.["@_Id"] || "").trim();
27872
+ return fallbackId.length > 0 ? fallbackId : void 0;
27873
+ }
27874
+ /** Constants are exposed so the element-writer mixin can reuse them. */
27875
+ static OLE_OBJECT_RELATIONSHIP_TYPE = OLE_OBJECT_RELATIONSHIP_TYPE;
27876
+ static OLE_IMAGE_RELATIONSHIP_TYPE = IMAGE_RELATIONSHIP_TYPE;
26868
27877
  /**
26869
27878
  * Build a p:sp XML object for an ink annotation element.
26870
27879
  * Each ink path becomes a separate a:path within a:pathLst,
@@ -27181,6 +28190,9 @@ var PptxHandlerRuntime16 = class extends PptxHandlerRuntime15 {
27181
28190
  buildOuterShadowXml(shapeStyle) {
27182
28191
  return this.colorStyleCodec.buildOuterShadowXml(shapeStyle);
27183
28192
  }
28193
+ buildPresetShadowXml(shapeStyle) {
28194
+ return this.colorStyleCodec.buildPresetShadowXml(shapeStyle);
28195
+ }
27184
28196
  buildInnerShadowXml(shapeStyle) {
27185
28197
  return this.colorStyleCodec.buildInnerShadowXml(shapeStyle);
27186
28198
  }
@@ -27731,6 +28743,10 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27731
28743
  const solidFill = runProperties["a:solidFill"];
27732
28744
  if (solidFill) {
27733
28745
  style.color = this.parseColor(solidFill);
28746
+ const colorXml = extractColorChoiceXml(solidFill);
28747
+ if (colorXml) {
28748
+ style.colorXml = colorXml;
28749
+ }
27734
28750
  }
27735
28751
  this.applyHyperlinkStyle(style, runProperties, relationshipMap);
27736
28752
  const capAttr = String(runProperties["@_cap"] || "").trim().toLowerCase();
@@ -27778,12 +28794,86 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
27778
28794
  if (bmk) {
27779
28795
  style.bookmark = bmk;
27780
28796
  }
28797
+ const altLang = String(runProperties["@_altLang"] || "").trim();
28798
+ if (altLang) {
28799
+ style.altLanguage = altLang;
28800
+ }
28801
+ if (runProperties["@_smtId"] !== void 0) {
28802
+ const smtIdRaw = Number.parseInt(String(runProperties["@_smtId"]), 10);
28803
+ if (Number.isFinite(smtIdRaw)) {
28804
+ style.smartTagId = smtIdRaw;
28805
+ }
28806
+ }
28807
+ this.applyTextFontMetadata(style, latin, "latin");
28808
+ this.applyTextFontMetadata(style, eastAsian, "eastAsia");
28809
+ this.applyTextFontMetadata(style, complexScript, "complexScript");
28810
+ this.applyTextFontMetadata(style, runProperties["a:sym"], "symbol");
27781
28811
  const runEffectList = runProperties["a:effectLst"];
27782
28812
  if (runEffectList) {
27783
28813
  this.applyTextRunEffects(style, runEffectList);
27784
28814
  }
27785
28815
  return style;
27786
28816
  }
28817
+ /**
28818
+ * Copy `@panose` / `@pitchFamily` / `@charset` from a font child node
28819
+ * (`a:latin`, `a:ea`, `a:cs`, `a:sym`) onto the matching `*Font*`
28820
+ * fields of `style`.
28821
+ */
28822
+ applyTextFontMetadata(style, fontNode, kind) {
28823
+ if (!fontNode) {
28824
+ return;
28825
+ }
28826
+ const panose = String(fontNode["@_panose"] || "").trim();
28827
+ const pitchRaw = fontNode["@_pitchFamily"];
28828
+ const charsetRaw = fontNode["@_charset"];
28829
+ const pitch = pitchRaw !== void 0 && pitchRaw !== null ? Number.parseInt(String(pitchRaw), 10) : void 0;
28830
+ const charset = charsetRaw !== void 0 && charsetRaw !== null ? Number.parseInt(String(charsetRaw), 10) : void 0;
28831
+ if (kind === "latin") {
28832
+ if (panose) {
28833
+ style.latinFontPanose = panose;
28834
+ }
28835
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28836
+ style.latinFontPitchFamily = pitch;
28837
+ }
28838
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28839
+ style.latinFontCharset = charset;
28840
+ }
28841
+ return;
28842
+ }
28843
+ if (kind === "eastAsia") {
28844
+ if (panose) {
28845
+ style.eastAsiaFontPanose = panose;
28846
+ }
28847
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28848
+ style.eastAsiaFontPitchFamily = pitch;
28849
+ }
28850
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28851
+ style.eastAsiaFontCharset = charset;
28852
+ }
28853
+ return;
28854
+ }
28855
+ if (kind === "complexScript") {
28856
+ if (panose) {
28857
+ style.complexScriptFontPanose = panose;
28858
+ }
28859
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28860
+ style.complexScriptFontPitchFamily = pitch;
28861
+ }
28862
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28863
+ style.complexScriptFontCharset = charset;
28864
+ }
28865
+ return;
28866
+ }
28867
+ if (panose) {
28868
+ style.symbolFontPanose = panose;
28869
+ }
28870
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
28871
+ style.symbolFontPitchFamily = pitch;
28872
+ }
28873
+ if (typeof charset === "number" && Number.isFinite(charset)) {
28874
+ style.symbolFontCharset = charset;
28875
+ }
28876
+ }
27787
28877
  };
27788
28878
 
27789
28879
  // src/core/core/runtime/PptxHandlerRuntimeTextEditing.ts
@@ -28219,7 +29309,7 @@ var PptxHandlerRuntime21 = class extends PptxHandlerRuntime20 {
28219
29309
  };
28220
29310
 
28221
29311
  // src/core/core/runtime/table-cell-save-helpers.ts
28222
- function writeCellFill(tcPr, style) {
29312
+ function writeCellFill(tcPr, style, resolveColorXml) {
28223
29313
  if (style.fillMode === "gradient" && style.gradientFillStops && style.gradientFillStops.length > 0) {
28224
29314
  delete tcPr["a:solidFill"];
28225
29315
  delete tcPr["a:pattFill"];
@@ -28298,11 +29388,12 @@ function writeCellFill(tcPr, style) {
28298
29388
  } else if (style.backgroundColor) {
28299
29389
  delete tcPr["a:gradFill"];
28300
29390
  delete tcPr["a:pattFill"];
28301
- tcPr["a:solidFill"] = {
28302
- "a:srgbClr": {
28303
- "@_val": style.backgroundColor.replace("#", "")
28304
- }
28305
- };
29391
+ const resolvedOriginal = style.backgroundColorXml && resolveColorXml ? resolveColorXml(style.backgroundColorXml) : void 0;
29392
+ tcPr["a:solidFill"] = serializeColorChoice(
29393
+ style.backgroundColorXml,
29394
+ resolvedOriginal,
29395
+ style.backgroundColor
29396
+ );
28306
29397
  }
28307
29398
  }
28308
29399
  function writeDiagonalBorders(tcPr, style, emuPerPx) {
@@ -28433,7 +29524,7 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28433
29524
  xmlCell["a:tcPr"] = {};
28434
29525
  }
28435
29526
  const tcPr = xmlCell["a:tcPr"];
28436
- writeCellFill(tcPr, style);
29527
+ writeCellFill(tcPr, style, (colorXml) => this.parseColor(colorXml));
28437
29528
  if (style.vAlign) {
28438
29529
  const vAlignMap = {
28439
29530
  top: "t",
@@ -28515,35 +29606,29 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
28515
29606
  }
28516
29607
  }
28517
29608
  }
28518
- if (style.marginLeft !== void 0 || style.marginRight !== void 0 || style.marginTop !== void 0 || style.marginBottom !== void 0) {
28519
- const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
28520
- if (!tcPr["a:tcMar"]) {
28521
- tcPr["a:tcMar"] = {};
28522
- }
28523
- const tcMar = tcPr["a:tcMar"];
28524
- if (style.marginLeft !== void 0) {
28525
- tcMar["a:marL"] = {
28526
- "@_w": String(Math.round(style.marginLeft * emuPerPx))
28527
- };
28528
- }
28529
- if (style.marginRight !== void 0) {
28530
- tcMar["a:marR"] = {
28531
- "@_w": String(Math.round(style.marginRight * emuPerPx))
28532
- };
28533
- }
28534
- if (style.marginTop !== void 0) {
28535
- tcMar["a:marT"] = {
28536
- "@_w": String(Math.round(style.marginTop * emuPerPx))
28537
- };
28538
- }
28539
- if (style.marginBottom !== void 0) {
28540
- tcMar["a:marB"] = {
28541
- "@_w": String(Math.round(style.marginBottom * emuPerPx))
28542
- };
28543
- }
29609
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
29610
+ if (style.marginLeft !== void 0) {
29611
+ tcPr["@_marL"] = String(Math.round(style.marginLeft * emuPerPx));
28544
29612
  }
29613
+ if (style.marginRight !== void 0) {
29614
+ tcPr["@_marR"] = String(Math.round(style.marginRight * emuPerPx));
29615
+ }
29616
+ if (style.marginTop !== void 0) {
29617
+ tcPr["@_marT"] = String(Math.round(style.marginTop * emuPerPx));
29618
+ }
29619
+ if (style.marginBottom !== void 0) {
29620
+ tcPr["@_marB"] = String(Math.round(style.marginBottom * emuPerPx));
29621
+ }
29622
+ delete tcPr["a:tcMar"];
28545
29623
  writeDiagonalBorders(tcPr, style, _PptxHandlerRuntime.EMU_PER_PX);
28546
29624
  writeCellTextFormatting(xmlCell, style, this.ensureArray.bind(this));
29625
+ const reordered = reorderObjectKeys(tcPr, TC_PR_BORDERS_ORDER);
29626
+ for (const key of Object.keys(tcPr)) {
29627
+ delete tcPr[key];
29628
+ }
29629
+ for (const key of Object.keys(reordered)) {
29630
+ tcPr[key] = reordered[key];
29631
+ }
28547
29632
  }
28548
29633
  };
28549
29634
 
@@ -28647,6 +29732,49 @@ function createDefaultXmlCell() {
28647
29732
  // src/core/core/runtime/table-xml-rebuild.ts
28648
29733
  var GRID_COL_ID_EXT_URI = "{9D8B030D-6E8A-4147-A177-3AD203B41FA5}";
28649
29734
  var A16_NAMESPACE = "http://schemas.microsoft.com/office/drawing/2014/main";
29735
+ function ensureA16NamespaceOnSlideRoot(slideRoot) {
29736
+ if (!slideRoot["@_xmlns:a16"]) {
29737
+ slideRoot["@_xmlns:a16"] = A16_NAMESPACE;
29738
+ }
29739
+ if (!slideRoot["@_xmlns:mc"]) {
29740
+ slideRoot["@_xmlns:mc"] = "http://schemas.openxmlformats.org/markup-compatibility/2006";
29741
+ }
29742
+ const existingIgnorable = String(slideRoot["@_mc:Ignorable"] || "").trim();
29743
+ if (existingIgnorable.length === 0) {
29744
+ slideRoot["@_mc:Ignorable"] = "a16";
29745
+ return;
29746
+ }
29747
+ const tokens = existingIgnorable.split(/\s+/).filter((token) => token.length > 0);
29748
+ if (!tokens.includes("a16")) {
29749
+ tokens.push("a16");
29750
+ slideRoot["@_mc:Ignorable"] = tokens.join(" ");
29751
+ }
29752
+ }
29753
+ function slideContainsA16Element(node) {
29754
+ if (node === null || node === void 0) {
29755
+ return false;
29756
+ }
29757
+ if (Array.isArray(node)) {
29758
+ for (const entry of node) {
29759
+ if (slideContainsA16Element(entry)) {
29760
+ return true;
29761
+ }
29762
+ }
29763
+ return false;
29764
+ }
29765
+ if (typeof node !== "object") {
29766
+ return false;
29767
+ }
29768
+ for (const [key, value] of Object.entries(node)) {
29769
+ if (key.startsWith("a16:")) {
29770
+ return true;
29771
+ }
29772
+ if (slideContainsA16Element(value)) {
29773
+ return true;
29774
+ }
29775
+ }
29776
+ return false;
29777
+ }
28650
29778
  function randomColumnId() {
28651
29779
  return String(Math.floor(Math.random() * 4294967295));
28652
29780
  }
@@ -28677,8 +29805,10 @@ function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
28677
29805
  "a:extLst": {
28678
29806
  "a:ext": {
28679
29807
  "@_uri": GRID_COL_ID_EXT_URI,
29808
+ // `xmlns:a16` is declared on the slide root by
29809
+ // `ensureA16NamespaceOnSlideRoot`; emitting it here too is
29810
+ // schema-redundant and PowerPoint flags it.
28680
29811
  "a16:colId": {
28681
- "@_xmlns:a16": A16_NAMESPACE,
28682
29812
  "@_val": existingColIds[i] || randomColumnId()
28683
29813
  }
28684
29814
  }
@@ -29037,26 +30167,64 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
29037
30167
  continue;
29038
30168
  }
29039
30169
  const scalingNode = this.xmlLookupService.getChildByLocalName(axisNode, "scaling");
29040
- if (!scalingNode) {
29041
- continue;
30170
+ if (scalingNode) {
30171
+ if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
30172
+ const logBaseKey = Object.keys(scalingNode).find(
30173
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30174
+ );
30175
+ if (logBaseKey) {
30176
+ scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
30177
+ } else {
30178
+ scalingNode["c:logBase"] = {
30179
+ "@_val": String(matchingAxis.logBase)
30180
+ };
30181
+ }
30182
+ } else if (matchingAxis.logScale === false) {
30183
+ const logBaseKey = Object.keys(scalingNode).find(
30184
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30185
+ );
30186
+ if (logBaseKey) {
30187
+ delete scalingNode[logBaseKey];
30188
+ }
30189
+ }
30190
+ this.upsertChartAxisChild(
30191
+ scalingNode,
30192
+ "min",
30193
+ matchingAxis.min !== void 0 ? String(matchingAxis.min) : void 0
30194
+ );
30195
+ this.upsertChartAxisChild(
30196
+ scalingNode,
30197
+ "max",
30198
+ matchingAxis.max !== void 0 ? String(matchingAxis.max) : void 0
30199
+ );
29042
30200
  }
29043
- if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
29044
- const logBaseKey = Object.keys(scalingNode).find(
29045
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30201
+ if (matchingAxis.numFmt) {
30202
+ const numFmtKey = Object.keys(axisNode).find(
30203
+ (k) => this.compatibilityService.getXmlLocalName(k) === "numFmt"
29046
30204
  );
29047
- if (logBaseKey) {
29048
- scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
30205
+ const numFmtAttrs = {
30206
+ "@_formatCode": matchingAxis.numFmt.formatCode,
30207
+ "@_sourceLinked": matchingAxis.numFmt.sourceLinked ? "1" : "0"
30208
+ };
30209
+ if (numFmtKey) {
30210
+ axisNode[numFmtKey] = numFmtAttrs;
29049
30211
  } else {
29050
- scalingNode["c:logBase"] = {
29051
- "@_val": String(matchingAxis.logBase)
29052
- };
30212
+ axisNode["c:numFmt"] = numFmtAttrs;
29053
30213
  }
29054
- } else if (matchingAxis.logScale === false) {
29055
- const logBaseKey = Object.keys(scalingNode).find(
29056
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
30214
+ }
30215
+ this.upsertChartAxisChild(
30216
+ axisNode,
30217
+ "majorUnit",
30218
+ matchingAxis.majorUnit !== void 0 ? String(matchingAxis.majorUnit) : void 0
30219
+ );
30220
+ if (matchingAxis.tickLblPos !== void 0) {
30221
+ const tickLblKey = Object.keys(axisNode).find(
30222
+ (k) => this.compatibilityService.getXmlLocalName(k) === "tickLblPos"
29057
30223
  );
29058
- if (logBaseKey) {
29059
- delete scalingNode[logBaseKey];
30224
+ if (tickLblKey) {
30225
+ axisNode[tickLblKey]["@_val"] = matchingAxis.tickLblPos;
30226
+ } else {
30227
+ axisNode["c:tickLblPos"] = { "@_val": matchingAxis.tickLblPos };
29060
30228
  }
29061
30229
  }
29062
30230
  }
@@ -29069,6 +30237,18 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
29069
30237
  }
29070
30238
  this.pendingChartUpdates = void 0;
29071
30239
  }
30240
+ /**
30241
+ * Upsert a `c:<localName>` child with `@_val` on an axis or scaling node.
30242
+ * When `value` is undefined, removes any existing child of that local name.
30243
+ */
30244
+ upsertChartAxisChild(parent, localName, value) {
30245
+ upsertChartAxisChild(
30246
+ parent,
30247
+ localName,
30248
+ value,
30249
+ (key) => this.compatibilityService.getXmlLocalName(key)
30250
+ );
30251
+ }
29072
30252
  /**
29073
30253
  * Update the cached point values in a chart reference node
29074
30254
  * (numRef/strRef or numLit/strLit).
@@ -30348,17 +31528,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30348
31528
  delete spPr["a:noFill"];
30349
31529
  delete spPr["a:gradFill"];
30350
31530
  delete spPr["a:blipFill"];
30351
- const solidFill = {
30352
- "a:srgbClr": {
30353
- "@_val": fillColor.replace("#", "")
30354
- }
30355
- };
30356
- if (typeof shapeStyle.fillOpacity === "number" && shapeStyle.fillOpacity >= 0 && shapeStyle.fillOpacity < 1) {
30357
- solidFill["a:srgbClr"]["a:alpha"] = {
30358
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.fillOpacity) * 1e5))
30359
- };
30360
- }
30361
- spPr["a:solidFill"] = solidFill;
31531
+ const resolvedOriginal = shapeStyle.fillColorXml ? this.parseColor(shapeStyle.fillColorXml) : void 0;
31532
+ spPr["a:solidFill"] = serializeColorChoice(
31533
+ shapeStyle.fillColorXml,
31534
+ resolvedOriginal,
31535
+ fillColor,
31536
+ shapeStyle.fillOpacity
31537
+ );
30362
31538
  }
30363
31539
  }
30364
31540
  if (shapeStyle.strokeColor !== void 0) {
@@ -30373,17 +31549,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30373
31549
  delete lineNode["a:solidFill"];
30374
31550
  } else {
30375
31551
  delete lineNode["a:noFill"];
30376
- const lineFill = {
30377
- "a:srgbClr": {
30378
- "@_val": shapeStyle.strokeColor.replace("#", "")
30379
- }
30380
- };
30381
- if (typeof shapeStyle.strokeOpacity === "number" && shapeStyle.strokeOpacity >= 0 && shapeStyle.strokeOpacity < 1) {
30382
- lineFill["a:srgbClr"]["a:alpha"] = {
30383
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.strokeOpacity) * 1e5))
30384
- };
30385
- }
30386
- lineNode["a:solidFill"] = lineFill;
31552
+ const resolvedStrokeOriginal = shapeStyle.strokeColorXml ? this.parseColor(shapeStyle.strokeColorXml) : void 0;
31553
+ lineNode["a:solidFill"] = serializeColorChoice(
31554
+ shapeStyle.strokeColorXml,
31555
+ resolvedStrokeOriginal,
31556
+ shapeStyle.strokeColor,
31557
+ shapeStyle.strokeOpacity
31558
+ );
30387
31559
  }
30388
31560
  }
30389
31561
  if (shapeStyle.strokeDash !== void 0) {
@@ -30464,7 +31636,11 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30464
31636
  } else if (shapeStyle.lineJoin === "bevel") {
30465
31637
  lineNode["a:bevel"] = {};
30466
31638
  } else if (shapeStyle.lineJoin === "miter") {
30467
- lineNode["a:miter"] = { "@_lim": "800000" };
31639
+ const miterNode = {};
31640
+ if (typeof shapeStyle.miterLimit === "number" && Number.isFinite(shapeStyle.miterLimit) && shapeStyle.miterLimit !== 8e5) {
31641
+ miterNode["@_lim"] = String(Math.round(shapeStyle.miterLimit));
31642
+ }
31643
+ lineNode["a:miter"] = miterNode;
30468
31644
  }
30469
31645
  }
30470
31646
  if (shapeStyle.lineCap !== void 0) {
@@ -30484,6 +31660,82 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30484
31660
  spPr["a:ln"]["a:effectLst"] = lineEffectListXml;
30485
31661
  }
30486
31662
  }
31663
+ /**
31664
+ * Serialize the shape's `<p:style>` block (CT_ShapeStyle §20.1.2.2.36)
31665
+ * from the persisted ref indices/colour XML. Emits children in spec
31666
+ * order: `lnRef → fillRef → effectRef → fontRef`.
31667
+ *
31668
+ * When the original shape XML already contained a `<p:style>` we mutate
31669
+ * that node in place so any unmodelled attributes/children are preserved.
31670
+ * When it didn't, we create one. When the shape no longer has any ref
31671
+ * data we leave the existing `<p:style>` (if any) untouched — silently
31672
+ * dropping it would break round-tripping.
31673
+ *
31674
+ * Phase 2 Stream B / C-H2.
31675
+ */
31676
+ applyShapeStyleRefs(shape, shapeStyle) {
31677
+ const hasAnyRef = shapeStyle.lnRefIdx !== void 0 || shapeStyle.fillRefIdx !== void 0 || shapeStyle.effectRefIdx !== void 0 || shapeStyle.fontRefIdx !== void 0;
31678
+ if (!hasAnyRef) {
31679
+ return;
31680
+ }
31681
+ const existing = shape["p:style"];
31682
+ const styleNode = existing ?? {};
31683
+ if (shapeStyle.lnRefIdx !== void 0) {
31684
+ const lnRef = styleNode["a:lnRef"] ?? {};
31685
+ lnRef["@_idx"] = String(shapeStyle.lnRefIdx);
31686
+ this.replaceRefColorChoice(lnRef, shapeStyle.lnRefColorXml);
31687
+ styleNode["a:lnRef"] = lnRef;
31688
+ }
31689
+ if (shapeStyle.fillRefIdx !== void 0) {
31690
+ const fillRef = styleNode["a:fillRef"] ?? {};
31691
+ fillRef["@_idx"] = String(shapeStyle.fillRefIdx);
31692
+ this.replaceRefColorChoice(fillRef, shapeStyle.fillRefColorXml);
31693
+ styleNode["a:fillRef"] = fillRef;
31694
+ }
31695
+ if (shapeStyle.effectRefIdx !== void 0) {
31696
+ const effectRef = styleNode["a:effectRef"] ?? {};
31697
+ effectRef["@_idx"] = String(shapeStyle.effectRefIdx);
31698
+ this.replaceRefColorChoice(effectRef, shapeStyle.effectRefColorXml);
31699
+ styleNode["a:effectRef"] = effectRef;
31700
+ }
31701
+ if (shapeStyle.fontRefIdx !== void 0) {
31702
+ const fontRef = styleNode["a:fontRef"] ?? {};
31703
+ fontRef["@_idx"] = shapeStyle.fontRefIdx;
31704
+ this.replaceRefColorChoice(fontRef, shapeStyle.fontRefColorXml);
31705
+ styleNode["a:fontRef"] = fontRef;
31706
+ }
31707
+ const reordered = reorderObjectKeys(styleNode, SHAPE_STYLE_ORDER);
31708
+ for (const key of Object.keys(styleNode)) {
31709
+ delete styleNode[key];
31710
+ }
31711
+ for (const key of Object.keys(reordered)) {
31712
+ styleNode[key] = reordered[key];
31713
+ }
31714
+ shape["p:style"] = styleNode;
31715
+ }
31716
+ /**
31717
+ * Replace any existing colour-choice child on a style-matrix-reference
31718
+ * element with the given preserved XML, or strip all colour children
31719
+ * when the override is undefined.
31720
+ */
31721
+ replaceRefColorChoice(refNode, colorXml) {
31722
+ for (const key of [
31723
+ "a:scrgbClr",
31724
+ "a:srgbClr",
31725
+ "a:hslClr",
31726
+ "a:sysClr",
31727
+ "a:schemeClr",
31728
+ "a:prstClr"
31729
+ ]) {
31730
+ delete refNode[key];
31731
+ }
31732
+ if (!colorXml) {
31733
+ return;
31734
+ }
31735
+ for (const [key, value] of Object.entries(colorXml)) {
31736
+ refNode[key] = value;
31737
+ }
31738
+ }
30487
31739
  };
30488
31740
 
30489
31741
  // src/core/core/runtime/PptxHandlerRuntimeSaveEffectsWriter.ts
@@ -30493,17 +31745,22 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30493
31745
  * effectDag, 3D scene, and 3D shape properties to the given spPr XML object.
30494
31746
  */
30495
31747
  applyEffectsAndThreeD(spPr, shapeStyle) {
30496
- const outerShadowXml = this.buildOuterShadowXml(shapeStyle);
31748
+ const presetShadowXml = shapeStyle.presetShadowName ? this.buildPresetShadowXml(shapeStyle) : void 0;
31749
+ const outerShadowXml = presetShadowXml ? void 0 : this.buildOuterShadowXml(shapeStyle);
30497
31750
  const innerShadowXml = this.buildInnerShadowXml(shapeStyle);
30498
31751
  const glowXml = this.buildGlowXml(shapeStyle);
30499
31752
  const softEdgeXml = this.buildSoftEdgeXml(shapeStyle);
30500
31753
  const reflectionXml = this.buildReflectionXml(shapeStyle);
30501
31754
  const blurXml = this.buildBlurXml(shapeStyle);
30502
- const hasAnyEffect = outerShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
31755
+ const hasAnyEffect = outerShadowXml || presetShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
30503
31756
  if (hasAnyEffect) {
30504
31757
  const effectList = spPr["a:effectLst"] || {};
30505
- if (outerShadowXml) {
31758
+ if (presetShadowXml) {
31759
+ effectList["a:prstShdw"] = presetShadowXml;
31760
+ delete effectList["a:outerShdw"];
31761
+ } else if (outerShadowXml) {
30506
31762
  effectList["a:outerShdw"] = outerShadowXml;
31763
+ delete effectList["a:prstShdw"];
30507
31764
  }
30508
31765
  if (innerShadowXml) {
30509
31766
  effectList["a:innerShdw"] = innerShadowXml;
@@ -30520,12 +31777,13 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30520
31777
  if (blurXml) {
30521
31778
  effectList["a:blur"] = blurXml;
30522
31779
  }
30523
- spPr["a:effectLst"] = effectList;
31780
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30524
31781
  } else {
30525
31782
  const effectList = spPr["a:effectLst"];
30526
31783
  if (effectList) {
30527
- if (shapeStyle.shadowColor !== void 0 && !outerShadowXml) {
31784
+ if (shapeStyle.shadowColor !== void 0 && !outerShadowXml && !presetShadowXml) {
30528
31785
  delete effectList["a:outerShdw"];
31786
+ delete effectList["a:prstShdw"];
30529
31787
  }
30530
31788
  if (shapeStyle.innerShadowColor !== void 0 && !innerShadowXml) {
30531
31789
  delete effectList["a:innerShdw"];
@@ -30544,6 +31802,8 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
30544
31802
  }
30545
31803
  if (Object.keys(effectList).length === 0) {
30546
31804
  delete spPr["a:effectLst"];
31805
+ } else {
31806
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
30547
31807
  }
30548
31808
  }
30549
31809
  }
@@ -30704,7 +31964,8 @@ var PptxHandlerRuntime30 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30704
31964
  );
30705
31965
  }
30706
31966
  if (el.textStyle?.hOverflow) {
30707
- bodyPr["@_hOverflow"] = el.textStyle.hOverflow;
31967
+ bodyPr["@_horzOverflow"] = el.textStyle.hOverflow;
31968
+ delete bodyPr["@_hOverflow"];
30708
31969
  }
30709
31970
  if (el.textStyle?.vertOverflow) {
30710
31971
  bodyPr["@_vertOverflow"] = el.textStyle.vertOverflow;
@@ -30935,7 +32196,10 @@ var PptxHandlerRuntime31 = class extends PptxHandlerRuntime30 {
30935
32196
  const elWithPaths = el;
30936
32197
  if (elWithPaths.customGeometryPaths && elWithPaths.customGeometryPaths.length > 0) {
30937
32198
  delete spPr["a:prstGeom"];
30938
- spPr["a:custGeom"] = customGeometryPathsToXml(elWithPaths.customGeometryPaths);
32199
+ spPr["a:custGeom"] = customGeometryPathsToXml(
32200
+ elWithPaths.customGeometryPaths,
32201
+ elWithPaths.customGeometryRawData
32202
+ );
30939
32203
  } else if (spPr["a:prstGeom"]) {
30940
32204
  const presetGeometry = el.type === "connector" ? this.normalizePresetGeometry(el.shapeType || "straightConnector1") : this.normalizePresetGeometry(el.shapeType);
30941
32205
  const prstGeom = spPr["a:prstGeom"];
@@ -31060,6 +32324,42 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31060
32324
  isGraphicFrameShape(shape) {
31061
32325
  return Boolean(shape["p:nvGraphicFramePr"] || shape["a:graphic"] && shape["p:xfrm"]);
31062
32326
  }
32327
+ /**
32328
+ * Reorder children of `p:spPr` to match CT_ShapeProperties (§20.1.2.2.35).
32329
+ * Also reorders any nested `a:blipFill` per CT_BlipFillProperties.
32330
+ * fast-xml-parser preserves insertion order; PowerPoint validates against
32331
+ * the schema's required order, so save-side mutations must be re-sorted.
32332
+ */
32333
+ finalizeSpPrSchemaOrder(shape) {
32334
+ const spPr = shape["p:spPr"];
32335
+ if (!spPr) {
32336
+ return;
32337
+ }
32338
+ const blipFill = spPr["a:blipFill"];
32339
+ if (blipFill) {
32340
+ this.reorderInPlace(blipFill, BLIP_FILL_ORDER);
32341
+ }
32342
+ this.reorderInPlace(spPr, SP_PR_ORDER);
32343
+ }
32344
+ /**
32345
+ * Reorder children of the picture-level `p:blipFill` (CT_BlipFillProperties).
32346
+ * Picture elements carry their blip data on the `p:pic` root, not under spPr.
32347
+ */
32348
+ finalizePictureBlipFillOrder(shape) {
32349
+ const pBlipFill = shape["p:blipFill"];
32350
+ if (pBlipFill) {
32351
+ this.reorderInPlace(pBlipFill, BLIP_FILL_ORDER);
32352
+ }
32353
+ }
32354
+ reorderInPlace(target, schemaOrder) {
32355
+ const reordered = reorderObjectKeys(target, schemaOrder);
32356
+ for (const key of Object.keys(target)) {
32357
+ delete target[key];
32358
+ }
32359
+ for (const key of Object.keys(reordered)) {
32360
+ target[key] = reordered[key];
32361
+ }
32362
+ }
31063
32363
  /** Whether an element ID indicates a template (layout/master) element. */
31064
32364
  isTemplateElementId(elementId) {
31065
32365
  return elementId.startsWith("layout-") || elementId.startsWith("master-");
@@ -31083,18 +32383,51 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31083
32383
  }
31084
32384
  return;
31085
32385
  }
32386
+ if (el.type === "contentPart") {
32387
+ if (shape) {
32388
+ this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
32389
+ collectors.contentParts.push(shape);
32390
+ } else {
32391
+ this.compatibilityService.reportWarning({
32392
+ code: "SAVE_ELEMENT_SKIPPED",
32393
+ message: `Content part '${el.id}' has no rawXml and was skipped during save.`,
32394
+ scope: "save",
32395
+ slideId: ctx.slide.id,
32396
+ elementId: el.id
32397
+ });
32398
+ }
32399
+ return;
32400
+ }
31086
32401
  if (!shape && (el.type === "text" || el.type === "shape")) {
31087
32402
  shape = this.createElementXml(el);
31088
32403
  }
31089
32404
  if (!shape && el.type === "connector") {
31090
32405
  shape = this.createConnectorXml(el);
31091
32406
  }
31092
- if (!shape && el.type === "ink") {
31093
- shape = this.createInkShapeXml(el);
32407
+ if (el.type === "ink") {
32408
+ if (!shape) {
32409
+ shape = this.createInkShapeXml(el);
32410
+ this.compatibilityService.reportWarning({
32411
+ code: "SAVE_INK_ENCODED_AS_CUSTGEOM",
32412
+ message: "SDK-created ink element serialized as custGeom shape; pressure/tool metadata not represented in OOXML aink format.",
32413
+ scope: "save",
32414
+ slideId: ctx.slide.id,
32415
+ elementId: el.id
32416
+ });
32417
+ }
31094
32418
  }
31095
32419
  if (!shape && el.type === "table") {
31096
32420
  shape = this.createTableGraphicFrameXml(el);
31097
32421
  }
32422
+ if (el.type === "ole") {
32423
+ const oleEl = el;
32424
+ if (shape) {
32425
+ this.applyOleTypedFieldUpdates(shape, oleEl);
32426
+ } else {
32427
+ const embedRid = this.resolveOleEmbedRelationshipId(ctx.slideRelationships, oleEl.oleTarget) || ctx.slideRelationshipRegistry.nextRelationshipId();
32428
+ shape = this.createOleGraphicFrameXml(oleEl, embedRid);
32429
+ }
32430
+ }
31098
32431
  if (!shape) {
31099
32432
  this.compatibilityService.reportWarning({
31100
32433
  code: "SAVE_ELEMENT_SKIPPED",
@@ -31107,11 +32440,14 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31107
32440
  }
31108
32441
  this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
31109
32442
  this.applyImageProperties(shape, el);
32443
+ this.finalizePictureBlipFillOrder(shape);
31110
32444
  this.applyGeometryUpdate(shape, el);
31111
32445
  if (hasShapeProperties(el) && el.shapeStyle && shape["p:spPr"]) {
31112
32446
  const spPr = shape["p:spPr"];
31113
32447
  this.applyFillAndStroke(spPr, el.shapeStyle);
31114
32448
  this.applyEffectsAndThreeD(spPr, el.shapeStyle);
32449
+ this.finalizeSpPrSchemaOrder(shape);
32450
+ this.applyShapeStyleRefs(shape, el.shapeStyle);
31115
32451
  }
31116
32452
  if (hasTextProperties(el)) {
31117
32453
  this.applyTextBodyContent(
@@ -31269,7 +32605,8 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31269
32605
  connectors: [],
31270
32606
  graphicFrames: [],
31271
32607
  groups: [],
31272
- model3ds: []
32608
+ model3ds: [],
32609
+ contentParts: []
31273
32610
  };
31274
32611
  const ctx = {
31275
32612
  slide,
@@ -31301,6 +32638,12 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31301
32638
  } else {
31302
32639
  delete spTree["p16:model3D"];
31303
32640
  }
32641
+ if (collectors.contentParts.length > 0) {
32642
+ spTree["p:contentPart"] = collectors.contentParts;
32643
+ } else {
32644
+ delete spTree["p:contentPart"];
32645
+ }
32646
+ this.reapplyAlternateContentEnvelopes(spTree, collectors);
31304
32647
  const reassigned = shapeIdValidator.validateAndDeduplicateIds(
31305
32648
  spTree,
31306
32649
  (v) => this.ensureArray(v)
@@ -31318,12 +32661,426 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
31318
32661
  this.zip.file(slideRelsPath, this.builder.build(slideRelsData));
31319
32662
  this.applySlideDrawingGuides(slideNode, slide);
31320
32663
  this.deduplicateExtensionLists(xmlObj);
32664
+ if (slideContainsA16Element(slideNode)) {
32665
+ ensureA16NamespaceOnSlideRoot(slideNode);
32666
+ }
31321
32667
  this.zip.file(slide.id, this.builder.build(xmlObj));
31322
32668
  }
32669
+ /**
32670
+ * Re-wrap selected children with their original `<mc:AlternateContent>`
32671
+ * envelope (CC-4).
32672
+ *
32673
+ * Parsing merged the selected branch (Choice when supported, otherwise
32674
+ * Fallback) into the spTree's tag arrays. Without re-wrapping, dirty
32675
+ * save would emit flat `<p:sp>`/`<p:pic>` etc. and drop the
32676
+ * `<mc:Fallback>` branch — losing legacy rendering for files originally
32677
+ * authored with newer-namespace features.
32678
+ *
32679
+ * Strategy: for each XmlObject in `collectors.*` that traces back to a
32680
+ * known AC block, group by block and:
32681
+ * 1. Remove the node from its flat collector / spTree array.
32682
+ * 2. Clone the original AC envelope.
32683
+ * 3. Replace the selected branch's `<{tag}>` children with the
32684
+ * live (possibly edited) nodes from the collectors.
32685
+ * 4. Leave the unselected branch verbatim.
32686
+ *
32687
+ * Final envelopes are appended to `spTree['mc:AlternateContent']`.
32688
+ */
32689
+ reapplyAlternateContentEnvelopes(spTree, collectors) {
32690
+ const TAG_TO_COLLECTOR = {
32691
+ "p:sp": collectors.shapes,
32692
+ "p:pic": collectors.pics,
32693
+ "p:cxnSp": collectors.connectors,
32694
+ "p:graphicFrame": collectors.graphicFrames,
32695
+ "p:grpSp": collectors.groups,
32696
+ "p:contentPart": collectors.contentParts,
32697
+ // `model3d` does not flow through SHAPE_TREE_ELEMENT_TAGS, but the
32698
+ // AC pathway in OpenXML decks frequently uses Choice = p16:model3D
32699
+ // + Fallback = p:pic, so map it for completeness.
32700
+ "p16:model3D": collectors.model3ds
32701
+ };
32702
+ const blockGroups = /* @__PURE__ */ new Map();
32703
+ for (const tag of Object.keys(TAG_TO_COLLECTOR)) {
32704
+ const collector = TAG_TO_COLLECTOR[tag];
32705
+ if (!collector) {
32706
+ continue;
32707
+ }
32708
+ for (const node of collector) {
32709
+ const block = this.alternateContentBlockByRawXml.get(node);
32710
+ if (!block) {
32711
+ continue;
32712
+ }
32713
+ let entries = blockGroups.get(block);
32714
+ if (!entries) {
32715
+ entries = [];
32716
+ blockGroups.set(block, entries);
32717
+ }
32718
+ entries.push({ tag, node, collector });
32719
+ }
32720
+ }
32721
+ if (blockGroups.size === 0) {
32722
+ return;
32723
+ }
32724
+ const envelopes = [];
32725
+ for (const [block, entries] of blockGroups) {
32726
+ for (const entry of entries) {
32727
+ const idx = entry.collector.indexOf(entry.node);
32728
+ if (idx !== -1) {
32729
+ entry.collector.splice(idx, 1);
32730
+ }
32731
+ }
32732
+ const clonedAc = { ...block.rawAc };
32733
+ const liveByTag = /* @__PURE__ */ new Map();
32734
+ for (const entry of entries) {
32735
+ let arr = liveByTag.get(entry.tag);
32736
+ if (!arr) {
32737
+ arr = [];
32738
+ liveByTag.set(entry.tag, arr);
32739
+ }
32740
+ arr.push(entry.node);
32741
+ }
32742
+ if (block.selectedBranch === "choice") {
32743
+ const choices = this.ensureArray(clonedAc["mc:Choice"]);
32744
+ const targetIdx = block.choiceIndex ?? 0;
32745
+ const original = choices[targetIdx];
32746
+ if (original) {
32747
+ const rebuilt = { ...original };
32748
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
32749
+ delete rebuilt[tag];
32750
+ }
32751
+ for (const [tag, nodes] of liveByTag) {
32752
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
32753
+ }
32754
+ choices[targetIdx] = rebuilt;
32755
+ clonedAc["mc:Choice"] = choices.length === 1 ? choices[0] : choices;
32756
+ }
32757
+ } else {
32758
+ const fallback = clonedAc["mc:Fallback"];
32759
+ if (fallback) {
32760
+ const rebuilt = { ...fallback };
32761
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
32762
+ delete rebuilt[tag];
32763
+ }
32764
+ for (const [tag, nodes] of liveByTag) {
32765
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
32766
+ }
32767
+ clonedAc["mc:Fallback"] = rebuilt;
32768
+ }
32769
+ }
32770
+ envelopes.push(clonedAc);
32771
+ }
32772
+ spTree["p:sp"] = collectors.shapes;
32773
+ spTree["p:pic"] = collectors.pics;
32774
+ spTree["p:cxnSp"] = collectors.connectors;
32775
+ spTree["p:graphicFrame"] = collectors.graphicFrames;
32776
+ if (collectors.groups.length > 0) {
32777
+ spTree["p:grpSp"] = collectors.groups;
32778
+ } else {
32779
+ delete spTree["p:grpSp"];
32780
+ }
32781
+ if (collectors.contentParts.length > 0) {
32782
+ spTree["p:contentPart"] = collectors.contentParts;
32783
+ } else {
32784
+ delete spTree["p:contentPart"];
32785
+ }
32786
+ if (collectors.model3ds.length > 0) {
32787
+ spTree["p16:model3D"] = collectors.model3ds;
32788
+ } else {
32789
+ delete spTree["p16:model3D"];
32790
+ }
32791
+ spTree["mc:AlternateContent"] = envelopes.length === 1 ? envelopes[0] : envelopes;
32792
+ }
32793
+ };
32794
+
32795
+ // src/core/core/runtime/PptxHandlerRuntimeSaveTheme.ts
32796
+ var PptxHandlerRuntime34 = class extends PptxHandlerRuntime33 {
32797
+ /**
32798
+ * Mark a theme path as dirty so the save pipeline will regenerate
32799
+ * the theme XML from in-memory state. Optional — without this the
32800
+ * original XML is preserved verbatim on save (C-H3).
32801
+ */
32802
+ markThemeDirty(themePath) {
32803
+ this.dirtyThemePaths.add(themePath);
32804
+ }
32805
+ /**
32806
+ * Mark all known theme paths dirty in one call.
32807
+ */
32808
+ markAllThemesDirty() {
32809
+ for (const themePath of this.originalThemeXmlByPath.keys()) {
32810
+ this.dirtyThemePaths.add(themePath);
32811
+ }
32812
+ for (const themePath of this.masterThemePaths.values()) {
32813
+ this.dirtyThemePaths.add(themePath);
32814
+ }
32815
+ }
32816
+ /**
32817
+ * Persist all theme parts during save. Called from the save pipeline
32818
+ * after master / layout XML have been flushed and before
32819
+ * presentation.xml is serialized.
32820
+ *
32821
+ * Order of operations per theme path:
32822
+ *
32823
+ * 1. If the path is *not* in {@link dirtyThemePaths}, the existing
32824
+ * ZIP entry is already correct — no-op. (Original XML was placed
32825
+ * into the ZIP at load time.)
32826
+ * 2. If the path is dirty, build a fresh `<a:theme>` document from
32827
+ * in-memory state and the captured raw subtrees, then overwrite
32828
+ * the ZIP entry.
32829
+ */
32830
+ async persistThemeParts() {
32831
+ const seenThemePaths = /* @__PURE__ */ new Set();
32832
+ for (const [masterPath, themePath] of this.masterThemePaths.entries()) {
32833
+ if (!themePath) {
32834
+ continue;
32835
+ }
32836
+ seenThemePaths.add(themePath);
32837
+ if (!this.dirtyThemePaths.has(themePath)) {
32838
+ continue;
32839
+ }
32840
+ const themeXml2 = this.buildThemeXml(themePath, masterPath);
32841
+ if (themeXml2) {
32842
+ this.zip.file(themePath, themeXml2);
32843
+ }
32844
+ }
32845
+ for (const [themePath] of this.originalThemeXmlByPath.entries()) {
32846
+ if (seenThemePaths.has(themePath)) {
32847
+ continue;
32848
+ }
32849
+ if (!this.dirtyThemePaths.has(themePath)) {
32850
+ continue;
32851
+ }
32852
+ const themeXml2 = this.buildThemeXml(themePath, void 0);
32853
+ if (themeXml2) {
32854
+ this.zip.file(themePath, themeXml2);
32855
+ }
32856
+ }
32857
+ }
32858
+ /**
32859
+ * Build a complete `<a:theme>` XML document from in-memory state.
32860
+ * Returns the serialized XML string (with XML prolog), or `undefined`
32861
+ * if there is no source data to emit.
32862
+ *
32863
+ * - Color scheme: built from per-master color map (or global fallback).
32864
+ * - Font scheme: built from per-master font map + per-script entries.
32865
+ * - Format scheme: re-emit the original XML subtree if available; else
32866
+ * build a minimal scheme from {@link themeFormatScheme}.
32867
+ * - objectDefaults / extraClrSchemeLst / custClrLst / extLst: re-emit
32868
+ * captured raw subtrees.
32869
+ */
32870
+ buildThemeXml(themePath, masterPath) {
32871
+ const colorMap = masterPath && this.masterThemeColorMaps.get(masterPath) || this.globalThemeColorMapSnapshot || this.themeColorMap;
32872
+ const fontMap = masterPath && this.masterThemeFontMaps.get(masterPath) || this.globalThemeFontMapSnapshot || this.themeFontMap;
32873
+ const themeName = this.masterThemeNames.get(themePath) || "Office Theme";
32874
+ const colorSchemeName = this.masterThemeColorSchemeNames.get(themePath) || themeName;
32875
+ const fontSchemeName = this.masterThemeFontSchemeNames.get(themePath) || themeName;
32876
+ const majorScripts = this.masterThemeMajorFontScripts.get(themePath) || {};
32877
+ const minorScripts = this.masterThemeMinorFontScripts.get(themePath) || {};
32878
+ const clrScheme = this.buildClrSchemeObject(colorSchemeName, colorMap);
32879
+ const fontScheme = this.buildFontSchemeObject(
32880
+ fontSchemeName,
32881
+ fontMap,
32882
+ majorScripts,
32883
+ minorScripts
32884
+ );
32885
+ const fmtScheme = this.extractRawSubtreeFromOriginal(themePath, [
32886
+ "a:theme",
32887
+ "a:themeElements",
32888
+ "a:fmtScheme"
32889
+ ]);
32890
+ const themeElements = {
32891
+ "a:clrScheme": clrScheme,
32892
+ "a:fontScheme": fontScheme
32893
+ };
32894
+ if (fmtScheme !== void 0) {
32895
+ themeElements["a:fmtScheme"] = fmtScheme;
32896
+ } else {
32897
+ themeElements["a:fmtScheme"] = this.buildMinimalFmtScheme(themeName);
32898
+ }
32899
+ const themeRoot = {
32900
+ "@_xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
32901
+ "@_name": themeName,
32902
+ "a:themeElements": themeElements
32903
+ };
32904
+ const objectDefaults = this.masterThemeObjectDefaults.get(themePath);
32905
+ if (objectDefaults && (objectDefaults.spDef || objectDefaults.lnDef || objectDefaults.txDef)) {
32906
+ const od = {};
32907
+ if (objectDefaults.spDef !== void 0) {
32908
+ od["a:spDef"] = objectDefaults.spDef;
32909
+ }
32910
+ if (objectDefaults.lnDef !== void 0) {
32911
+ od["a:lnDef"] = objectDefaults.lnDef;
32912
+ }
32913
+ if (objectDefaults.txDef !== void 0) {
32914
+ od["a:txDef"] = objectDefaults.txDef;
32915
+ }
32916
+ themeRoot["a:objectDefaults"] = od;
32917
+ } else {
32918
+ themeRoot["a:objectDefaults"] = {};
32919
+ }
32920
+ const extraClr = this.masterThemeExtraClrSchemeLst.get(themePath);
32921
+ themeRoot["a:extraClrSchemeLst"] = extraClr !== void 0 ? extraClr : {};
32922
+ const custClr = this.masterThemeCustClrLst.get(themePath);
32923
+ if (custClr !== void 0) {
32924
+ themeRoot["a:custClrLst"] = custClr;
32925
+ }
32926
+ const themeExt = this.masterThemeExtLst.get(themePath);
32927
+ if (themeExt !== void 0) {
32928
+ themeRoot["a:extLst"] = themeExt;
32929
+ }
32930
+ const doc = {
32931
+ "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
32932
+ "a:theme": themeRoot
32933
+ };
32934
+ try {
32935
+ return this.builder.build(doc);
32936
+ } catch (error) {
32937
+ console.warn(`Failed to build theme XML for ${themePath}:`, error);
32938
+ return void 0;
32939
+ }
32940
+ }
32941
+ /**
32942
+ * Build the `a:clrScheme` XmlObject from a colour map. Each slot
32943
+ * value is interpreted as either a `#RRGGBB` srgb hex or a known
32944
+ * sysClr token (currently always emitted as srgbClr — the in-memory
32945
+ * map is hex-typed; sysClr round-trip belongs to the broader C-H3
32946
+ * fix to preserve original color XML and is out of scope here).
32947
+ */
32948
+ buildClrSchemeObject(schemeName, colorMap) {
32949
+ const slot = (key) => {
32950
+ const hex = String(colorMap[key] || "").replace(/^#/, "");
32951
+ const srgb = hex.length === 6 ? hex.toUpperCase() : "000000";
32952
+ return { "a:srgbClr": { "@_val": srgb } };
32953
+ };
32954
+ return {
32955
+ "@_name": schemeName,
32956
+ "a:dk1": slot("dk1"),
32957
+ "a:lt1": slot("lt1"),
32958
+ "a:dk2": slot("dk2"),
32959
+ "a:lt2": slot("lt2"),
32960
+ "a:accent1": slot("accent1"),
32961
+ "a:accent2": slot("accent2"),
32962
+ "a:accent3": slot("accent3"),
32963
+ "a:accent4": slot("accent4"),
32964
+ "a:accent5": slot("accent5"),
32965
+ "a:accent6": slot("accent6"),
32966
+ "a:hlink": slot("hlink"),
32967
+ "a:folHlink": slot("folHlink")
32968
+ };
32969
+ }
32970
+ /**
32971
+ * Build the `a:fontScheme` XmlObject from a font map plus per-script
32972
+ * font tables.
32973
+ *
32974
+ * Phase 4 Stream A / M4.
32975
+ */
32976
+ buildFontSchemeObject(schemeName, fontMap, majorScripts, minorScripts) {
32977
+ const buildFontGroup = (latinKey, eaKey, csKey, scripts) => {
32978
+ const group = {
32979
+ "a:latin": { "@_typeface": fontMap[latinKey] || "Calibri" },
32980
+ "a:ea": { "@_typeface": fontMap[eaKey] || "" },
32981
+ "a:cs": { "@_typeface": fontMap[csKey] || "" }
32982
+ };
32983
+ const scriptKeys = Object.keys(scripts);
32984
+ if (scriptKeys.length > 0) {
32985
+ const fontEntries = scriptKeys.map((script) => ({
32986
+ "@_script": script,
32987
+ "@_typeface": scripts[script]
32988
+ }));
32989
+ group["a:font"] = fontEntries.length === 1 ? fontEntries[0] : fontEntries;
32990
+ }
32991
+ return group;
32992
+ };
32993
+ return {
32994
+ "@_name": schemeName,
32995
+ "a:majorFont": buildFontGroup("mj-lt", "mj-ea", "mj-cs", majorScripts),
32996
+ "a:minorFont": buildFontGroup("mn-lt", "mn-ea", "mn-cs", minorScripts)
32997
+ };
32998
+ }
32999
+ /**
33000
+ * Re-parse the original theme XML and pluck out a subtree by path,
33001
+ * returning the raw parser object. Returns `undefined` when the
33002
+ * original is missing or the path doesn't exist.
33003
+ *
33004
+ * Used to preserve `a:fmtScheme` byte-for-byte through a regenerate
33005
+ * round-trip, since the in-memory PptxThemeFormatScheme is lossy.
33006
+ */
33007
+ extractRawSubtreeFromOriginal(themePath, path2) {
33008
+ const original = this.originalThemeXmlByPath.get(themePath);
33009
+ if (!original) {
33010
+ return void 0;
33011
+ }
33012
+ try {
33013
+ const parsed = this.parser.parse(original);
33014
+ let cursor = parsed;
33015
+ for (const segment of path2) {
33016
+ if (cursor && typeof cursor === "object" && segment in cursor) {
33017
+ cursor = cursor[segment];
33018
+ } else {
33019
+ return void 0;
33020
+ }
33021
+ }
33022
+ return cursor;
33023
+ } catch {
33024
+ return void 0;
33025
+ }
33026
+ }
33027
+ /**
33028
+ * Last-resort minimal `<a:fmtScheme>` body. Mirrors the SDK new-deck
33029
+ * builder's output for new presentations, scaled down to the smallest
33030
+ * schema-valid form.
33031
+ */
33032
+ buildMinimalFmtScheme(name) {
33033
+ const phClrSolid = { "a:solidFill": { "a:schemeClr": { "@_val": "phClr" } } };
33034
+ return {
33035
+ "@_name": name,
33036
+ "a:fillStyleLst": {
33037
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
33038
+ "a:gradFill": []
33039
+ },
33040
+ "a:lnStyleLst": {
33041
+ "a:ln": [
33042
+ {
33043
+ "@_w": "6350",
33044
+ "@_cap": "flat",
33045
+ "@_cmpd": "sng",
33046
+ "@_algn": "ctr",
33047
+ ...phClrSolid,
33048
+ "a:prstDash": { "@_val": "solid" },
33049
+ "a:miter": { "@_lim": "800000" }
33050
+ },
33051
+ {
33052
+ "@_w": "12700",
33053
+ "@_cap": "flat",
33054
+ "@_cmpd": "sng",
33055
+ "@_algn": "ctr",
33056
+ ...phClrSolid,
33057
+ "a:prstDash": { "@_val": "solid" },
33058
+ "a:miter": { "@_lim": "800000" }
33059
+ },
33060
+ {
33061
+ "@_w": "19050",
33062
+ "@_cap": "flat",
33063
+ "@_cmpd": "sng",
33064
+ "@_algn": "ctr",
33065
+ ...phClrSolid,
33066
+ "a:prstDash": { "@_val": "solid" },
33067
+ "a:miter": { "@_lim": "800000" }
33068
+ }
33069
+ ]
33070
+ },
33071
+ "a:effectStyleLst": {
33072
+ "a:effectStyle": [{ "a:effectLst": {} }, { "a:effectLst": {} }, { "a:effectLst": {} }]
33073
+ },
33074
+ "a:bgFillStyleLst": {
33075
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
33076
+ "a:gradFill": []
33077
+ }
33078
+ };
33079
+ }
31323
33080
  };
31324
33081
 
31325
33082
  // src/core/core/runtime/PptxHandlerRuntimeSavePipeline.ts
31326
- var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime33 {
33083
+ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
31327
33084
  /**
31328
33085
  * Resolve the effective conformance class for this save operation.
31329
33086
  *
@@ -31410,6 +33167,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31410
33167
  for (const [masterPath, masterXmlObj] of this.masterXmlMap.entries()) {
31411
33168
  this.zip.file(masterPath, this.builder.build(masterXmlObj));
31412
33169
  }
33170
+ await this.persistThemeParts();
31413
33171
  await this.applyEmbeddedFontPreservation(options?.embeddedFonts);
31414
33172
  if (this.presentationData) {
31415
33173
  this.presentationSaveBuilder.applySaveOptions({
@@ -31499,7 +33257,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31499
33257
  };
31500
33258
 
31501
33259
  // src/core/core/runtime/PptxHandlerRuntimeElementParsing.ts
31502
- var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
33260
+ var PptxHandlerRuntime36 = class _PptxHandlerRuntime extends PptxHandlerRuntime35 {
31503
33261
  /**
31504
33262
  * Parse media data (video/audio path and MIME type) from graphic frame data.
31505
33263
  */
@@ -31721,7 +33479,7 @@ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
31721
33479
  };
31722
33480
 
31723
33481
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderLookup.ts
31724
- var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
33482
+ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31725
33483
  findPlaceholderInShapeTree(spTree, expected) {
31726
33484
  if (!spTree) {
31727
33485
  return void 0;
@@ -31874,7 +33632,7 @@ var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
31874
33632
  };
31875
33633
 
31876
33634
  // src/core/core/runtime/PptxHandlerRuntimeGeometryParsing.ts
31877
- var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
33635
+ var PptxHandlerRuntime38 = class extends PptxHandlerRuntime37 {
31878
33636
  parseGeometryAdjustments(prstGeom) {
31879
33637
  if (!prstGeom) {
31880
33638
  return void 0;
@@ -31983,6 +33741,103 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
31983
33741
  }
31984
33742
  return handles.length > 0 ? handles : void 0;
31985
33743
  }
33744
+ /**
33745
+ * Extract `a:gdLst`/`a:ahLst`/`a:cxnLst`/`a:rect` raw XML from a `a:custGeom`
33746
+ * node so they can be re-emitted on save when the geometry is edited.
33747
+ * Returns `undefined` when none of these auxiliary children carry data.
33748
+ */
33749
+ extractCustomGeometryRawData(custGeom) {
33750
+ if (!custGeom) {
33751
+ return void 0;
33752
+ }
33753
+ const isNonEmpty = (node) => {
33754
+ if (node === void 0 || node === null) {
33755
+ return false;
33756
+ }
33757
+ if (typeof node !== "object") {
33758
+ return true;
33759
+ }
33760
+ return Object.keys(node).length > 0;
33761
+ };
33762
+ const result = {};
33763
+ const gdLst = custGeom["a:gdLst"];
33764
+ if (isNonEmpty(gdLst)) {
33765
+ result.gdLstXml = gdLst;
33766
+ }
33767
+ const ahLst = custGeom["a:ahLst"];
33768
+ if (isNonEmpty(ahLst)) {
33769
+ result.ahLstXml = ahLst;
33770
+ }
33771
+ const cxnLst = custGeom["a:cxnLst"];
33772
+ if (isNonEmpty(cxnLst)) {
33773
+ result.cxnLstXml = cxnLst;
33774
+ }
33775
+ const rect = custGeom["a:rect"];
33776
+ if (isNonEmpty(rect)) {
33777
+ result.rectXml = rect;
33778
+ }
33779
+ return Object.keys(result).length > 0 ? result : void 0;
33780
+ }
33781
+ /**
33782
+ * Build structured `CustomGeometryPath[]` from a parsed `a:custGeom` node,
33783
+ * including per-path `@fill`/`@stroke`/`@extrusionOk` attributes so they
33784
+ * survive a round-trip when the path list is later regenerated.
33785
+ *
33786
+ * Falls back to SVG → structured-path conversion when no structured path
33787
+ * info is otherwise available.
33788
+ */
33789
+ buildStructuredCustomGeometryPaths(custGeom, pathData, pathWidth, pathHeight) {
33790
+ if (!custGeom) {
33791
+ return void 0;
33792
+ }
33793
+ const pathLst = custGeom["a:pathLst"];
33794
+ if (!pathLst) {
33795
+ return void 0;
33796
+ }
33797
+ const pathNodes = this.ensureArray(pathLst["a:path"]);
33798
+ if (pathNodes.length === 0) {
33799
+ return void 0;
33800
+ }
33801
+ const segments = svgToCustomGeometryPaths(pathData, pathWidth, pathHeight);
33802
+ if (segments.length === 0) {
33803
+ return void 0;
33804
+ }
33805
+ const validFillModes = /* @__PURE__ */ new Set([
33806
+ "norm",
33807
+ "lighten",
33808
+ "lightenLess",
33809
+ "darken",
33810
+ "darkenLess",
33811
+ "none"
33812
+ ]);
33813
+ const parseBoolAttr2 = (value) => {
33814
+ if (value === void 0 || value === null || value === "") {
33815
+ return void 0;
33816
+ }
33817
+ if (value === "1" || value === "true" || value === true) {
33818
+ return true;
33819
+ }
33820
+ if (value === "0" || value === "false" || value === false) {
33821
+ return false;
33822
+ }
33823
+ return void 0;
33824
+ };
33825
+ const target = segments[0];
33826
+ const firstNode = pathNodes[0];
33827
+ const fillAttr = String(firstNode["@_fill"] ?? "").trim();
33828
+ if (validFillModes.has(fillAttr)) {
33829
+ target.fillMode = fillAttr;
33830
+ }
33831
+ const strokeAttr = parseBoolAttr2(firstNode["@_stroke"]);
33832
+ if (strokeAttr !== void 0) {
33833
+ target.stroke = strokeAttr;
33834
+ }
33835
+ const extrusionAttr = parseBoolAttr2(firstNode["@_extrusionOk"]);
33836
+ if (extrusionAttr !== void 0) {
33837
+ target.extrusionOk = extrusionAttr;
33838
+ }
33839
+ return segments;
33840
+ }
31986
33841
  parseCustomGeometry(custGeom, shapeWidth, shapeHeight) {
31987
33842
  if (!custGeom || !custGeom["a:pathLst"] || !custGeom["a:pathLst"]?.["a:path"]) {
31988
33843
  return null;
@@ -32051,7 +33906,7 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
32051
33906
  };
32052
33907
 
32053
33908
  // src/core/core/runtime/PptxHandlerRuntimeShapeImageFill.ts
32054
- var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime37 {
33909
+ var PptxHandlerRuntime39 = class _PptxHandlerRuntime extends PptxHandlerRuntime38 {
32055
33910
  /**
32056
33911
  * Parse a shape that has an image fill (a:blipFill inside spPr)
32057
33912
  * This handles shapes like rectangles filled with images (e.g., wood texture backgrounds)
@@ -32251,7 +34106,7 @@ var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
32251
34106
  };
32252
34107
 
32253
34108
  // src/core/core/runtime/PptxHandlerRuntimeTextDefaults.ts
32254
- var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
34109
+ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32255
34110
  /**
32256
34111
  * Apply {@link PlaceholderDefaults} body-level properties to a
32257
34112
  * {@link TextStyle} as fallback values (only sets fields that are
@@ -32375,7 +34230,7 @@ var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
32375
34230
  };
32376
34231
 
32377
34232
  // src/core/core/runtime/PptxHandlerRuntimeBulletParsing.ts
32378
- var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34233
+ var PptxHandlerRuntime41 = class extends PptxHandlerRuntime40 {
32379
34234
  resolveParagraphBulletInfo(paragraph, paragraphIndex, txBody, inheritedTxBody, isBodyPlaceholder = false, slidePath) {
32380
34235
  if (!paragraph) {
32381
34236
  return null;
@@ -32406,7 +34261,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32406
34261
  if (candidate["a:buNone"]) {
32407
34262
  return { none: true };
32408
34263
  }
32409
- if (candidate["a:buChar"] || candidate["a:buAutoNum"] || candidate["a:buBlip"]) {
34264
+ if (candidate["a:buChar"] || candidate["a:buAutoNum"] || candidate["a:buBlip"] || candidate["a:buFontTx"] !== void 0 || candidate["a:buClrTx"] !== void 0 || candidate["a:buSzTx"] !== void 0) {
32410
34265
  resolvedBulletProps = candidate;
32411
34266
  break;
32412
34267
  }
@@ -32420,6 +34275,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32420
34275
  }
32421
34276
  const buFont = resolvedBulletProps["a:buFont"];
32422
34277
  const fontFamily = buFont?.["@_typeface"] ? String(buFont["@_typeface"]) : void 0;
34278
+ const fontInherit = resolvedBulletProps["a:buFontTx"] !== void 0;
32423
34279
  const buSzPct = resolvedBulletProps["a:buSzPct"];
32424
34280
  let sizePercent;
32425
34281
  if (buSzPct?.["@_val"] !== void 0) {
@@ -32436,6 +34292,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32436
34292
  sizePts = ptsRaw / 100;
32437
34293
  }
32438
34294
  }
34295
+ const sizeInherit = resolvedBulletProps["a:buSzTx"] !== void 0;
32439
34296
  const buClr = resolvedBulletProps["a:buClr"];
32440
34297
  let color;
32441
34298
  if (buClr) {
@@ -32444,6 +34301,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32444
34301
  color = String(srgb["@_val"]);
32445
34302
  }
32446
34303
  }
34304
+ const colorInherit = resolvedBulletProps["a:buClrTx"] !== void 0;
32447
34305
  const bulletChar = String(
32448
34306
  resolvedBulletProps["a:buChar"]?.["@_char"] || ""
32449
34307
  );
@@ -32453,7 +34311,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32453
34311
  fontFamily,
32454
34312
  sizePercent,
32455
34313
  sizePts,
32456
- color
34314
+ color,
34315
+ ...fontInherit ? { fontInherit: true } : {},
34316
+ ...colorInherit ? { colorInherit: true } : {},
34317
+ ...sizeInherit ? { sizeInherit: true } : {}
32457
34318
  };
32458
34319
  }
32459
34320
  const autoNum = resolvedBulletProps["a:buAutoNum"];
@@ -32468,7 +34329,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32468
34329
  fontFamily,
32469
34330
  sizePercent,
32470
34331
  sizePts,
32471
- color
34332
+ color,
34333
+ ...fontInherit ? { fontInherit: true } : {},
34334
+ ...colorInherit ? { colorInherit: true } : {},
34335
+ ...sizeInherit ? { sizeInherit: true } : {}
32472
34336
  };
32473
34337
  }
32474
34338
  const buBlip = resolvedBulletProps["a:buBlip"];
@@ -32580,7 +34444,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
32580
34444
  };
32581
34445
 
32582
34446
  // src/core/core/runtime/PptxHandlerRuntimeShapeBodyParsing.ts
32583
- var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime40 {
34447
+ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
32584
34448
  /**
32585
34449
  * Parse `a:spLocks` attributes into a structured {@link PptxShapeLocks} object.
32586
34450
  * Returns `undefined` when the node is absent or contains no lock attributes.
@@ -32675,7 +34539,8 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32675
34539
  if (Number.isFinite(spcColRaw) && spcColRaw > 0) {
32676
34540
  textStyle.columnSpacing = spcColRaw / _PptxHandlerRuntime.EMU_PER_PX;
32677
34541
  }
32678
- const hOverflow = String(bodyPr["@_hOverflow"] || "").trim();
34542
+ const hOverflowRaw = bodyPr["@_horzOverflow"] ?? bodyPr["@_hOverflow"];
34543
+ const hOverflow = String(hOverflowRaw || "").trim();
32679
34544
  if (hOverflow === "overflow" || hOverflow === "clip") {
32680
34545
  textStyle.hOverflow = hOverflow;
32681
34546
  }
@@ -32858,7 +34723,7 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
32858
34723
  };
32859
34724
 
32860
34725
  // src/core/core/runtime/PptxHandlerRuntimeShapeTextParsing.ts
32861
- var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
34726
+ var PptxHandlerRuntime43 = class _PptxHandlerRuntime extends PptxHandlerRuntime42 {
32862
34727
  /**
32863
34728
  * Resolve paragraph-level styles (alignment, spacing, margins, tabs,
32864
34729
  * level styles) for a single paragraph. Modifies `textStyle` in place
@@ -33040,7 +34905,7 @@ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33040
34905
  };
33041
34906
 
33042
34907
  // src/core/core/runtime/PptxHandlerRuntimeShapeParagraphContentParsing.ts
33043
- var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
34908
+ var PptxHandlerRuntime44 = class extends PptxHandlerRuntime43 {
33044
34909
  /**
33045
34910
  * Collect text content (runs, fields, equations, bullets) for a single
33046
34911
  * paragraph and return text parts + segments. The returned `seedStyle`
@@ -33223,6 +35088,23 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33223
35088
  parts.push("\n");
33224
35089
  segments.push({ text: "\n", style: { ...mergedDefaultRunStyle } });
33225
35090
  }
35091
+ const firstSegmentIndex = segments.length === 0 ? -1 : 0;
35092
+ if (firstSegmentIndex >= 0) {
35093
+ const pPrRaw = p["a:pPr"];
35094
+ const lvlRaw = pPrRaw?.["@_lvl"];
35095
+ if (lvlRaw !== void 0) {
35096
+ const lvlParsed = Number.parseInt(String(lvlRaw), 10);
35097
+ if (Number.isFinite(lvlParsed) && lvlParsed > 0) {
35098
+ segments[firstSegmentIndex].paragraphLevel = Math.min(Math.max(lvlParsed, 0), 8);
35099
+ }
35100
+ }
35101
+ const endParaRPrRaw = p["a:endParaRPr"];
35102
+ if (endParaRPrRaw && typeof endParaRPrRaw === "object") {
35103
+ segments[firstSegmentIndex].endParaRunProperties = {
35104
+ ...endParaRPrRaw
35105
+ };
35106
+ }
35107
+ }
33226
35108
  return { parts, segments, seedStyle };
33227
35109
  }
33228
35110
  /**
@@ -33332,7 +35214,7 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
33332
35214
  };
33333
35215
 
33334
35216
  // src/core/core/runtime/PptxHandlerRuntimeShapeParsing.ts
33335
- var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime43 {
35217
+ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
33336
35218
  parseShape(shape, id, slidePath) {
33337
35219
  try {
33338
35220
  const spPr = shape["p:spPr"];
@@ -33368,6 +35250,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33368
35250
  let pathData;
33369
35251
  let pathWidth;
33370
35252
  let pathHeight;
35253
+ let customGeometryRawData;
33371
35254
  const custGeom = effectiveSpPr?.["a:custGeom"];
33372
35255
  if (custGeom) {
33373
35256
  const customPath = this.parseCustomGeometry(
@@ -33380,6 +35263,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33380
35263
  pathData = customPath.pathData;
33381
35264
  pathWidth = customPath.pathWidth;
33382
35265
  pathHeight = customPath.pathHeight;
35266
+ customGeometryRawData = this.extractCustomGeometryRawData(custGeom);
33383
35267
  }
33384
35268
  }
33385
35269
  const geomNode = custGeom ?? effectiveSpPr?.["a:prstGeom"];
@@ -33537,7 +35421,8 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33537
35421
  type: "shape",
33538
35422
  pathData,
33539
35423
  pathWidth,
33540
- pathHeight
35424
+ pathHeight,
35425
+ customGeometryRawData
33541
35426
  };
33542
35427
  } catch (e) {
33543
35428
  console.warn(`[pptx] Skipping shape element (${id}):`, e);
@@ -33547,7 +35432,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33547
35432
  };
33548
35433
 
33549
35434
  // src/core/core/runtime/PptxHandlerRuntimePictureParsing.ts
33550
- var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
35435
+ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
33551
35436
  async parsePicture(pic, id, slidePath) {
33552
35437
  try {
33553
35438
  const spPr = pic["p:spPr"];
@@ -33786,7 +35671,7 @@ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
33786
35671
  };
33787
35672
 
33788
35673
  // src/core/core/runtime/PptxHandlerRuntimeSpTreeParsing.ts
33789
- var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
35674
+ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
33790
35675
  /**
33791
35676
  * Known element tag names that appear as direct children of `p:spTree`
33792
35677
  * (or `p:grpSp`) and represent renderable shapes/objects.
@@ -34131,9 +36016,18 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34131
36016
  * Unwrap mc:AlternateContent elements within a shape tree (or group),
34132
36017
  * merging selected branch children into the parent element arrays.
34133
36018
  * Delegates to the standalone alternate-content utility.
36019
+ *
36020
+ * Records each consumed AC envelope in {@link alternateContentBlockByRawXml}
36021
+ * so the save layer can re-emit the original `<mc:Choice>` /
36022
+ * `<mc:Fallback>` shape on dirty save (CC-4).
34134
36023
  */
34135
36024
  unwrapAlternateContent(container) {
34136
- unwrapAlternateContent(container);
36025
+ const blocks = unwrapAlternateContent(container);
36026
+ for (const block of blocks) {
36027
+ for (const ref of block.childRefs) {
36028
+ this.alternateContentBlockByRawXml.set(ref.node, block);
36029
+ }
36030
+ }
34137
36031
  }
34138
36032
  /**
34139
36033
  * Forward declaration – implemented in PptxHandlerRuntimeGroupParsing.
@@ -34144,7 +36038,7 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34144
36038
  };
34145
36039
 
34146
36040
  // src/core/core/runtime/PptxHandlerRuntimeGroupParsing.ts
34147
- var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
36041
+ var PptxHandlerRuntime48 = class _PptxHandlerRuntime extends PptxHandlerRuntime47 {
34148
36042
  async parseGroupShape(group, baseId, slidePath, rawXmlStr) {
34149
36043
  const grpSpPr = group["p:grpSpPr"];
34150
36044
  const xfrm = grpSpPr?.["a:xfrm"];
@@ -34317,7 +36211,7 @@ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34317
36211
  };
34318
36212
 
34319
36213
  // src/core/core/runtime/PptxHandlerRuntimeSlideParsing.ts
34320
- var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
36214
+ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34321
36215
  /**
34322
36216
  * Parse text body from a connector shape (p:cxnSp > p:txBody).
34323
36217
  * Uses the same text run extraction logic as regular shapes but
@@ -34429,7 +36323,7 @@ var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
34429
36323
  };
34430
36324
 
34431
36325
  // src/core/core/runtime/PptxHandlerRuntimeColorAndEffects.ts
34432
- var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
36326
+ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34433
36327
  /**
34434
36328
  * Forward declaration – implemented in PptxHandlerRuntimeThemeProcessing.
34435
36329
  * Re-resolves gradient stops by substituting `phClr` with the given colour.
@@ -34470,8 +36364,9 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34470
36364
  return void 0;
34471
36365
  }
34472
36366
  const resolvedKey = normalized === "phclr" ? "accent1" : normalized;
34473
- if (this.currentSlideClrMapOverride) {
34474
- const remapped = this.currentSlideClrMapOverride[resolvedKey];
36367
+ const overrideMap = this.currentSlideClrMapOverride ?? this.currentMasterClrMap;
36368
+ if (overrideMap) {
36369
+ const remapped = overrideMap[resolvedKey];
34475
36370
  if (remapped) {
34476
36371
  return this.themeColorMap[remapped] || this.getDefaultSchemeColorMap()[remapped];
34477
36372
  }
@@ -34554,7 +36449,7 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
34554
36449
  };
34555
36450
 
34556
36451
  // src/core/core/runtime/PptxHandlerRuntimeBackgroundParsing.ts
34557
- var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
36452
+ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34558
36453
  async extractBackgroundImage(slideXml2, slidePath, rootElement = "p:sld") {
34559
36454
  try {
34560
36455
  const bg = slideXml2[rootElement]?.["p:cSld"]?.["p:bg"];
@@ -34765,7 +36660,7 @@ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
34765
36660
  };
34766
36661
 
34767
36662
  // src/core/core/runtime/PptxHandlerRuntimeSlideUtils.ts
34768
- var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
36663
+ var PptxHandlerRuntime52 = class extends PptxHandlerRuntime51 {
34769
36664
  /**
34770
36665
  * Retrieve the background gradient from a layout, falling back to master.
34771
36666
  */
@@ -34836,6 +36731,64 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34836
36731
  }
34837
36732
  return void 0;
34838
36733
  }
36734
+ /**
36735
+ * Find the master file path referenced by a layout via its relationships.
36736
+ */
36737
+ findMasterPathForLayoutBase(layoutPath) {
36738
+ const layoutRels = this.slideRelsMap.get(layoutPath);
36739
+ if (!layoutRels) {
36740
+ return void 0;
36741
+ }
36742
+ for (const [, target] of layoutRels.entries()) {
36743
+ if (target.includes("slideMaster")) {
36744
+ const layoutDir = layoutPath.substring(0, layoutPath.lastIndexOf("/") + 1);
36745
+ return target.startsWith("..") ? this.resolvePath(layoutDir, target) : `ppt/${target.replace("../", "")}`;
36746
+ }
36747
+ }
36748
+ return void 0;
36749
+ }
36750
+ /**
36751
+ * Switch the active master state (clrMap + theme color/font/format
36752
+ * scheme) so that scheme-colour resolution for the slide currently
36753
+ * being parsed walks through the correct master.
36754
+ *
36755
+ * Multi-master decks must resolve scheme colours against each slide's
36756
+ * own master rather than always against `masterFiles[0]`.
36757
+ *
36758
+ * Phase 2 Stream B / C-H4.
36759
+ */
36760
+ async setActiveMasterForSlide(slidePath) {
36761
+ const layoutPath = this.findLayoutPathForSlide(slidePath);
36762
+ if (!layoutPath) {
36763
+ this.currentMasterClrMap = null;
36764
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
36765
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
36766
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
36767
+ return;
36768
+ }
36769
+ if (!this.slideRelsMap.has(layoutPath)) {
36770
+ const layoutRelsPath = `${layoutPath.replace("slideLayouts/", "slideLayouts/_rels/")}.rels`;
36771
+ try {
36772
+ await this.loadSlideRelationships(layoutPath, layoutRelsPath);
36773
+ } catch {
36774
+ }
36775
+ }
36776
+ const masterPath = this.findMasterPathForLayoutBase(layoutPath);
36777
+ if (!masterPath) {
36778
+ this.currentMasterClrMap = null;
36779
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
36780
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
36781
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
36782
+ return;
36783
+ }
36784
+ this.currentMasterClrMap = this.masterClrMaps.get(masterPath) ?? null;
36785
+ const masterColorMap = this.masterThemeColorMaps.get(masterPath);
36786
+ this.themeColorMap = masterColorMap ? { ...masterColorMap } : { ...this.globalThemeColorMapSnapshot };
36787
+ const masterFontMap = this.masterThemeFontMaps.get(masterPath);
36788
+ this.themeFontMap = masterFontMap ? { ...masterFontMap } : { ...this.globalThemeFontMapSnapshot };
36789
+ const masterFormatScheme = this.masterThemeFormatSchemes.get(masterPath);
36790
+ this.themeFormatScheme = masterFormatScheme ?? this.globalThemeFormatSchemeSnapshot;
36791
+ }
34839
36792
  /**
34840
36793
  * Extract the `p:bg/@showAnimation` flag from a slide's XML.
34841
36794
  * Returns `true` when the background should animate, `false` when
@@ -34976,7 +36929,7 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
34976
36929
  };
34977
36930
 
34978
36931
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderStyles.ts
34979
- var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime51 {
36932
+ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
34980
36933
  /**
34981
36934
  * Parse a single `a:lvlXpPr` node into a structured
34982
36935
  * {@link PlaceholderTextLevelStyle}.
@@ -35097,7 +37050,7 @@ var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35097
37050
  };
35098
37051
 
35099
37052
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderDefaults.ts
35100
- var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
37053
+ var PptxHandlerRuntime54 = class _PptxHandlerRuntime extends PptxHandlerRuntime53 {
35101
37054
  /**
35102
37055
  * Extract structured {@link PlaceholderDefaults} from a layout or master
35103
37056
  * shape that carries a `p:ph` element.
@@ -35240,7 +37193,118 @@ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35240
37193
  };
35241
37194
 
35242
37195
  // src/core/core/runtime/PptxHandlerRuntimeMasterElements.ts
35243
- var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
37196
+ function parseHeaderFooterFlags(hf) {
37197
+ if (!hf) {
37198
+ return void 0;
37199
+ }
37200
+ const result = {};
37201
+ if (hf["@_hdr"] !== void 0) {
37202
+ result.hasHeader = String(hf["@_hdr"]) !== "0";
37203
+ }
37204
+ if (hf["@_ftr"] !== void 0) {
37205
+ result.hasFooter = String(hf["@_ftr"]) !== "0";
37206
+ }
37207
+ if (hf["@_dt"] !== void 0) {
37208
+ result.hasDateTime = String(hf["@_dt"]) !== "0";
37209
+ }
37210
+ if (hf["@_sldNum"] !== void 0) {
37211
+ result.hasSlideNumber = String(hf["@_sldNum"]) !== "0";
37212
+ }
37213
+ return Object.keys(result).length > 0 ? result : void 0;
37214
+ }
37215
+ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37216
+ /**
37217
+ * Parse a `CT_TextListStyle` node (`a:defPPr` + `a:lvl1pPr` … `a:lvl9pPr`)
37218
+ * into a level-keyed style map. Used for `<p:txStyles>` children
37219
+ * (`p:titleStyle`, `p:bodyStyle`, `p:otherStyle`) — see ECMA-376 §19.3.1.52.
37220
+ */
37221
+ parseTextListStyle(node) {
37222
+ if (!node) {
37223
+ return void 0;
37224
+ }
37225
+ const levels = {};
37226
+ const defParsed = this.parsePlaceholderLevelStyle(node["a:defPPr"]);
37227
+ if (defParsed) {
37228
+ levels[-1] = defParsed;
37229
+ }
37230
+ for (let lvl = 1; lvl <= 9; lvl++) {
37231
+ const parsed = this.parsePlaceholderLevelStyle(
37232
+ node[`a:lvl${lvl}pPr`]
37233
+ );
37234
+ if (parsed) {
37235
+ levels[lvl - 1] = parsed;
37236
+ }
37237
+ }
37238
+ return Object.keys(levels).length > 0 ? levels : void 0;
37239
+ }
37240
+ /**
37241
+ * Parse `<p:txStyles>` from a slide-master XML object into a structured
37242
+ * {@link PptxMasterTextStyles}. Used to populate `PptxSlideMaster.txStyles`
37243
+ * so the title/body/other text-style cascade (P-H1) is visible on the
37244
+ * typed model.
37245
+ */
37246
+ parseMasterTxStyles(masterXml) {
37247
+ const txStyles = masterXml?.["p:txStyles"];
37248
+ if (!txStyles) {
37249
+ return void 0;
37250
+ }
37251
+ const titleStyle = this.parseTextListStyle(txStyles["p:titleStyle"]);
37252
+ const bodyStyle = this.parseTextListStyle(txStyles["p:bodyStyle"]);
37253
+ const otherStyle = this.parseTextListStyle(txStyles["p:otherStyle"]);
37254
+ if (!titleStyle && !bodyStyle && !otherStyle) {
37255
+ return void 0;
37256
+ }
37257
+ const result = {};
37258
+ if (titleStyle) {
37259
+ result.titleStyle = titleStyle;
37260
+ }
37261
+ if (bodyStyle) {
37262
+ result.bodyStyle = bodyStyle;
37263
+ }
37264
+ if (otherStyle) {
37265
+ result.otherStyle = otherStyle;
37266
+ }
37267
+ return result;
37268
+ }
37269
+ /**
37270
+ * Enrich an array of {@link PptxSlideMaster} entries (already produced by
37271
+ * `parseSlideMasters`) with parsed `<p:txStyles>`. Loads each master's XML
37272
+ * once, parses, and caches it in `masterXmlMap` for downstream consumers.
37273
+ *
37274
+ * Also stores the parsed result on the per-master cache so that the
37275
+ * inheritance chain in `applyMasterTextStyleCascade` can find it without
37276
+ * re-parsing.
37277
+ */
37278
+ async enrichSlideMastersWithTxStyles(slideMasters) {
37279
+ for (const master of slideMasters) {
37280
+ try {
37281
+ let masterXmlObj = this.masterXmlMap.get(master.path);
37282
+ if (!masterXmlObj) {
37283
+ const xmlStr = await this.zip.file(master.path)?.async("string");
37284
+ if (!xmlStr) {
37285
+ continue;
37286
+ }
37287
+ masterXmlObj = this.parser.parse(xmlStr);
37288
+ this.masterXmlMap.set(master.path, masterXmlObj);
37289
+ }
37290
+ const sldMaster = masterXmlObj["p:sldMaster"];
37291
+ if (!sldMaster) {
37292
+ continue;
37293
+ }
37294
+ const parsed = this.parseMasterTxStyles(sldMaster);
37295
+ if (parsed) {
37296
+ master.txStyles = parsed;
37297
+ this.masterTxStylesCache.set(master.path, parsed);
37298
+ }
37299
+ const hf = parseHeaderFooterFlags(sldMaster["p:hf"]);
37300
+ if (hf) {
37301
+ master.headerFooter = hf;
37302
+ }
37303
+ } catch (e) {
37304
+ console.warn("Failed to parse master txStyles:", e);
37305
+ }
37306
+ }
37307
+ }
35244
37308
  parsePresentationDefaultTextStyle() {
35245
37309
  const presentation = this.presentationData?.["p:presentation"];
35246
37310
  const defaultTextStyle = presentation?.["p:defaultTextStyle"];
@@ -35396,7 +37460,7 @@ var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
35396
37460
  };
35397
37461
 
35398
37462
  // src/core/core/runtime/PptxHandlerRuntimeLayoutElements.ts
35399
- var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37463
+ var PptxHandlerRuntime56 = class extends PptxHandlerRuntime55 {
35400
37464
  async getLayoutElements(slidePath) {
35401
37465
  const slideRels = this.slideRelsMap.get(slidePath);
35402
37466
  if (!slideRels) {
@@ -35520,10 +37584,10 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35520
37584
  }
35521
37585
  }
35522
37586
  }
35523
- this.currentSlideClrMapOverride = prevClrMapOverride;
35524
37587
  const layoutShowMasterSp = layoutXmlObj["p:sldLayout"]?.["@_showMasterSp"];
35525
37588
  const showMasterSp = layoutShowMasterSp === void 0 || String(layoutShowMasterSp).trim().toLowerCase() !== "0" && String(layoutShowMasterSp).trim().toLowerCase() !== "false";
35526
37589
  const masterElements = showMasterSp ? await this.getMasterElements(layoutPath) : [];
37590
+ this.currentSlideClrMapOverride = prevClrMapOverride;
35527
37591
  const allElements = [...masterElements, ...elements];
35528
37592
  this.layoutCache.set(layoutPath, allElements);
35529
37593
  return allElements;
@@ -35535,7 +37599,7 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
35535
37599
  };
35536
37600
 
35537
37601
  // src/core/core/runtime/PptxHandlerRuntimeThemeFormatScheme.ts
35538
- var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime55 {
37602
+ var PptxHandlerRuntime57 = class _PptxHandlerRuntime extends PptxHandlerRuntime56 {
35539
37603
  /**
35540
37604
  * Collect fill-style children from a style list node, preserving
35541
37605
  * document order. Handles `a:solidFill`, `a:gradFill`, `a:pattFill`,
@@ -35777,7 +37841,7 @@ var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
35777
37841
  };
35778
37842
 
35779
37843
  // src/core/core/runtime/PptxHandlerRuntimeThemeOverrides.ts
35780
- var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
37844
+ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
35781
37845
  /**
35782
37846
  * Parse the `a:fmtScheme` element from the theme into a structured
35783
37847
  * {@link PptxThemeFormatScheme}. Each sub-list (fillStyleLst, lnStyleLst,
@@ -35974,8 +38038,10 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
35974
38038
  }
35975
38039
  const fontScheme = root["a:fontScheme"];
35976
38040
  if (fontScheme) {
35977
- const majorLatin = fontScheme["a:majorFont"]?.["a:latin"];
35978
- const minorLatin = fontScheme["a:minorFont"]?.["a:latin"];
38041
+ const majorFontNode = fontScheme["a:majorFont"];
38042
+ const minorFontNode = fontScheme["a:minorFont"];
38043
+ const majorLatin = majorFontNode?.["a:latin"];
38044
+ const minorLatin = minorFontNode?.["a:latin"];
35979
38045
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
35980
38046
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
35981
38047
  if (!result.colorOverrides) {
@@ -35998,13 +38064,46 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
35998
38064
  };
35999
38065
 
36000
38066
  // src/core/core/runtime/PptxHandlerRuntimeThemeRefResolution.ts
36001
- var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
38067
+ var COLOR_CHOICE_KEYS2 = [
38068
+ "a:scrgbClr",
38069
+ "a:srgbClr",
38070
+ "a:hslClr",
38071
+ "a:sysClr",
38072
+ "a:schemeClr",
38073
+ "a:prstClr"
38074
+ ];
38075
+ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38076
+ /**
38077
+ * Pull the verbatim colour-choice child out of a style-matrix-reference
38078
+ * element (`a:lnRef`/`a:fillRef`/`a:effectRef`/`a:fontRef`). Returns the
38079
+ * full child object so it can be round-tripped at save time, preserving
38080
+ * any contained colour transforms (`a:lumMod`, `a:tint`, etc.).
38081
+ */
38082
+ extractRefColorXml(refNode) {
38083
+ if (!refNode) {
38084
+ return void 0;
38085
+ }
38086
+ for (const key of COLOR_CHOICE_KEYS2) {
38087
+ const child = refNode[key];
38088
+ if (child !== void 0) {
38089
+ return { [key]: child };
38090
+ }
38091
+ }
38092
+ return void 0;
38093
+ }
36002
38094
  /**
36003
38095
  * Resolve a `a:effectRef` element into concrete effect properties
36004
38096
  * by looking up `@_idx` in the theme format scheme's effect style list.
36005
38097
  */
36006
38098
  resolveThemeEffectRef(refNode, style) {
36007
38099
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
38100
+ if (Number.isFinite(idx) && idx > 0) {
38101
+ style.effectRefIdx = idx;
38102
+ }
38103
+ const overrideColorXml = this.extractRefColorXml(refNode);
38104
+ if (overrideColorXml) {
38105
+ style.effectRefColorXml = overrideColorXml;
38106
+ }
36008
38107
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.effectStyles.length) {
36009
38108
  return;
36010
38109
  }
@@ -36057,6 +38156,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36057
38156
  resolveThemeLineRef(refNode, style) {
36058
38157
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
36059
38158
  const overrideColor = this.parseColor(refNode);
38159
+ if (Number.isFinite(idx) && idx > 0) {
38160
+ style.lnRefIdx = idx;
38161
+ }
38162
+ const overrideColorXml = this.extractRefColorXml(refNode);
38163
+ if (overrideColorXml) {
38164
+ style.lnRefColorXml = overrideColorXml;
38165
+ }
36060
38166
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.lineStyles.length) {
36061
38167
  style.strokeColor = overrideColor;
36062
38168
  if (overrideColor) {
@@ -36128,6 +38234,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36128
38234
  */
36129
38235
  resolveThemeFillRef(refNode, style) {
36130
38236
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
38237
+ if (Number.isFinite(idx) && idx > 0) {
38238
+ style.fillRefIdx = idx;
38239
+ }
38240
+ const overrideColorXml = this.extractRefColorXml(refNode);
38241
+ if (overrideColorXml) {
38242
+ style.fillRefColorXml = overrideColorXml;
38243
+ }
36131
38244
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme) {
36132
38245
  style.fillMode = "theme";
36133
38246
  style.fillColor = this.parseColor(refNode);
@@ -36193,7 +38306,7 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
36193
38306
  };
36194
38307
 
36195
38308
  // src/core/core/runtime/PptxHandlerRuntimeThemeLoading.ts
36196
- var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38309
+ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36197
38310
  async resolvePrimaryThemePath() {
36198
38311
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36199
38312
  if (!masterFiles || masterFiles.length === 0) {
@@ -36303,62 +38416,97 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36303
38416
  formatScheme: this.themeFormatScheme
36304
38417
  };
36305
38418
  }
36306
- async applySlideMasterColorMap(defaultMap) {
38419
+ /**
38420
+ * Parse every slide master's `<p:clrMap>` element and store the alias
38421
+ * dictionaries on {@link masterClrMaps}. Do *not* mutate
38422
+ * {@link themeColorMap} — alias resolution happens at colour-lookup
38423
+ * time so that:
38424
+ *
38425
+ * 1. The raw theme scheme stays the source of truth (clrMap is a
38426
+ * routing layer, not a colour table).
38427
+ * 2. Multi-master decks resolve each slide against its own master's
38428
+ * clrMap rather than always against `masterFiles[0]`.
38429
+ * 3. Layout `clrMapOvr` semantics work correctly when a slide's master
38430
+ * differs from the deck's first master.
38431
+ *
38432
+ * Phase 2 Stream B / C-H4.
38433
+ */
38434
+ async applySlideMasterColorMap(_defaultMap) {
36307
38435
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
36308
38436
  if (!masterFiles || masterFiles.length === 0) {
36309
38437
  return;
36310
38438
  }
36311
- try {
36312
- const masterXml = await masterFiles[0].async("string");
36313
- const masterData = this.parser.parse(masterXml);
36314
- const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
36315
- if (!clrMap) {
36316
- return;
36317
- }
36318
- const aliasKeys = [
36319
- "bg1",
36320
- "tx1",
36321
- "bg2",
36322
- "tx2",
36323
- "accent1",
36324
- "accent2",
36325
- "accent3",
36326
- "accent4",
36327
- "accent5",
36328
- "accent6",
36329
- "hlink",
36330
- "folHlink"
36331
- ];
36332
- for (const aliasKey of aliasKeys) {
36333
- const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
36334
- if (!mappedKey) {
38439
+ const aliasKeys = [
38440
+ "bg1",
38441
+ "tx1",
38442
+ "bg2",
38443
+ "tx2",
38444
+ "accent1",
38445
+ "accent2",
38446
+ "accent3",
38447
+ "accent4",
38448
+ "accent5",
38449
+ "accent6",
38450
+ "hlink",
38451
+ "folHlink"
38452
+ ];
38453
+ for (const file of masterFiles) {
38454
+ try {
38455
+ const masterXml = await file.async("string");
38456
+ const masterData = this.parser.parse(masterXml);
38457
+ const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
38458
+ if (!clrMap) {
36335
38459
  continue;
36336
38460
  }
36337
- const mappedColor = this.themeColorMap[mappedKey] || defaultMap[mappedKey];
36338
- if (mappedColor) {
36339
- this.themeColorMap[aliasKey] = mappedColor;
38461
+ const aliasMap = {};
38462
+ for (const aliasKey of aliasKeys) {
38463
+ const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
38464
+ if (mappedKey) {
38465
+ aliasMap[aliasKey] = mappedKey;
38466
+ }
38467
+ }
38468
+ if (Object.keys(aliasMap).length > 0) {
38469
+ this.masterClrMaps.set(file.name, aliasMap);
36340
38470
  }
38471
+ } catch (error) {
38472
+ console.warn(`Failed to parse slide master color map for ${file.name}:`, error);
36341
38473
  }
36342
- } catch (error) {
36343
- console.warn("Failed to parse slide master color map:", error);
36344
38474
  }
36345
38475
  }
36346
- async loadThemeData() {
36347
- const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
36348
- if (!themeFiles || themeFiles.length === 0) {
36349
- return;
38476
+ /**
38477
+ * Parse a single theme part into structured colour, font, and format
38478
+ * scheme dictionaries. Used both for the global default theme and for
38479
+ * each master's per-master theme (multi-master support).
38480
+ *
38481
+ * Phase 2 Stream B / C-H4.
38482
+ */
38483
+ async parseThemePart(themePath) {
38484
+ const themeFile = this.zip.file(themePath);
38485
+ if (!themeFile) {
38486
+ return null;
36350
38487
  }
36351
- const preferredThemePath = await this.resolvePrimaryThemePath();
36352
- const preferredThemeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) : void 0;
36353
- const themeFile = preferredThemeFile ?? themeFiles[0];
36354
38488
  const themeXml2 = await themeFile.async("string");
38489
+ this.originalThemeXmlByPath.set(themePath, themeXml2);
36355
38490
  const themeData = this.parser.parse(themeXml2);
36356
38491
  const themeRoot = themeData["a:theme"];
36357
38492
  const themeElements = themeRoot?.["a:themeElements"];
36358
38493
  const colorScheme = themeElements?.["a:clrScheme"];
36359
38494
  const fontScheme = themeElements?.["a:fontScheme"];
38495
+ const fmtScheme = themeElements?.["a:fmtScheme"];
38496
+ const themeName = String(themeRoot?.["@_name"] || "").trim();
38497
+ if (themeName) {
38498
+ this.masterThemeNames.set(themePath, themeName);
38499
+ }
38500
+ const colorSchemeName = String(colorScheme?.["@_name"] || "").trim();
38501
+ if (colorSchemeName) {
38502
+ this.masterThemeColorSchemeNames.set(themePath, colorSchemeName);
38503
+ }
38504
+ const fontSchemeName = String(fontScheme?.["@_name"] || "").trim();
38505
+ if (fontSchemeName) {
38506
+ this.masterThemeFontSchemeNames.set(themePath, fontSchemeName);
38507
+ }
36360
38508
  const defaultMap = this.getDefaultSchemeColorMap();
36361
- this.themeColorMap = { ...defaultMap };
38509
+ const colorMap = { ...defaultMap };
36362
38510
  if (colorScheme) {
36363
38511
  const schemeKeys = [
36364
38512
  "dk1",
@@ -36378,39 +38526,196 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
36378
38526
  const colorNode = colorScheme[`a:${key}`];
36379
38527
  const parsed = this.parseColorChoice(colorNode);
36380
38528
  if (parsed) {
36381
- this.themeColorMap[key] = parsed;
38529
+ colorMap[key] = parsed;
36382
38530
  }
36383
38531
  }
36384
38532
  }
36385
- this.themeColorMap["tx1"] = this.themeColorMap["dk1"] || defaultMap["dk1"];
36386
- this.themeColorMap["bg1"] = this.themeColorMap["lt1"] || defaultMap["lt1"];
36387
- this.themeColorMap["tx2"] = this.themeColorMap["dk2"] || defaultMap["dk2"];
36388
- this.themeColorMap["bg2"] = this.themeColorMap["lt2"] || defaultMap["lt2"];
36389
- await this.applySlideMasterColorMap(defaultMap);
36390
- const majorLatin = fontScheme?.["a:majorFont"]?.["a:latin"];
36391
- const minorLatin = fontScheme?.["a:minorFont"]?.["a:latin"];
38533
+ colorMap["tx1"] = colorMap["dk1"] || defaultMap["dk1"];
38534
+ colorMap["bg1"] = colorMap["lt1"] || defaultMap["lt1"];
38535
+ colorMap["tx2"] = colorMap["dk2"] || defaultMap["dk2"];
38536
+ colorMap["bg2"] = colorMap["lt2"] || defaultMap["lt2"];
38537
+ const majorFontNode = fontScheme?.["a:majorFont"];
38538
+ const minorFontNode = fontScheme?.["a:minorFont"];
38539
+ const majorLatin = majorFontNode?.["a:latin"];
38540
+ const minorLatin = minorFontNode?.["a:latin"];
36392
38541
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
36393
38542
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
36394
- this.themeFontMap = {};
38543
+ const fontMap = {};
36395
38544
  if (majorFont) {
36396
- this.themeFontMap["mj-lt"] = majorFont;
36397
- this.themeFontMap["mj-ea"] = majorFont;
36398
- this.themeFontMap["mj-cs"] = majorFont;
38545
+ fontMap["mj-lt"] = majorFont;
38546
+ fontMap["mj-ea"] = majorFont;
38547
+ fontMap["mj-cs"] = majorFont;
36399
38548
  }
36400
38549
  if (minorFont) {
36401
- this.themeFontMap["mn-lt"] = minorFont;
36402
- this.themeFontMap["mn-ea"] = minorFont;
36403
- this.themeFontMap["mn-cs"] = minorFont;
38550
+ fontMap["mn-lt"] = minorFont;
38551
+ fontMap["mn-ea"] = minorFont;
38552
+ fontMap["mn-cs"] = minorFont;
36404
38553
  }
36405
- const fmtScheme = themeElements?.["a:fmtScheme"];
36406
- if (fmtScheme) {
36407
- this.themeFormatScheme = this.parseFormatScheme(fmtScheme);
38554
+ const majorEa = this.normalizeTypefaceToken(
38555
+ String(majorFontNode?.["a:ea"]?.["@_typeface"] || "")
38556
+ );
38557
+ if (majorEa) {
38558
+ fontMap["mj-ea"] = majorEa;
36408
38559
  }
38560
+ const majorCs = this.normalizeTypefaceToken(
38561
+ String(majorFontNode?.["a:cs"]?.["@_typeface"] || "")
38562
+ );
38563
+ if (majorCs) {
38564
+ fontMap["mj-cs"] = majorCs;
38565
+ }
38566
+ const minorEa = this.normalizeTypefaceToken(
38567
+ String(minorFontNode?.["a:ea"]?.["@_typeface"] || "")
38568
+ );
38569
+ if (minorEa) {
38570
+ fontMap["mn-ea"] = minorEa;
38571
+ }
38572
+ const minorCs = this.normalizeTypefaceToken(
38573
+ String(minorFontNode?.["a:cs"]?.["@_typeface"] || "")
38574
+ );
38575
+ if (minorCs) {
38576
+ fontMap["mn-cs"] = minorCs;
38577
+ }
38578
+ const majorScripts = this.collectFontScriptOverrides(majorFontNode);
38579
+ if (Object.keys(majorScripts).length > 0) {
38580
+ this.masterThemeMajorFontScripts.set(themePath, majorScripts);
38581
+ }
38582
+ const minorScripts = this.collectFontScriptOverrides(minorFontNode);
38583
+ if (Object.keys(minorScripts).length > 0) {
38584
+ this.masterThemeMinorFontScripts.set(themePath, minorScripts);
38585
+ }
38586
+ const objectDefaultsNode = themeRoot?.["a:objectDefaults"];
38587
+ if (objectDefaultsNode) {
38588
+ const od = {
38589
+ spDef: objectDefaultsNode["a:spDef"],
38590
+ lnDef: objectDefaultsNode["a:lnDef"],
38591
+ txDef: objectDefaultsNode["a:txDef"]
38592
+ };
38593
+ if (od.spDef !== void 0 || od.lnDef !== void 0 || od.txDef !== void 0) {
38594
+ this.masterThemeObjectDefaults.set(themePath, od);
38595
+ }
38596
+ }
38597
+ const extraClrSchemeLst = themeRoot?.["a:extraClrSchemeLst"];
38598
+ if (extraClrSchemeLst !== void 0) {
38599
+ this.masterThemeExtraClrSchemeLst.set(themePath, extraClrSchemeLst);
38600
+ }
38601
+ const custClrLst = themeRoot?.["a:custClrLst"];
38602
+ if (custClrLst !== void 0) {
38603
+ this.masterThemeCustClrLst.set(themePath, custClrLst);
38604
+ }
38605
+ const themeExtLst = themeRoot?.["a:extLst"];
38606
+ if (themeExtLst !== void 0) {
38607
+ this.masterThemeExtLst.set(themePath, themeExtLst);
38608
+ }
38609
+ const formatScheme = fmtScheme ? this.parseFormatScheme(fmtScheme) : void 0;
38610
+ return { colorMap, fontMap, formatScheme };
38611
+ }
38612
+ /**
38613
+ * Parse `<a:font script="…" typeface="…"/>` children of a major or
38614
+ * minor font node into a `script -> typeface` dictionary.
38615
+ *
38616
+ * fast-xml-parser collapses repeated tags into arrays, so iterate
38617
+ * over the array form regardless of how many siblings are present.
38618
+ *
38619
+ * Phase 4 Stream A / M4.
38620
+ */
38621
+ collectFontScriptOverrides(fontNode) {
38622
+ const overrides = {};
38623
+ if (!fontNode) {
38624
+ return overrides;
38625
+ }
38626
+ const fontEntries = this.ensureArray(fontNode["a:font"]);
38627
+ for (const entry of fontEntries) {
38628
+ const script = String(entry?.["@_script"] || "").trim();
38629
+ const typeface = this.normalizeTypefaceToken(String(entry?.["@_typeface"] || ""));
38630
+ if (script && typeface) {
38631
+ overrides[script] = typeface;
38632
+ }
38633
+ }
38634
+ return overrides;
38635
+ }
38636
+ /**
38637
+ * Resolve the theme file path referenced by a given master's `.rels`.
38638
+ * Returns `undefined` when the master has no theme relationship.
38639
+ */
38640
+ async resolveThemePathForMaster(masterPath) {
38641
+ const relsPath = masterPath.replace(
38642
+ /ppt\/slideMasters\/(slideMaster\d+)\.xml/,
38643
+ "ppt/slideMasters/_rels/$1.xml.rels"
38644
+ );
38645
+ const relsXml = this.zip.file(relsPath);
38646
+ if (!relsXml) {
38647
+ return void 0;
38648
+ }
38649
+ const relsData = this.parser.parse(await relsXml.async("string"));
38650
+ const relNodes = this.ensureArray(relsData?.Relationships?.Relationship);
38651
+ for (const rel of relNodes) {
38652
+ const target = String(rel["@_Target"] || "");
38653
+ if (!target.includes("theme")) {
38654
+ continue;
38655
+ }
38656
+ const themePath = target.startsWith("..") ? this.resolvePath(masterPath.substring(0, masterPath.lastIndexOf("/") + 1), target) : target.startsWith("/") ? target.slice(1) : `ppt/${target.replace(/^\.?\//, "")}`;
38657
+ if (themePath.startsWith("ppt/theme/")) {
38658
+ return themePath;
38659
+ }
38660
+ }
38661
+ return void 0;
38662
+ }
38663
+ /**
38664
+ * Populate {@link masterThemeColorMaps}, {@link masterThemeFontMaps},
38665
+ * and {@link masterThemeFormatSchemes} for every slide master in the
38666
+ * deck. Multi-master support — Phase 2 Stream B / C-H4.
38667
+ */
38668
+ async loadPerMasterThemes() {
38669
+ const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
38670
+ if (!masterFiles || masterFiles.length === 0) {
38671
+ return;
38672
+ }
38673
+ for (const file of masterFiles) {
38674
+ try {
38675
+ const themePath = await this.resolveThemePathForMaster(file.name);
38676
+ if (!themePath) {
38677
+ continue;
38678
+ }
38679
+ this.masterThemePaths.set(file.name, themePath);
38680
+ const parsed = await this.parseThemePart(themePath);
38681
+ if (!parsed) {
38682
+ continue;
38683
+ }
38684
+ this.masterThemeColorMaps.set(file.name, parsed.colorMap);
38685
+ this.masterThemeFontMaps.set(file.name, parsed.fontMap);
38686
+ if (parsed.formatScheme) {
38687
+ this.masterThemeFormatSchemes.set(file.name, parsed.formatScheme);
38688
+ }
38689
+ } catch (error) {
38690
+ console.warn(`Failed to load per-master theme for ${file.name}:`, error);
38691
+ }
38692
+ }
38693
+ }
38694
+ async loadThemeData() {
38695
+ const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
38696
+ if (!themeFiles || themeFiles.length === 0) {
38697
+ return;
38698
+ }
38699
+ const preferredThemePath = await this.resolvePrimaryThemePath();
38700
+ const themeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) ?? themeFiles[0] : themeFiles[0];
38701
+ const parsed = await this.parseThemePart(themeFile.name);
38702
+ if (parsed) {
38703
+ this.themeColorMap = parsed.colorMap;
38704
+ this.themeFontMap = parsed.fontMap;
38705
+ if (parsed.formatScheme) {
38706
+ this.themeFormatScheme = parsed.formatScheme;
38707
+ }
38708
+ }
38709
+ await this.applySlideMasterColorMap(this.getDefaultSchemeColorMap());
38710
+ await this.loadPerMasterThemes();
38711
+ this.globalThemeColorMapSnapshot = { ...this.themeColorMap };
38712
+ this.globalThemeFontMapSnapshot = { ...this.themeFontMap };
38713
+ this.globalThemeFormatSchemeSnapshot = this.themeFormatScheme;
36409
38714
  }
36410
38715
  };
36411
38716
 
36412
38717
  // src/core/core/runtime/PptxHandlerRuntimeThemeProcessing.ts
36413
- var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
38718
+ var PptxHandlerRuntime61 = class extends PptxHandlerRuntime60 {
36414
38719
  // ---------------------------------------------------------------------------
36415
38720
  // Theme editing — update colour scheme, font scheme, and name in the zip
36416
38721
  // ---------------------------------------------------------------------------
@@ -36560,7 +38865,7 @@ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
36560
38865
  };
36561
38866
 
36562
38867
  // src/core/core/runtime/PptxHandlerRuntimeComments.ts
36563
- var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime60 {
38868
+ var PptxHandlerRuntime62 = class _PptxHandlerRuntime extends PptxHandlerRuntime61 {
36564
38869
  /**
36565
38870
  * Parse modern threaded comments (PowerPoint 2019+ / Office 365).
36566
38871
  * Modern comments use `p188:cmLst`/`p15:cmLst` roots within
@@ -36793,7 +39098,7 @@ var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
36793
39098
  };
36794
39099
 
36795
39100
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtXmlUtils.ts
36796
- var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
39101
+ var PptxHandlerRuntime63 = class extends PptxHandlerRuntime62 {
36797
39102
  async readXmlPartByRelationshipId(slidePath, relationshipId) {
36798
39103
  const normalizedRelationshipId = String(relationshipId || "").trim();
36799
39104
  if (normalizedRelationshipId.length === 0) {
@@ -36948,7 +39253,7 @@ var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
36948
39253
  };
36949
39254
 
36950
39255
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtParsing.ts
36951
- var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime62 {
39256
+ var PptxHandlerRuntime64 = class _PptxHandlerRuntime extends PptxHandlerRuntime63 {
36952
39257
  /**
36953
39258
  * Parse quick style from `ppt/diagrams/quickStyles*.xml`.
36954
39259
  */
@@ -37107,7 +39412,7 @@ var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
37107
39412
  };
37108
39413
 
37109
39414
  // src/core/core/runtime/PptxHandlerRuntimeSmartArt.ts
37110
- var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
39415
+ var PptxHandlerRuntime65 = class _PptxHandlerRuntime extends PptxHandlerRuntime64 {
37111
39416
  async getSmartArtDataForGraphicFrame(slidePath, graphicFrame) {
37112
39417
  const graphicData = this.xmlLookupService.getChildByLocalName(
37113
39418
  this.xmlLookupService.getChildByLocalName(graphicFrame, "graphic"),
@@ -37158,10 +39463,14 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37158
39463
  const layoutPart = layoutRelationshipId.length > 0 ? await this.readXmlPartByRelationshipId(slidePath, layoutRelationshipId) : void 0;
37159
39464
  const layoutType = layoutPart?.partPath?.split("/").pop()?.replace(/\.[^.]+$/, "") || void 0;
37160
39465
  const chrome = this.parseSmartArtChrome(dataModel);
37161
- const drawingRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37162
- const drawingShapes = await this.parseSmartArtDrawingShapes(slidePath, drawingRelationshipId);
37163
39466
  const colorsRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
37164
39467
  const colorTransform = await this.parseSmartArtColorTransform(slidePath, colorsRelationshipId);
39468
+ const drawingResolution = await this.resolveSmartArtDrawingPart(
39469
+ slidePath,
39470
+ diagramDataRelationshipId
39471
+ );
39472
+ const drawingShapes = drawingResolution ? await this.parseSmartArtDrawingShapesFromPath(drawingResolution.path) : [];
39473
+ const drawingRelationshipId = drawingResolution?.relId;
37165
39474
  const styleRelationshipId = String(relationshipIds["@_r:qs"] || "").trim();
37166
39475
  const quickStyle = await this.parseSmartArtQuickStyle(slidePath, styleRelationshipId);
37167
39476
  return {
@@ -37173,11 +39482,87 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37173
39482
  colorTransform,
37174
39483
  quickStyle,
37175
39484
  dataRelId: diagramDataRelationshipId,
37176
- drawingRelId: drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
39485
+ drawingRelId: drawingRelationshipId && drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
37177
39486
  colorsRelId: colorsRelationshipId.length > 0 ? colorsRelationshipId : void 0,
37178
39487
  styleRelId: styleRelationshipId.length > 0 ? styleRelationshipId : void 0
37179
39488
  };
37180
39489
  }
39490
+ /**
39491
+ * Resolve the SmartArt drawing-shapes part path + relationship id.
39492
+ *
39493
+ * Strategy:
39494
+ * 1. Locate the data-model part's rels file
39495
+ * (`ppt/diagrams/_rels/data*.xml.rels`) via `slideRelsMap`.
39496
+ * 2. Find a relationship whose `Type` matches the `…/diagramDrawing`
39497
+ * URI. PowerPoint emits this for any deck that has had its
39498
+ * SmartArt rendered to drawing shapes.
39499
+ * 3. Return the matched part path (so the caller can load it
39500
+ * directly) and the relationship id (for round-trip preservation).
39501
+ */
39502
+ async resolveSmartArtDrawingPart(slidePath, diagramDataRelationshipId) {
39503
+ if (diagramDataRelationshipId.length === 0) {
39504
+ return void 0;
39505
+ }
39506
+ const slideRels = this.slideRelsMap.get(slidePath);
39507
+ const dataTarget = slideRels?.get(diagramDataRelationshipId);
39508
+ if (!dataTarget) {
39509
+ return void 0;
39510
+ }
39511
+ const dataPath = this.resolveImagePath(slidePath, dataTarget);
39512
+ const dataDir = dataPath.replace(/\/[^/]+$/, "");
39513
+ const dataFile = dataPath.split("/").pop() ?? "";
39514
+ const dataRelsPath = `${dataDir}/_rels/${dataFile}.rels`;
39515
+ const relsXml = await this.zip.file(dataRelsPath)?.async("string");
39516
+ if (!relsXml) {
39517
+ return void 0;
39518
+ }
39519
+ try {
39520
+ const parsed = this.parser.parse(relsXml);
39521
+ const relsRoot = parsed["Relationships"];
39522
+ if (!relsRoot) {
39523
+ return void 0;
39524
+ }
39525
+ const rels = this.ensureArray(relsRoot["Relationship"]);
39526
+ const drawingRel = rels.find(
39527
+ (rel) => String(rel?.["@_Type"] || "").endsWith("/diagramDrawing")
39528
+ );
39529
+ const id = String(drawingRel?.["@_Id"] || "").trim();
39530
+ const target = String(drawingRel?.["@_Target"] || "").trim();
39531
+ if (id.length === 0 || target.length === 0) {
39532
+ return void 0;
39533
+ }
39534
+ const drawingPath = this.resolveImagePath(dataPath, target);
39535
+ return { relId: id, path: drawingPath };
39536
+ } catch {
39537
+ return void 0;
39538
+ }
39539
+ }
39540
+ /**
39541
+ * Parse SmartArt drawing shapes given an absolute part path.
39542
+ *
39543
+ * Wraps `parseSmartArtDrawingShapes` (which expects a slide-relative
39544
+ * relationship id) with a path-based lookup so the resolution layer
39545
+ * can pull the part from anywhere in the package.
39546
+ */
39547
+ async parseSmartArtDrawingShapesFromPath(drawingPath) {
39548
+ const xmlString = await this.zip.file(drawingPath)?.async("string");
39549
+ if (!xmlString) {
39550
+ return [];
39551
+ }
39552
+ try {
39553
+ const xml = this.parser.parse(xmlString);
39554
+ const drawing = this.xmlLookupService.getChildByLocalName(xml, "drawing");
39555
+ const spTree = this.xmlLookupService.getChildByLocalName(drawing || xml, "spTree");
39556
+ if (!spTree) {
39557
+ return [];
39558
+ }
39559
+ const shapes = this.xmlLookupService.getChildrenArrayByLocalName(spTree, "sp");
39560
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
39561
+ return shapes.map((sp, index) => this.parseDrawingShape(sp, index, emuPerPx)).filter((entry) => entry !== null);
39562
+ } catch {
39563
+ return [];
39564
+ }
39565
+ }
37181
39566
  parseSmartArtConnections(dataModel) {
37182
39567
  const connectionList = this.xmlLookupService.getChildByLocalName(dataModel, "cxnLst");
37183
39568
  const rawConnections = this.xmlLookupService.getChildrenArrayByLocalName(connectionList, "cxn");
@@ -37208,7 +39593,7 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
37208
39593
  };
37209
39594
 
37210
39595
  // src/core/core/runtime/PptxHandlerRuntimeChartDetection.ts
37211
- var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
39596
+ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37212
39597
  detectChartType(plotArea) {
37213
39598
  if (!plotArea) {
37214
39599
  return "unknown";
@@ -37317,7 +39702,7 @@ var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
37317
39702
  };
37318
39703
 
37319
39704
  // src/core/core/runtime/PptxHandlerRuntimeChartParsingHelpers.ts
37320
- var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
39705
+ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37321
39706
  /**
37322
39707
  * Parse `c:plotVisOnly` from the chart root element.
37323
39708
  *
@@ -37378,7 +39763,7 @@ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
37378
39763
  };
37379
39764
 
37380
39765
  // src/core/core/runtime/PptxHandlerRuntimeChartExternalData.ts
37381
- var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
39766
+ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37382
39767
  /**
37383
39768
  * Parse `c:externalData` from the chart's `c:chartSpace` and resolve
37384
39769
  * the external relationship target from the chart part's .rels file.
@@ -37494,7 +39879,7 @@ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
37494
39879
  };
37495
39880
 
37496
39881
  // src/core/core/runtime/PptxHandlerRuntimeChartColorStyle.ts
37497
- var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
39882
+ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37498
39883
  /**
37499
39884
  * Parse the Office 2013+ chart color style part (`chartColorStyle*.xml`)
37500
39885
  * referenced from the chart's relationships.
@@ -37601,7 +39986,7 @@ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
37601
39986
  };
37602
39987
 
37603
39988
  // src/core/core/runtime/PptxHandlerRuntimeChartParsing.ts
37604
- var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
39989
+ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
37605
39990
  /**
37606
39991
  * Parse chart data from a graphic frame element on a slide.
37607
39992
  *
@@ -37848,7 +40233,7 @@ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
37848
40233
  };
37849
40234
 
37850
40235
  // src/core/core/runtime/PptxHandlerRuntimePresentationStructure.ts
37851
- var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
40236
+ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
37852
40237
  parseEditorAnimations(slideXml2) {
37853
40238
  return this.editorAnimationService.parseEditorAnimations(slideXml2);
37854
40239
  }
@@ -38093,7 +40478,7 @@ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
38093
40478
  };
38094
40479
 
38095
40480
  // src/core/core/runtime/PptxHandlerRuntimeEmbeddedFonts.ts
38096
- var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
40481
+ var PptxHandlerRuntime72 = class extends PptxHandlerRuntime71 {
38097
40482
  async getEmbeddedFonts() {
38098
40483
  const embeddedFontEntries = this.ensureArray(
38099
40484
  this.presentationData?.["p:presentation"]?.["p:embeddedFontLst"]?.["p:embeddedFont"]
@@ -38265,7 +40650,7 @@ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
38265
40650
  };
38266
40651
 
38267
40652
  // src/core/core/runtime/PptxHandlerRuntimeLoadSession.ts
38268
- var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime71 {
40653
+ var PptxHandlerRuntime73 = class _PptxHandlerRuntime extends PptxHandlerRuntime72 {
38269
40654
  isZipContainer(data) {
38270
40655
  const bytes = new Uint8Array(data);
38271
40656
  if (bytes.byteLength < 4) {
@@ -38308,6 +40693,23 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38308
40693
  this.imageDataCache.clear();
38309
40694
  this.themeColorMap = {};
38310
40695
  this.themeFontMap = {};
40696
+ this.masterClrMaps.clear();
40697
+ this.masterThemeColorMaps.clear();
40698
+ this.masterThemeFontMaps.clear();
40699
+ this.masterThemeFormatSchemes.clear();
40700
+ this.masterThemePaths.clear();
40701
+ this.masterThemeMajorFontScripts.clear();
40702
+ this.masterThemeMinorFontScripts.clear();
40703
+ this.masterThemeNames.clear();
40704
+ this.masterThemeFontSchemeNames.clear();
40705
+ this.masterThemeColorSchemeNames.clear();
40706
+ this.originalThemeXmlByPath.clear();
40707
+ this.dirtyThemePaths.clear();
40708
+ this.masterThemeObjectDefaults.clear();
40709
+ this.masterThemeExtraClrSchemeLst.clear();
40710
+ this.masterThemeCustClrLst.clear();
40711
+ this.masterThemeExtLst.clear();
40712
+ this.currentMasterClrMap = null;
38311
40713
  this.presentationDefaultTextStyle = void 0;
38312
40714
  this.commentAuthorMap.clear();
38313
40715
  this.commentAuthorDetails.clear();
@@ -38462,6 +40864,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38462
40864
  setCurrentSlideClrMapOverride: (override) => {
38463
40865
  this.currentSlideClrMapOverride = override;
38464
40866
  },
40867
+ setActiveMasterForSlide: (slidePath) => this.setActiveMasterForSlide(slidePath),
38465
40868
  findLayoutPathForSlide: (slidePath) => this.findLayoutPathForSlide(slidePath),
38466
40869
  loadThemeOverride: (partBasePath) => this.loadThemeOverride(partBasePath),
38467
40870
  applyThemeOverrideState: (override) => this.applyThemeOverrideState(override),
@@ -38492,7 +40895,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38492
40895
  };
38493
40896
 
38494
40897
  // src/core/core/runtime/PptxHandlerRuntimeLoadPipeline.ts
38495
- var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
40898
+ var PptxHandlerRuntime74 = class extends PptxHandlerRuntime73 {
38496
40899
  async buildLoadData(presentationState, slidesWithWarnings) {
38497
40900
  const headerFooter = this.extractHeaderFooter();
38498
40901
  const presentationProperties = await this.parsePresentationProperties();
@@ -38504,6 +40907,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38504
40907
  const notesMaster = await this.parseNotesMaster();
38505
40908
  const handoutMaster = await this.parseHandoutMaster();
38506
40909
  const slideMasters = await this.parseSlideMasters();
40910
+ await this.enrichSlideMastersWithTxStyles(slideMasters);
38507
40911
  const tags = await this.parseTags();
38508
40912
  const customProperties = await this.parseCustomProperties();
38509
40913
  const coreProperties = await this.parseCoreProperties();
@@ -38838,7 +41242,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
38838
41242
  };
38839
41243
 
38840
41244
  // src/core/core/runtime/PptxHandlerRuntimeImplementation.ts
38841
- var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime73 {
41245
+ var PptxHandlerRuntime75 = class _PptxHandlerRuntime extends PptxHandlerRuntime74 {
38842
41246
  constructor(dependencyFactory = new PptxRuntimeDependencyFactory()) {
38843
41247
  super();
38844
41248
  this.dependencyFactory = dependencyFactory;
@@ -38886,6 +41290,9 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38886
41290
  extractGradientPathType: (gradFill) => this.colorStyleCodec.extractGradientPathType(gradFill),
38887
41291
  extractGradientFocalPoint: (gradFill) => this.colorStyleCodec.extractGradientFocalPoint(gradFill),
38888
41292
  extractGradientFillToRect: (gradFill) => this.colorStyleCodec.extractGradientFillToRect(gradFill),
41293
+ extractGradientFlip: (gradFill) => this.colorStyleCodec.extractGradientFlip(gradFill),
41294
+ extractGradientRotWithShape: (gradFill) => this.colorStyleCodec.extractGradientRotWithShape(gradFill),
41295
+ extractGradientScaled: (gradFill) => this.colorStyleCodec.extractGradientScaled(gradFill),
38889
41296
  normalizeStrokeDashType: (value) => this.normalizeStrokeDashType(value),
38890
41297
  normalizeConnectorArrowType: (value) => this.normalizeConnectorArrowType(value),
38891
41298
  ensureArray: (value) => this.ensureArray(value),
@@ -38959,11 +41366,11 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
38959
41366
  };
38960
41367
 
38961
41368
  // src/core/core/PptxHandlerRuntime.ts
38962
- var PptxHandlerRuntime75 = class extends PptxHandlerRuntime74 {
41369
+ var PptxHandlerRuntime76 = class extends PptxHandlerRuntime75 {
38963
41370
  };
38964
41371
 
38965
41372
  // src/core/core/PptxHandlerRuntimeFactory.ts
38966
- var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime75();
41373
+ var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime76();
38967
41374
 
38968
41375
  // src/core/PptxHandlerCore.ts
38969
41376
  var PptxHandlerCore = class {