pptx-react-viewer 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/index.js +587 -288
  2. package/dist/index.mjs +587 -288
  3. package/dist/pptx-viewer.css +1 -1
  4. package/dist/viewer/index.js +587 -288
  5. package/dist/viewer/index.mjs +587 -288
  6. package/node_modules/emf-converter/package.json +2 -2
  7. package/node_modules/mtx-decompressor/package.json +2 -2
  8. package/node_modules/pptx-viewer-core/dist/{SvgExporter--H1PDfAY.d.ts → SvgExporter-0TxiiorD.d.ts} +1 -1
  9. package/node_modules/pptx-viewer-core/dist/{SvgExporter-Dq_2eV_r.d.mts → SvgExporter-BQ4KbRO9.d.mts} +1 -1
  10. package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +2 -2
  11. package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +2 -2
  12. package/node_modules/pptx-viewer-core/dist/cli/index.js +2875 -321
  13. package/node_modules/pptx-viewer-core/dist/cli/index.mjs +2875 -321
  14. package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +3 -3
  15. package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +3 -3
  16. package/node_modules/pptx-viewer-core/dist/index.d.mts +713 -44
  17. package/node_modules/pptx-viewer-core/dist/index.d.ts +713 -44
  18. package/node_modules/pptx-viewer-core/dist/index.js +2958 -390
  19. package/node_modules/pptx-viewer-core/dist/index.mjs +2949 -390
  20. package/node_modules/pptx-viewer-core/dist/{presentation-BozkirFp.d.mts → presentation-ArhfImJ5.d.mts} +225 -8
  21. package/node_modules/pptx-viewer-core/dist/{presentation-BozkirFp.d.ts → presentation-ArhfImJ5.d.ts} +225 -8
  22. package/node_modules/pptx-viewer-core/dist/{text-operations-B2JbPA5H.d.mts → text-operations-CLj-sJyk.d.mts} +1 -1
  23. package/node_modules/pptx-viewer-core/dist/{text-operations-zwF6i4eH.d.ts → text-operations-rhJV-A_W.d.ts} +1 -1
  24. package/node_modules/pptx-viewer-core/package.json +5 -5
  25. package/package.json +20 -20
@@ -682,12 +682,25 @@ function svgToCustomGeometryPaths(pathData, width, height) {
682
682
  function pointToXml(pt2) {
683
683
  return { "@_x": String(Math.round(pt2.x)), "@_y": String(Math.round(pt2.y)) };
684
684
  }
685
- function customGeometryPathsToXml(paths) {
685
+ function customGeometryPathsToXml(paths, rawData) {
686
686
  const xmlPaths = paths.map((path) => {
687
687
  const pathXml = {
688
688
  "@_w": String(Math.round(path.width)),
689
689
  "@_h": String(Math.round(path.height))
690
690
  };
691
+ if (path.fillMode) {
692
+ pathXml["@_fill"] = path.fillMode;
693
+ }
694
+ if (path.stroke === false) {
695
+ pathXml["@_stroke"] = "0";
696
+ } else if (path.stroke === true) {
697
+ pathXml["@_stroke"] = "1";
698
+ }
699
+ if (path.extrusionOk === true) {
700
+ pathXml["@_extrusionOk"] = "1";
701
+ } else if (path.extrusionOk === false) {
702
+ pathXml["@_extrusionOk"] = "0";
703
+ }
691
704
  const moveToList = [];
692
705
  const lnToList = [];
693
706
  const cubicBezToList = [];
@@ -745,12 +758,12 @@ function customGeometryPathsToXml(paths) {
745
758
  }
746
759
  return pathXml;
747
760
  });
748
- return {
761
+ const result = {
749
762
  "a:avLst": {},
750
- "a:gdLst": {},
751
- "a:ahLst": {},
752
- "a:cxnLst": {},
753
- "a:rect": {
763
+ "a:gdLst": rawData?.gdLstXml ?? {},
764
+ "a:ahLst": rawData?.ahLstXml ?? {},
765
+ "a:cxnLst": rawData?.cxnLstXml ?? {},
766
+ "a:rect": rawData?.rectXml ?? {
754
767
  "@_l": "l",
755
768
  "@_t": "t",
756
769
  "@_r": "r",
@@ -760,6 +773,7 @@ function customGeometryPathsToXml(paths) {
760
773
  "a:path": xmlPaths.length === 1 ? xmlPaths[0] : xmlPaths
761
774
  }
762
775
  };
776
+ return result;
763
777
  }
764
778
 
765
779
  // src/core/builders/sdk/ElementFactory.ts
@@ -1447,7 +1461,11 @@ function isoNow() {
1447
1461
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z");
1448
1462
  }
1449
1463
  var STANDARD_LAYOUTS = [
1450
- { name: "Title Slide", type: "ctrTitle" },
1464
+ // `type` is ST_SlideLayoutType per ECMA-376 §19.7.15. The Title Slide
1465
+ // layout uses `title`; `ctrTitle` is a placeholder type (ST_PlaceholderType)
1466
+ // and is not valid here — PowerPoint's OPC loader rejects the package
1467
+ // with ERROR_FILE_CORRUPT (0x80070570) when it sees it.
1468
+ { name: "Title Slide", type: "title" },
1451
1469
  { name: "Title and Content", type: "obj" },
1452
1470
  { name: "Section Header", type: "secHead" },
1453
1471
  { name: "Two Content", type: "twoObj" },
@@ -1481,7 +1499,7 @@ ${slideOverrides}
1481
1499
  <Override PartName="/ppt/viewProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml"/>
1482
1500
  <Override PartName="/ppt/tableStyles.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"/>
1483
1501
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
1484
- <Override PartName="/docProps/app.xml" ContentType="application/vnd.ms-officedocument.extended-properties+xml"/>
1502
+ <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
1485
1503
  </Types>`;
1486
1504
  }
1487
1505
  function rootRelsXml() {
@@ -1505,6 +1523,9 @@ ${Array.from(
1505
1523
  <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
1506
1524
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
1507
1525
  xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
1526
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
1527
+ xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main"
1528
+ xmlns:p15="http://schemas.microsoft.com/office/powerpoint/2012/main"
1508
1529
  saveSubsetFonts="1">
1509
1530
  <p:sldMasterIdLst>
1510
1531
  <p:sldMasterId id="2147483648" r:id="rId1"/>
@@ -2807,9 +2828,40 @@ var PptxPresentationSlidesReconciler = class {
2807
2828
  "@_r:id": slide.rId
2808
2829
  };
2809
2830
  });
2810
- presentation["p:sldIdLst"] = slideIdList;
2811
- input.presentationData["p:presentation"] = presentation;
2831
+ input.presentationData["p:presentation"] = this.insertSlideIdListInOrder(
2832
+ presentation,
2833
+ slideIdList
2834
+ );
2835
+ }
2836
+ }
2837
+ /**
2838
+ * Return a new presentation XML object whose `p:sldIdLst` child sits in the
2839
+ * schema-mandated position (after `sldMasterIdLst` / `notesMasterIdLst` /
2840
+ * `handoutMasterIdLst`, before `sldSz`). Non-sldIdLst keys keep their
2841
+ * relative order; attribute keys (`@_...`) stay at the front.
2842
+ */
2843
+ insertSlideIdListInOrder(presentation, slideIdList) {
2844
+ const before = /* @__PURE__ */ new Set([
2845
+ "p:sldMasterIdLst",
2846
+ "p:notesMasterIdLst",
2847
+ "p:handoutMasterIdLst"
2848
+ ]);
2849
+ const rebuilt = {};
2850
+ let inserted = false;
2851
+ for (const key of Object.keys(presentation)) {
2852
+ if (key === "p:sldIdLst") {
2853
+ continue;
2854
+ }
2855
+ if (!inserted && !key.startsWith("@_") && !before.has(key)) {
2856
+ rebuilt["p:sldIdLst"] = slideIdList;
2857
+ inserted = true;
2858
+ }
2859
+ rebuilt[key] = presentation[key];
2812
2860
  }
2861
+ if (!inserted) {
2862
+ rebuilt["p:sldIdLst"] = slideIdList;
2863
+ }
2864
+ return rebuilt;
2813
2865
  }
2814
2866
  async attachNewSlide(init) {
2815
2867
  const sourceSlidePath = init.input.findSourceSlidePath(init.slide.sourceSlideId);
@@ -2895,6 +2947,22 @@ var PptxPresentationSlidesReconciler = class {
2895
2947
  };
2896
2948
 
2897
2949
  // src/core/core/builders/PptxSlideRelationshipRegistry.ts
2950
+ function isExternalTarget(target) {
2951
+ const normalized = target.trim();
2952
+ if (normalized.length === 0) {
2953
+ return false;
2954
+ }
2955
+ const colonIdx = normalized.indexOf(":");
2956
+ if (colonIdx <= 0) {
2957
+ return false;
2958
+ }
2959
+ const slashIdx = normalized.indexOf("/");
2960
+ if (slashIdx !== -1 && slashIdx < colonIdx) {
2961
+ return false;
2962
+ }
2963
+ const scheme = normalized.slice(0, colonIdx);
2964
+ return /^[A-Za-z][A-Za-z0-9+\-.]*$/.test(scheme);
2965
+ }
2898
2966
  var PptxSlideRelationshipRegistry = class {
2899
2967
  relationships;
2900
2968
  usedRelationshipIds = /* @__PURE__ */ new Set();
@@ -2965,7 +3033,7 @@ var PptxSlideRelationshipRegistry = class {
2965
3033
  return existingRelationshipId;
2966
3034
  }
2967
3035
  const relationshipId = this.nextRelationshipId();
2968
- const targetMode = /^(https?:|mailto:|ftp:|file:)/i.test(normalizedTarget) ? "External" : void 0;
3036
+ const targetMode = isExternalTarget(normalizedTarget) ? "External" : void 0;
2969
3037
  this.upsertRelationship(
2970
3038
  relationshipId,
2971
3039
  this.hyperlinkRelationshipType,
@@ -3215,6 +3283,82 @@ var PptxSlideNotesPartUpdater = class {
3215
3283
  }
3216
3284
  };
3217
3285
 
3286
+ // src/core/utils/xml-reorder.ts
3287
+ function reorderObjectKeys(obj, schemaOrder) {
3288
+ const result = {};
3289
+ const consumed = /* @__PURE__ */ new Set();
3290
+ for (const key of schemaOrder) {
3291
+ if (Object.hasOwn(obj, key)) {
3292
+ const value = obj[key];
3293
+ if (value !== void 0) {
3294
+ result[key] = value;
3295
+ }
3296
+ consumed.add(key);
3297
+ }
3298
+ }
3299
+ for (const key of Object.keys(obj)) {
3300
+ if (consumed.has(key)) {
3301
+ continue;
3302
+ }
3303
+ const value = obj[key];
3304
+ if (value !== void 0) {
3305
+ result[key] = value;
3306
+ }
3307
+ }
3308
+ return result;
3309
+ }
3310
+ var EFFECT_LST_ORDER = [
3311
+ "a:blur",
3312
+ "a:fillOverlay",
3313
+ "a:glow",
3314
+ "a:innerShdw",
3315
+ "a:outerShdw",
3316
+ "a:prstShdw",
3317
+ "a:reflection",
3318
+ "a:softEdge"
3319
+ ];
3320
+ var SP_PR_ORDER = [
3321
+ "a:xfrm",
3322
+ "a:custGeom",
3323
+ "a:prstGeom",
3324
+ "a:noFill",
3325
+ "a:solidFill",
3326
+ "a:gradFill",
3327
+ "a:blipFill",
3328
+ "a:pattFill",
3329
+ "a:grpFill",
3330
+ "a:ln",
3331
+ "a:effectLst",
3332
+ "a:effectDag",
3333
+ "a:scene3d",
3334
+ "a:sp3d",
3335
+ "a:extLst"
3336
+ ];
3337
+ var TC_PR_BORDERS_ORDER = [
3338
+ "a:lnL",
3339
+ "a:lnR",
3340
+ "a:lnT",
3341
+ "a:lnB",
3342
+ "a:lnTlToBr",
3343
+ "a:lnBlToTr",
3344
+ "a:cell3D",
3345
+ "a:noFill",
3346
+ "a:solidFill",
3347
+ "a:gradFill",
3348
+ "a:blipFill",
3349
+ "a:pattFill",
3350
+ "a:grpFill",
3351
+ "a:headers",
3352
+ "a:extLst"
3353
+ ];
3354
+ var BLIP_FILL_ORDER = ["a:blip", "a:srcRect", "a:tile", "a:stretch"];
3355
+ var SHAPE_STYLE_ORDER = [
3356
+ "a:lnRef",
3357
+ "a:fillRef",
3358
+ "a:effectRef",
3359
+ "a:fontRef"
3360
+ ];
3361
+
3218
3362
  // src/core/core/builders/PptxSlideBackgroundBuilder.ts
3219
3363
  var PptxSlideBackgroundBuilder = class {
3220
3364
  applyBackground(init) {
@@ -3250,10 +3394,13 @@ var PptxSlideBackgroundBuilder = class {
3250
3394
  init.slideImageRelationshipType,
3251
3395
  relativeBackgroundImagePath
3252
3396
  );
3253
- backgroundProperties["a:blipFill"] = {
3254
- "a:blip": { "@_r:embed": backgroundRelationshipId },
3255
- "a:stretch": { "a:fillRect": {} }
3256
- };
3397
+ backgroundProperties["a:blipFill"] = reorderObjectKeys(
3398
+ {
3399
+ "a:blip": { "@_r:embed": backgroundRelationshipId },
3400
+ "a:stretch": { "a:fillRect": {} }
3401
+ },
3402
+ BLIP_FILL_ORDER
3403
+ );
3257
3404
  }
3258
3405
  } else if (hasBackgroundColor && init.slide.backgroundColor) {
3259
3406
  backgroundProperties["a:solidFill"] = {
@@ -4075,6 +4222,45 @@ var PptxGradientStyleCodec = class {
4075
4222
  b: Number.isFinite(b) ? this.context.clampUnitInterval(b / 1e5) : 0
4076
4223
  };
4077
4224
  }
4225
+ extractGradientFlip(gradFill) {
4226
+ const flipRaw = String(gradFill["@_flip"] || "").trim().toLowerCase();
4227
+ if (flipRaw === "x" || flipRaw === "y" || flipRaw === "xy" || flipRaw === "none") {
4228
+ return flipRaw;
4229
+ }
4230
+ return void 0;
4231
+ }
4232
+ extractGradientRotWithShape(gradFill) {
4233
+ const rot = gradFill["@_rotWithShape"];
4234
+ if (rot === void 0 || rot === null) {
4235
+ return void 0;
4236
+ }
4237
+ const token = String(rot).trim().toLowerCase();
4238
+ if (token === "1" || token === "true") {
4239
+ return true;
4240
+ }
4241
+ if (token === "0" || token === "false") {
4242
+ return false;
4243
+ }
4244
+ return void 0;
4245
+ }
4246
+ extractGradientScaled(gradFill) {
4247
+ const lin = gradFill["a:lin"];
4248
+ if (!lin) {
4249
+ return void 0;
4250
+ }
4251
+ const scaled = lin["@_scaled"];
4252
+ if (scaled === void 0 || scaled === null) {
4253
+ return void 0;
4254
+ }
4255
+ const token = String(scaled).trim().toLowerCase();
4256
+ if (token === "1" || token === "true") {
4257
+ return true;
4258
+ }
4259
+ if (token === "0" || token === "false") {
4260
+ return false;
4261
+ }
4262
+ return void 0;
4263
+ }
4078
4264
  extractGradientAngle(gradFill) {
4079
4265
  const angleRaw = Number.parseInt(
4080
4266
  String(gradFill["a:lin"]?.["@_ang"] || ""),
@@ -4161,11 +4347,14 @@ var PptxGradientStyleCodec = class {
4161
4347
  return void 0;
4162
4348
  }
4163
4349
  const gradientType = shapeStyle.fillGradientType || "linear";
4164
- const gradientXml = {
4165
- "a:gsLst": {
4166
- "a:gs": stops
4167
- }
4168
- };
4350
+ const gradientXml = {};
4351
+ if (shapeStyle.fillGradientFlip && shapeStyle.fillGradientFlip !== "none") {
4352
+ gradientXml["@_flip"] = shapeStyle.fillGradientFlip;
4353
+ }
4354
+ if (shapeStyle.fillGradientRotWithShape !== void 0) {
4355
+ gradientXml["@_rotWithShape"] = shapeStyle.fillGradientRotWithShape ? "1" : "0";
4356
+ }
4357
+ gradientXml["a:gsLst"] = { "a:gs": stops };
4169
4358
  if (gradientType === "radial") {
4170
4359
  const pathType = shapeStyle.fillGradientPathType || "circle";
4171
4360
  const pathXml = {
@@ -4195,10 +4384,15 @@ var PptxGradientStyleCodec = class {
4195
4384
  gradientXml["a:path"] = pathXml;
4196
4385
  } else {
4197
4386
  const normalizedAngle = typeof shapeStyle.fillGradientAngle === "number" && Number.isFinite(shapeStyle.fillGradientAngle) ? shapeStyle.fillGradientAngle : 90;
4198
- gradientXml["a:lin"] = {
4199
- "@_ang": String(Math.round(normalizedAngle * 6e4)),
4200
- "@_scaled": "1"
4387
+ const linNode = {
4388
+ "@_ang": String(Math.round(normalizedAngle * 6e4))
4201
4389
  };
4390
+ if (shapeStyle.fillGradientScaled !== void 0) {
4391
+ linNode["@_scaled"] = shapeStyle.fillGradientScaled ? "1" : "0";
4392
+ } else {
4393
+ linNode["@_scaled"] = "1";
4394
+ }
4395
+ gradientXml["a:lin"] = linNode;
4202
4396
  }
4203
4397
  return gradientXml;
4204
4398
  }
@@ -4595,6 +4789,30 @@ var PRESET_SHADOW_OPACITY_MAP = {
4595
4789
  };
4596
4790
 
4597
4791
  // src/core/core/builders/PptxShapeEffectStyleExtractor.ts
4792
+ var VALID_ALIGNMENTS = /* @__PURE__ */ new Set(["tl", "t", "tr", "l", "ctr", "r", "bl", "b", "br"]);
4793
+ function parseIntAttr(value) {
4794
+ if (value === void 0 || value === null || value === "") {
4795
+ return void 0;
4796
+ }
4797
+ const parsed = Number.parseInt(String(value), 10);
4798
+ return Number.isFinite(parsed) ? parsed : void 0;
4799
+ }
4800
+ function parseAlignmentAttr(value) {
4801
+ const v = String(value ?? "").trim();
4802
+ return VALID_ALIGNMENTS.has(v) ? v : void 0;
4803
+ }
4804
+ function parseBoolAttr(value) {
4805
+ if (typeof value === "boolean") {
4806
+ return value;
4807
+ }
4808
+ if (value === "1" || value === "true") {
4809
+ return true;
4810
+ }
4811
+ if (value === "0" || value === "false") {
4812
+ return false;
4813
+ }
4814
+ return void 0;
4815
+ }
4598
4816
  var PptxShapeEffectStyleExtractor = class {
4599
4817
  context;
4600
4818
  constructor(context) {
@@ -4618,7 +4836,12 @@ var PptxShapeEffectStyleExtractor = class {
4618
4836
  const shadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
4619
4837
  const shadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
4620
4838
  const rotateWithShape = outerShadow["@_rotWithShape"];
4621
- const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : void 0;
4839
+ const shadowRotateWithShape = typeof rotateWithShape === "boolean" ? rotateWithShape : rotateWithShape === "1" || rotateWithShape === "true" ? true : rotateWithShape === "0" || rotateWithShape === "false" ? false : void 0;
4840
+ const shadowScaleX = parseIntAttr(outerShadow["@_sx"]);
4841
+ const shadowScaleY = parseIntAttr(outerShadow["@_sy"]);
4842
+ const shadowSkewX = parseIntAttr(outerShadow["@_kx"]);
4843
+ const shadowSkewY = parseIntAttr(outerShadow["@_ky"]);
4844
+ const shadowAlignment = parseAlignmentAttr(outerShadow["@_algn"]);
4622
4845
  return {
4623
4846
  shadowColor,
4624
4847
  shadowOpacity,
@@ -4627,7 +4850,12 @@ var PptxShapeEffectStyleExtractor = class {
4627
4850
  shadowOffsetY,
4628
4851
  shadowAngle: directionDegrees,
4629
4852
  shadowDistance: distance,
4630
- shadowRotateWithShape
4853
+ shadowRotateWithShape,
4854
+ shadowScaleX,
4855
+ shadowScaleY,
4856
+ shadowSkewX,
4857
+ shadowSkewY,
4858
+ shadowAlignment
4631
4859
  };
4632
4860
  }
4633
4861
  extractPresetShadowStyle(effectLstParent) {
@@ -4673,12 +4901,14 @@ var PptxShapeEffectStyleExtractor = class {
4673
4901
  const directionRadians = directionDegrees * Math.PI / 180;
4674
4902
  const innerShadowOffsetX = distance !== void 0 ? Math.round(Math.cos(directionRadians) * distance * 100) / 100 : void 0;
4675
4903
  const innerShadowOffsetY = distance !== void 0 ? Math.round(Math.sin(directionRadians) * distance * 100) / 100 : void 0;
4904
+ const innerShadowRotateWithShape = parseBoolAttr(innerShadow["@_rotWithShape"]);
4676
4905
  return {
4677
4906
  innerShadowColor,
4678
4907
  innerShadowOpacity,
4679
4908
  innerShadowBlur,
4680
4909
  innerShadowOffsetX,
4681
- innerShadowOffsetY
4910
+ innerShadowOffsetY,
4911
+ innerShadowRotateWithShape
4682
4912
  };
4683
4913
  }
4684
4914
  extractGlowStyle(shapeProps) {
@@ -4727,6 +4957,16 @@ var PptxShapeEffectStyleExtractor = class {
4727
4957
  const reflectionRotation = Number.isFinite(rotationRaw) ? rotationRaw / 6e4 : void 0;
4728
4958
  const distanceRaw = Number.parseInt(String(reflectionNode["@_dist"] || ""), 10);
4729
4959
  const reflectionDistance = Number.isFinite(distanceRaw) && distanceRaw >= 0 ? distanceRaw / this.context.emuPerPx : void 0;
4960
+ const fadeDirRaw = parseIntAttr(reflectionNode["@_fadeDir"]);
4961
+ const reflectionFadeDirection = fadeDirRaw !== void 0 ? fadeDirRaw / 6e4 : void 0;
4962
+ const reflectionScaleX = parseIntAttr(reflectionNode["@_sx"]);
4963
+ const reflectionScaleY = parseIntAttr(reflectionNode["@_sy"]);
4964
+ const reflectionSkewX = parseIntAttr(reflectionNode["@_kx"]);
4965
+ const reflectionSkewY = parseIntAttr(reflectionNode["@_ky"]);
4966
+ const reflectionAlignment = parseAlignmentAttr(reflectionNode["@_algn"]);
4967
+ const reflectionRotateWithShape = parseBoolAttr(reflectionNode["@_rotWithShape"]);
4968
+ const stPosRaw = parseIntAttr(reflectionNode["@_stPos"]);
4969
+ const reflectionStartPosition = stPosRaw !== void 0 ? stPosRaw / 1e5 : void 0;
4730
4970
  return {
4731
4971
  reflectionBlurRadius,
4732
4972
  reflectionStartOpacity,
@@ -4734,7 +4974,15 @@ var PptxShapeEffectStyleExtractor = class {
4734
4974
  reflectionEndPosition,
4735
4975
  reflectionDirection,
4736
4976
  reflectionRotation,
4737
- reflectionDistance
4977
+ reflectionDistance,
4978
+ reflectionFadeDirection,
4979
+ reflectionScaleX,
4980
+ reflectionScaleY,
4981
+ reflectionSkewX,
4982
+ reflectionSkewY,
4983
+ reflectionAlignment,
4984
+ reflectionRotateWithShape,
4985
+ reflectionStartPosition
4738
4986
  };
4739
4987
  }
4740
4988
  extractBlurStyle(shapeProps) {
@@ -4786,11 +5034,56 @@ var PptxShapeEffectXmlBuilder = class {
4786
5034
  }
4787
5035
  }
4788
5036
  };
5037
+ if (typeof shapeStyle.shadowScaleX === "number") {
5038
+ xmlObj["@_sx"] = String(Math.round(shapeStyle.shadowScaleX));
5039
+ }
5040
+ if (typeof shapeStyle.shadowScaleY === "number") {
5041
+ xmlObj["@_sy"] = String(Math.round(shapeStyle.shadowScaleY));
5042
+ }
5043
+ if (typeof shapeStyle.shadowSkewX === "number") {
5044
+ xmlObj["@_kx"] = String(Math.round(shapeStyle.shadowSkewX));
5045
+ }
5046
+ if (typeof shapeStyle.shadowSkewY === "number") {
5047
+ xmlObj["@_ky"] = String(Math.round(shapeStyle.shadowSkewY));
5048
+ }
5049
+ if (shapeStyle.shadowAlignment) {
5050
+ xmlObj["@_algn"] = shapeStyle.shadowAlignment;
5051
+ }
4789
5052
  if (typeof shapeStyle.shadowRotateWithShape === "boolean") {
4790
5053
  xmlObj["@_rotWithShape"] = shapeStyle.shadowRotateWithShape ? "1" : "0";
4791
5054
  }
4792
5055
  return xmlObj;
4793
5056
  }
5057
+ buildPresetShadowXml(shapeStyle) {
5058
+ const preset = shapeStyle.presetShadowName;
5059
+ if (!preset || preset.length === 0) {
5060
+ return void 0;
5061
+ }
5062
+ const shadowColor = String(shapeStyle.shadowColor || "#000000").trim();
5063
+ const shadowOpacity = typeof shapeStyle.shadowOpacity === "number" && Number.isFinite(shapeStyle.shadowOpacity) ? this.context.clampUnitInterval(shapeStyle.shadowOpacity) : 0.5;
5064
+ let distance;
5065
+ let directionDegrees;
5066
+ if (typeof shapeStyle.shadowAngle === "number" && typeof shapeStyle.shadowDistance === "number") {
5067
+ directionDegrees = shapeStyle.shadowAngle;
5068
+ distance = shapeStyle.shadowDistance;
5069
+ } else {
5070
+ const ox = typeof shapeStyle.shadowOffsetX === "number" ? shapeStyle.shadowOffsetX : 0;
5071
+ const oy = typeof shapeStyle.shadowOffsetY === "number" ? shapeStyle.shadowOffsetY : 0;
5072
+ distance = Math.sqrt(ox * ox + oy * oy);
5073
+ directionDegrees = (Math.atan2(oy, ox) * 180 / Math.PI + 360) % 360;
5074
+ }
5075
+ return {
5076
+ "@_prst": preset,
5077
+ "@_dist": String(Math.round(distance * this.context.emuPerPx)),
5078
+ "@_dir": String(Math.round(directionDegrees * 6e4)),
5079
+ "a:srgbClr": {
5080
+ "@_val": shadowColor.replace("#", ""),
5081
+ "a:alpha": {
5082
+ "@_val": String(Math.round(shadowOpacity * 1e5))
5083
+ }
5084
+ }
5085
+ };
5086
+ }
4794
5087
  buildInnerShadowXml(shapeStyle) {
4795
5088
  const innerColor = String(shapeStyle.innerShadowColor || "").trim();
4796
5089
  if (innerColor.length === 0 || innerColor === "transparent") {
@@ -4802,7 +5095,7 @@ var PptxShapeEffectXmlBuilder = class {
4802
5095
  const opacity = typeof shapeStyle.innerShadowOpacity === "number" && Number.isFinite(shapeStyle.innerShadowOpacity) ? this.context.clampUnitInterval(shapeStyle.innerShadowOpacity) : 0.5;
4803
5096
  const distance = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
4804
5097
  const directionDegrees = (Math.atan2(offsetY, offsetX) * 180 / Math.PI + 360) % 360;
4805
- return {
5098
+ const xmlObj = {
4806
5099
  "@_blurRad": String(Math.round(blurValue * this.context.emuPerPx)),
4807
5100
  "@_dist": String(Math.round(distance * this.context.emuPerPx)),
4808
5101
  "@_dir": String(Math.round(directionDegrees * 6e4)),
@@ -4813,6 +5106,10 @@ var PptxShapeEffectXmlBuilder = class {
4813
5106
  }
4814
5107
  }
4815
5108
  };
5109
+ if (typeof shapeStyle.innerShadowRotateWithShape === "boolean") {
5110
+ xmlObj["@_rotWithShape"] = shapeStyle.innerShadowRotateWithShape ? "1" : "0";
5111
+ }
5112
+ return xmlObj;
4816
5113
  }
4817
5114
  buildGlowXml(shapeStyle) {
4818
5115
  const glowColor = String(shapeStyle.glowColor || "").trim();
@@ -4874,6 +5171,30 @@ var PptxShapeEffectXmlBuilder = class {
4874
5171
  Math.round(shapeStyle.reflectionDistance * this.context.emuPerPx)
4875
5172
  );
4876
5173
  }
5174
+ if (typeof shapeStyle.reflectionFadeDirection === "number") {
5175
+ reflectionXml["@_fadeDir"] = String(Math.round(shapeStyle.reflectionFadeDirection * 6e4));
5176
+ }
5177
+ if (typeof shapeStyle.reflectionScaleX === "number") {
5178
+ reflectionXml["@_sx"] = String(Math.round(shapeStyle.reflectionScaleX));
5179
+ }
5180
+ if (typeof shapeStyle.reflectionScaleY === "number") {
5181
+ reflectionXml["@_sy"] = String(Math.round(shapeStyle.reflectionScaleY));
5182
+ }
5183
+ if (typeof shapeStyle.reflectionSkewX === "number") {
5184
+ reflectionXml["@_kx"] = String(Math.round(shapeStyle.reflectionSkewX));
5185
+ }
5186
+ if (typeof shapeStyle.reflectionSkewY === "number") {
5187
+ reflectionXml["@_ky"] = String(Math.round(shapeStyle.reflectionSkewY));
5188
+ }
5189
+ if (shapeStyle.reflectionAlignment) {
5190
+ reflectionXml["@_algn"] = shapeStyle.reflectionAlignment;
5191
+ }
5192
+ if (typeof shapeStyle.reflectionRotateWithShape === "boolean") {
5193
+ reflectionXml["@_rotWithShape"] = shapeStyle.reflectionRotateWithShape ? "1" : "0";
5194
+ }
5195
+ if (typeof shapeStyle.reflectionStartPosition === "number") {
5196
+ reflectionXml["@_stPos"] = String(Math.round(shapeStyle.reflectionStartPosition * 1e5));
5197
+ }
4877
5198
  return reflectionXml;
4878
5199
  }
4879
5200
  buildBlurXml(shapeStyle) {
@@ -4981,6 +5302,9 @@ var PptxShapeEffectXmlCodec = class {
4981
5302
  buildOuterShadowXml(shapeStyle) {
4982
5303
  return this.builder.buildOuterShadowXml(shapeStyle);
4983
5304
  }
5305
+ buildPresetShadowXml(shapeStyle) {
5306
+ return this.builder.buildPresetShadowXml(shapeStyle);
5307
+ }
4984
5308
  buildInnerShadowXml(shapeStyle) {
4985
5309
  return this.builder.buildInnerShadowXml(shapeStyle);
4986
5310
  }
@@ -5141,6 +5465,9 @@ var PptxColorStyleCodec = class {
5141
5465
  buildOuterShadowXml(shapeStyle) {
5142
5466
  return this.shapeEffectXmlCodec.buildOuterShadowXml(shapeStyle);
5143
5467
  }
5468
+ buildPresetShadowXml(shapeStyle) {
5469
+ return this.shapeEffectXmlCodec.buildPresetShadowXml(shapeStyle);
5470
+ }
5144
5471
  buildInnerShadowXml(shapeStyle) {
5145
5472
  return this.shapeEffectXmlCodec.buildInnerShadowXml(shapeStyle);
5146
5473
  }
@@ -5162,6 +5489,15 @@ var PptxColorStyleCodec = class {
5162
5489
  extractGradientFillColor(gradFill) {
5163
5490
  return this.gradientStyleCodec.extractGradientFillColor(gradFill);
5164
5491
  }
5492
+ extractGradientFlip(gradFill) {
5493
+ return this.gradientStyleCodec.extractGradientFlip(gradFill);
5494
+ }
5495
+ extractGradientRotWithShape(gradFill) {
5496
+ return this.gradientStyleCodec.extractGradientRotWithShape(gradFill);
5497
+ }
5498
+ extractGradientScaled(gradFill) {
5499
+ return this.gradientStyleCodec.extractGradientScaled(gradFill);
5500
+ }
5165
5501
  extractGradientPathType(gradFill) {
5166
5502
  return this.gradientStyleCodec.extractGradientPathType(gradFill);
5167
5503
  }
@@ -5173,6 +5509,61 @@ var PptxColorStyleCodec = class {
5173
5509
  }
5174
5510
  };
5175
5511
 
5512
+ // src/core/utils/color-xml-preservation.ts
5513
+ var COLOR_CHOICE_KEYS = [
5514
+ "a:srgbClr",
5515
+ "a:schemeClr",
5516
+ "a:sysClr",
5517
+ "a:prstClr",
5518
+ "a:scrgbClr",
5519
+ "a:hslClr"
5520
+ ];
5521
+ function extractColorChoiceXml(parent) {
5522
+ if (!parent) {
5523
+ return void 0;
5524
+ }
5525
+ for (const key of COLOR_CHOICE_KEYS) {
5526
+ if (parent[key] !== void 0) {
5527
+ return { [key]: parent[key] };
5528
+ }
5529
+ }
5530
+ return void 0;
5531
+ }
5532
+ function normalizeHex(value) {
5533
+ const raw = String(value ?? "").trim();
5534
+ if (raw.length === 0) {
5535
+ return "";
5536
+ }
5537
+ const hex = raw.replace(/^#/, "");
5538
+ if (/^[0-9a-fA-F]{6}$/.test(hex)) {
5539
+ return hex.toUpperCase();
5540
+ }
5541
+ return raw.toLowerCase();
5542
+ }
5543
+ function colorsEqual(left, right) {
5544
+ if (left === void 0 || right === void 0) {
5545
+ return false;
5546
+ }
5547
+ return normalizeHex(left) === normalizeHex(right);
5548
+ }
5549
+ function buildSrgbColorChoice(hex, opacity) {
5550
+ const normalized = String(hex || "").replace(/^#/, "");
5551
+ const srgb = { "@_val": normalized };
5552
+ if (typeof opacity === "number" && Number.isFinite(opacity) && opacity >= 0 && opacity < 1) {
5553
+ const alphaPct = Math.round(Math.max(0, Math.min(1, opacity)) * 1e5);
5554
+ srgb["a:alpha"] = { "@_val": String(alphaPct) };
5555
+ }
5556
+ return { "a:srgbClr": srgb };
5557
+ }
5558
+ function serializeColorChoice(originalColorXml, currentResolvedHex, fallbackHex, opacity, options = {}) {
5559
+ if (originalColorXml && colorsEqual(currentResolvedHex, fallbackHex)) {
5560
+ if (options.preserveAlphaFromOriginal !== false) {
5561
+ return originalColorXml;
5562
+ }
5563
+ }
5564
+ return buildSrgbColorChoice(fallbackHex, opacity);
5565
+ }
5566
+
5176
5567
  // src/core/core/builders/shape-style-3d-helpers.ts
5177
5568
  function applyScene3dStyle(shapeProps, style) {
5178
5569
  const scene3dNode = shapeProps["a:scene3d"];
@@ -5258,6 +5649,10 @@ function applyStrokeColor(lineNode, style, context) {
5258
5649
  const lineFill = lineNode["a:solidFill"];
5259
5650
  style.strokeColor = context.parseColor(lineFill);
5260
5651
  style.strokeOpacity = context.extractColorOpacity(lineFill);
5652
+ const strokeColorXml = extractColorChoiceXml(lineFill);
5653
+ if (strokeColorXml) {
5654
+ style.strokeColorXml = strokeColorXml;
5655
+ }
5261
5656
  } else if (lineNode["a:gradFill"]) {
5262
5657
  style.strokeColor = context.extractGradientFillColor(lineNode["a:gradFill"]);
5263
5658
  style.strokeOpacity = context.extractGradientOpacity(lineNode["a:gradFill"]);
@@ -5325,6 +5720,14 @@ function applyJoinCapCompound(lineNode, style) {
5325
5720
  style.lineJoin = "bevel";
5326
5721
  } else if ("a:miter" in lineNode) {
5327
5722
  style.lineJoin = "miter";
5723
+ const miterNode = lineNode["a:miter"];
5724
+ const limRaw = miterNode?.["@_lim"];
5725
+ if (limRaw !== void 0 && limRaw !== "") {
5726
+ const parsed = parseInt(String(limRaw), 10);
5727
+ if (Number.isFinite(parsed)) {
5728
+ style.miterLimit = parsed;
5729
+ }
5730
+ }
5328
5731
  }
5329
5732
  const capValue = String(lineNode["@_cap"] || "").trim().toLowerCase();
5330
5733
  if (capValue === "rnd" || capValue === "sq" || capValue === "flat") {
@@ -5381,6 +5784,10 @@ var PptxShapeStyleExtractor = class {
5381
5784
  style.fillMode = "solid";
5382
5785
  style.fillColor = this.context.parseColor(solidFill);
5383
5786
  style.fillOpacity = this.context.extractColorOpacity(solidFill);
5787
+ const solidFillColorXml = extractColorChoiceXml(solidFill);
5788
+ if (solidFillColorXml) {
5789
+ style.fillColorXml = solidFillColorXml;
5790
+ }
5384
5791
  } else if (gradFill) {
5385
5792
  style.fillMode = "gradient";
5386
5793
  style.fillColor = this.context.extractGradientFillColor(gradFill);
@@ -5392,6 +5799,18 @@ var PptxShapeStyleExtractor = class {
5392
5799
  style.fillGradientPathType = this.context.extractGradientPathType(gradFill);
5393
5800
  style.fillGradientFocalPoint = this.context.extractGradientFocalPoint(gradFill);
5394
5801
  style.fillGradientFillToRect = this.context.extractGradientFillToRect(gradFill);
5802
+ const gradFlip = this.context.extractGradientFlip(gradFill);
5803
+ if (gradFlip) {
5804
+ style.fillGradientFlip = gradFlip;
5805
+ }
5806
+ const gradRot = this.context.extractGradientRotWithShape(gradFill);
5807
+ if (gradRot !== void 0) {
5808
+ style.fillGradientRotWithShape = gradRot;
5809
+ }
5810
+ const gradScaled = this.context.extractGradientScaled(gradFill);
5811
+ if (gradScaled !== void 0) {
5812
+ style.fillGradientScaled = gradScaled;
5813
+ }
5395
5814
  } else if (pattFill) {
5396
5815
  style.fillMode = "pattern";
5397
5816
  style.fillColor = this.context.parseColor(pattFill["a:fgClr"]) || this.context.parseColor(pattFill["a:bgClr"]);
@@ -5473,10 +5892,45 @@ var PptxShapeStyleExtractor = class {
5473
5892
  if (styleNode?.["a:effectRef"]) {
5474
5893
  this.context.resolveThemeEffectRef(styleNode["a:effectRef"], style);
5475
5894
  }
5895
+ const fontRef = styleNode?.["a:fontRef"];
5896
+ if (fontRef) {
5897
+ const idxAttr = String(fontRef["@_idx"] || "").trim();
5898
+ if (idxAttr.length > 0) {
5899
+ style.fontRefIdx = idxAttr;
5900
+ }
5901
+ const overrideColorXml = this.extractFontRefColorXml(fontRef);
5902
+ if (overrideColorXml) {
5903
+ style.fontRefColorXml = overrideColorXml;
5904
+ }
5905
+ }
5476
5906
  applyScene3dStyle(shapeProps, style);
5477
5907
  applyShape3dStyle(shapeProps, style, this.context);
5478
5908
  return style;
5479
5909
  }
5910
+ /**
5911
+ * Pull the verbatim colour-choice child out of an `a:fontRef` element,
5912
+ * preserving any contained colour transforms for round-trip.
5913
+ */
5914
+ extractFontRefColorXml(refNode) {
5915
+ if (!refNode) {
5916
+ return void 0;
5917
+ }
5918
+ const keys = [
5919
+ "a:scrgbClr",
5920
+ "a:srgbClr",
5921
+ "a:hslClr",
5922
+ "a:sysClr",
5923
+ "a:schemeClr",
5924
+ "a:prstClr"
5925
+ ];
5926
+ for (const key of keys) {
5927
+ const child = refNode[key];
5928
+ if (child !== void 0) {
5929
+ return { [key]: child };
5930
+ }
5931
+ }
5932
+ return void 0;
5933
+ }
5480
5934
  /**
5481
5935
  * Extract p14:hiddenFill from the shape properties extension list.
5482
5936
  * URI: {AF507438-7753-43E0-B8FC-AC1667EBCBE1}
@@ -5527,10 +5981,15 @@ var PptxShapeStyleExtractor = class {
5527
5981
  function applyCellFillStyle(cellProperties, style, context) {
5528
5982
  let hasStyle = false;
5529
5983
  if (cellProperties?.["a:solidFill"]) {
5530
- const fillColor = context.parseColor(cellProperties["a:solidFill"]);
5984
+ const solidFillNode = cellProperties["a:solidFill"];
5985
+ const fillColor = context.parseColor(solidFillNode);
5531
5986
  if (fillColor) {
5532
5987
  style.fillMode = "solid";
5533
5988
  style.backgroundColor = fillColor;
5989
+ const bgColorXml = extractColorChoiceXml(solidFillNode);
5990
+ if (bgColorXml) {
5991
+ style.backgroundColorXml = bgColorXml;
5992
+ }
5534
5993
  hasStyle = true;
5535
5994
  }
5536
5995
  }
@@ -5567,6 +6026,10 @@ function applyCellFillStyle(cellProperties, style, context) {
5567
6026
  }
5568
6027
  }
5569
6028
  }
6029
+ if (cellProperties?.["a:noFill"] !== void 0) {
6030
+ style.fillMode = "none";
6031
+ hasStyle = true;
6032
+ }
5570
6033
  if (cellProperties?.["a:pattFill"]) {
5571
6034
  const pattFill = cellProperties["a:pattFill"];
5572
6035
  const fgColor = context.parseColor(pattFill["a:fgClr"]);
@@ -5873,8 +6336,7 @@ var PptxTableDataParser = class {
5873
6336
  return width / totalWidthEmu;
5874
6337
  }) : gridColumns.map(() => 1 / Math.max(gridColumns.length, 1));
5875
6338
  const tableProperties = tableNode["a:tblPr"] || {};
5876
- const tableStyleNode = tableProperties["a:tblStyle"];
5877
- const tableStyleId = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim() || void 0;
6339
+ const tableStyleId = this.extractTableStyleId(tableProperties);
5878
6340
  const xmlRows = this.context.ensureArray(tableNode["a:tr"]);
5879
6341
  const rows = xmlRows.map((rowNode) => {
5880
6342
  const rowHeightEmu = parseInt(String(rowNode?.["@_h"] || "0"), 10) || 0;
@@ -5909,6 +6371,28 @@ var PptxTableDataParser = class {
5909
6371
  return void 0;
5910
6372
  }
5911
6373
  }
6374
+ /**
6375
+ * Read the table style ID from `a:tblPr`.
6376
+ *
6377
+ * ECMA-376 §21.1.3.13 defines `<a:tableStyleId>{GUID}</a:tableStyleId>` as
6378
+ * a child element of `a:tblPr` carrying the GUID as element text. Older
6379
+ * inputs (and earlier versions of this library) used the legacy
6380
+ * `<a:tblStyle val="{GUID}"/>` child element or a `@_tblStyle` attribute.
6381
+ * Accept all three; the spec form takes precedence.
6382
+ */
6383
+ extractTableStyleId(tableProperties) {
6384
+ const tableStyleIdNode = tableProperties["a:tableStyleId"];
6385
+ if (tableStyleIdNode !== void 0 && tableStyleIdNode !== null) {
6386
+ const direct = typeof tableStyleIdNode === "string" || typeof tableStyleIdNode === "number" ? String(tableStyleIdNode) : String(tableStyleIdNode["#text"] ?? "");
6387
+ const trimmed = direct.trim();
6388
+ if (trimmed.length > 0) {
6389
+ return trimmed;
6390
+ }
6391
+ }
6392
+ const tableStyleNode = tableProperties["a:tblStyle"];
6393
+ const legacy = String(tableStyleNode?.["@_val"] || tableProperties["@_tblStyle"] || "").trim();
6394
+ return legacy.length > 0 ? legacy : void 0;
6395
+ }
5912
6396
  extractTableCellText(tableCell) {
5913
6397
  const paragraphs = this.context.ensureArray(tableCell?.["a:txBody"]?.["a:p"]);
5914
6398
  const lines = [];
@@ -6205,6 +6689,19 @@ var PptxGraphicFrameParser = class {
6205
6689
  if (graphicData["a:videoFile"] || graphicData["a:audioFile"] || uri.includes("/drawingml/2006/media")) {
6206
6690
  return "media";
6207
6691
  }
6692
+ if (graphicData["aink:ink"] || uri.includes("/2010/ink") || uri.includes("drawing/2010/ink")) {
6693
+ return "ink";
6694
+ }
6695
+ const alternateContent = graphicData["mc:AlternateContent"];
6696
+ if (alternateContent) {
6697
+ const choices = Array.isArray(alternateContent["mc:Choice"]) ? alternateContent["mc:Choice"] : alternateContent["mc:Choice"] ? [alternateContent["mc:Choice"]] : [];
6698
+ for (const choice of choices) {
6699
+ const requires = String(choice?.["@_Requires"] || "").toLowerCase();
6700
+ if (requires.includes("aink") || choice?.["aink:ink"]) {
6701
+ return "ink";
6702
+ }
6703
+ }
6704
+ }
6208
6705
  return "unknown";
6209
6706
  }
6210
6707
  };
@@ -7106,6 +7603,7 @@ var PptxDocumentPropertiesUpdater = class {
7106
7603
  }));
7107
7604
  if (sanitized.length === 0) {
7108
7605
  this.context.zip.remove("docProps/custom.xml");
7606
+ await this.removeCustomPropertiesPackagingArtifacts();
7109
7607
  return;
7110
7608
  }
7111
7609
  const customXml = {
@@ -7125,6 +7623,116 @@ var PptxDocumentPropertiesUpdater = class {
7125
7623
  }
7126
7624
  };
7127
7625
  this.context.zip.file("docProps/custom.xml", this.context.builder.build(customXml));
7626
+ await this.ensureCustomPropertiesPackagingArtifacts();
7627
+ }
7628
+ /**
7629
+ * Ensure `[Content_Types].xml` has an `Override` for `docProps/custom.xml`
7630
+ * and the root `_rels/.rels` references it (ECMA-376 §15.2.12.2 +
7631
+ * Part 2 §10.1.2.5). Without these, the package fails OPC validation
7632
+ * and Office strips the custom properties on next save.
7633
+ */
7634
+ async ensureCustomPropertiesPackagingArtifacts() {
7635
+ const customContentType = "application/vnd.openxmlformats-officedocument.custom-properties+xml";
7636
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
7637
+ const ctFile = this.context.zip.file("[Content_Types].xml");
7638
+ if (ctFile) {
7639
+ try {
7640
+ const ctXml = await ctFile.async("string");
7641
+ const ctData = this.context.parser.parse(ctXml);
7642
+ const types = ctData["Types"];
7643
+ if (types) {
7644
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
7645
+ const hasCustomOverride = overrides.some(
7646
+ (o) => String(o?.["@_PartName"] || "") === "/docProps/custom.xml"
7647
+ );
7648
+ if (!hasCustomOverride) {
7649
+ overrides.push({
7650
+ "@_PartName": "/docProps/custom.xml",
7651
+ "@_ContentType": customContentType
7652
+ });
7653
+ types["Override"] = overrides.length === 1 ? overrides[0] : overrides;
7654
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
7655
+ }
7656
+ }
7657
+ } catch (error) {
7658
+ console.warn("Failed to update [Content_Types].xml for custom properties:", error);
7659
+ }
7660
+ }
7661
+ const relsFile = this.context.zip.file("_rels/.rels");
7662
+ if (relsFile) {
7663
+ try {
7664
+ const relsXml = await relsFile.async("string");
7665
+ const relsData = this.context.parser.parse(relsXml);
7666
+ const relationships = relsData["Relationships"];
7667
+ if (relationships) {
7668
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
7669
+ const hasCustomRel = rels.some((r) => String(r?.["@_Type"] || "") === customRelType);
7670
+ if (!hasCustomRel) {
7671
+ let maxId = 0;
7672
+ for (const rel of rels) {
7673
+ const id = String(rel?.["@_Id"] || "");
7674
+ const num = Number.parseInt(id.replace(/^rId/, ""), 10);
7675
+ if (Number.isFinite(num) && num > maxId) {
7676
+ maxId = num;
7677
+ }
7678
+ }
7679
+ rels.push({
7680
+ "@_Id": `rId${maxId + 1}`,
7681
+ "@_Type": customRelType,
7682
+ "@_Target": "docProps/custom.xml"
7683
+ });
7684
+ relationships["Relationship"] = rels;
7685
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
7686
+ }
7687
+ }
7688
+ } catch (error) {
7689
+ console.warn("Failed to update _rels/.rels for custom properties:", error);
7690
+ }
7691
+ }
7692
+ }
7693
+ /**
7694
+ * Remove the Override + root rel for `docProps/custom.xml` when the
7695
+ * caller has emptied custom properties so the package doesn't keep an
7696
+ * orphan content-type entry referencing a deleted part.
7697
+ */
7698
+ async removeCustomPropertiesPackagingArtifacts() {
7699
+ const customRelType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties";
7700
+ const ctFile = this.context.zip.file("[Content_Types].xml");
7701
+ if (ctFile) {
7702
+ try {
7703
+ const ctXml = await ctFile.async("string");
7704
+ const ctData = this.context.parser.parse(ctXml);
7705
+ const types = ctData["Types"];
7706
+ if (types) {
7707
+ const overrides = Array.isArray(types["Override"]) ? types["Override"] : types["Override"] ? [types["Override"]] : [];
7708
+ const filtered = overrides.filter(
7709
+ (o) => String(o?.["@_PartName"] || "") !== "/docProps/custom.xml"
7710
+ );
7711
+ if (filtered.length !== overrides.length) {
7712
+ types["Override"] = filtered.length === 1 ? filtered[0] : filtered;
7713
+ this.context.zip.file("[Content_Types].xml", this.context.builder.build(ctData));
7714
+ }
7715
+ }
7716
+ } catch {
7717
+ }
7718
+ }
7719
+ const relsFile = this.context.zip.file("_rels/.rels");
7720
+ if (relsFile) {
7721
+ try {
7722
+ const relsXml = await relsFile.async("string");
7723
+ const relsData = this.context.parser.parse(relsXml);
7724
+ const relationships = relsData["Relationships"];
7725
+ if (relationships) {
7726
+ const rels = Array.isArray(relationships["Relationship"]) ? relationships["Relationship"] : relationships["Relationship"] ? [relationships["Relationship"]] : [];
7727
+ const filtered = rels.filter((r) => String(r?.["@_Type"] || "") !== customRelType);
7728
+ if (filtered.length !== rels.length) {
7729
+ relationships["Relationship"] = filtered;
7730
+ this.context.zip.file("_rels/.rels", this.context.builder.build(relsData));
7731
+ }
7732
+ }
7733
+ } catch {
7734
+ }
7735
+ }
7128
7736
  }
7129
7737
  normalizeCustomPropertyType(type) {
7130
7738
  const supportedTypes = /* @__PURE__ */ new Set([
@@ -7477,6 +8085,7 @@ var PptxSlideLoaderService = class {
7477
8085
  await params.loadSlideRelationships(path, slideRelsPath);
7478
8086
  const clrMapOverride = params.parseSlideClrMapOverride(slideXmlObj);
7479
8087
  params.setCurrentSlideClrMapOverride(clrMapOverride);
8088
+ await params.setActiveMasterForSlide?.(path);
7480
8089
  let restoreThemeOverride;
7481
8090
  try {
7482
8091
  const layoutPathForOverride = params.findLayoutPathForSlide(path);
@@ -12452,6 +13061,20 @@ var AXIS_TYPE_MAP = {
12452
13061
  dateAx: "dateAx",
12453
13062
  serAx: "serAx"
12454
13063
  };
13064
+ function upsertChartAxisChild(parent, localName, value, getLocalName) {
13065
+ const existingKey = Object.keys(parent).find((k) => getLocalName(k) === localName);
13066
+ if (value === void 0) {
13067
+ if (existingKey) {
13068
+ delete parent[existingKey];
13069
+ }
13070
+ return;
13071
+ }
13072
+ if (existingKey) {
13073
+ parent[existingKey]["@_val"] = value;
13074
+ } else {
13075
+ parent[`c:${localName}`] = { "@_val": value };
13076
+ }
13077
+ }
12455
13078
  function parseChartAxes(plotArea, xmlLookup, colorParser, getLocalName) {
12456
13079
  const result = [];
12457
13080
  for (const key of Object.keys(plotArea)) {
@@ -12572,6 +13195,27 @@ function parseSingleAxis(axisNode, axisType, xmlLookup, colorParser) {
12572
13195
  if (dispUnitsNode) {
12573
13196
  parseDisplayUnits(dispUnitsNode, xmlLookup, result);
12574
13197
  }
13198
+ const majorUnitNode = xmlLookup.getChildByLocalName(axisNode, "majorUnit");
13199
+ if (majorUnitNode) {
13200
+ const majorVal = parseFloat(String(majorUnitNode["@_val"]));
13201
+ if (Number.isFinite(majorVal)) {
13202
+ result.majorUnit = majorVal;
13203
+ }
13204
+ }
13205
+ const minorUnitNode = xmlLookup.getChildByLocalName(axisNode, "minorUnit");
13206
+ if (minorUnitNode) {
13207
+ const minorVal = parseFloat(String(minorUnitNode["@_val"]));
13208
+ if (Number.isFinite(minorVal)) {
13209
+ result.minorUnit = minorVal;
13210
+ }
13211
+ }
13212
+ const tickLblPosNode = xmlLookup.getChildByLocalName(axisNode, "tickLblPos");
13213
+ if (tickLblPosNode) {
13214
+ const v = String(tickLblPosNode["@_val"] || "").trim();
13215
+ if (v === "high" || v === "low" || v === "nextTo" || v === "none") {
13216
+ result.tickLblPos = v;
13217
+ }
13218
+ }
12575
13219
  return result;
12576
13220
  }
12577
13221
  var VALID_DISPLAY_UNITS = /* @__PURE__ */ new Set([
@@ -13884,23 +14528,55 @@ var SHAPE_TREE_ELEMENT_TAGS = /* @__PURE__ */ new Set([
13884
14528
  "p16:model3D",
13885
14529
  ...VML_SHAPE_TAGS
13886
14530
  ]);
14531
+ function diagnoseSelection(ac) {
14532
+ const choices = ensureArray2(ac["mc:Choice"]);
14533
+ for (let i = 0; i < choices.length; i++) {
14534
+ const choice = choices[i];
14535
+ const requires = String(choice?.["@_Requires"] ?? "").trim();
14536
+ if (requires.length === 0 || areNamespacesSupported(requires)) {
14537
+ const resolved = resolveNestedAlternateContent(choice);
14538
+ return { branch: "choice", choiceIndex: i, resolved };
14539
+ }
14540
+ }
14541
+ const fallback = ac["mc:Fallback"];
14542
+ if (fallback) {
14543
+ return { branch: "fallback", resolved: resolveNestedAlternateContent(fallback) };
14544
+ }
14545
+ return void 0;
14546
+ }
13887
14547
  function unwrapAlternateContent(container) {
13888
14548
  const altContents = ensureArray2(container["mc:AlternateContent"]);
13889
14549
  if (altContents.length === 0) {
13890
- return;
14550
+ return [];
13891
14551
  }
14552
+ const blocks = [];
13892
14553
  for (const ac of altContents) {
13893
- const branch = selectAlternateContentBranch(ac);
13894
- if (!branch) {
14554
+ const diagnosis = diagnoseSelection(ac);
14555
+ if (!diagnosis) {
13895
14556
  continue;
13896
14557
  }
14558
+ const block = {
14559
+ rawAc: ac,
14560
+ selectedBranch: diagnosis.branch,
14561
+ choiceIndex: diagnosis.branch === "choice" ? diagnosis.choiceIndex : void 0,
14562
+ childRefs: []
14563
+ };
14564
+ const branch = diagnosis.resolved;
13897
14565
  for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
13898
14566
  const children = ensureArray2(branch[tag]);
13899
14567
  if (children.length > 0) {
13900
14568
  container[tag] = [...ensureArray2(container[tag]), ...children];
14569
+ for (const child of children) {
14570
+ block.childRefs.push({ tag, node: child });
14571
+ }
13901
14572
  }
13902
14573
  }
14574
+ if (block.childRefs.length > 0) {
14575
+ blocks.push(block);
14576
+ }
13903
14577
  }
14578
+ delete container["mc:AlternateContent"];
14579
+ return blocks;
13904
14580
  }
13905
14581
  function isNamespaceSupported(ns) {
13906
14582
  return SUPPORTED_MC_NAMESPACES.has(ns);
@@ -13918,7 +14594,7 @@ function ensureArray2(val) {
13918
14594
 
13919
14595
  // src/core/utils/body-properties-parser.ts
13920
14596
  function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
13921
- const parseBoolAttr = (attr) => {
14597
+ const parseBoolAttr2 = (attr) => {
13922
14598
  const raw = bodyPr[attr];
13923
14599
  if (raw === void 0) {
13924
14600
  return void 0;
@@ -13926,19 +14602,19 @@ function parseBodyPrBooleanAttrs(bodyPr, textStyle) {
13926
14602
  const val = String(raw).trim().toLowerCase();
13927
14603
  return val === "1" || val === "true";
13928
14604
  };
13929
- const compatLnSpc = parseBoolAttr("@_compatLnSpc");
14605
+ const compatLnSpc = parseBoolAttr2("@_compatLnSpc");
13930
14606
  if (compatLnSpc !== void 0) {
13931
14607
  textStyle.compatibleLineSpacing = compatLnSpc;
13932
14608
  }
13933
- const forceAA = parseBoolAttr("@_forceAA");
14609
+ const forceAA = parseBoolAttr2("@_forceAA");
13934
14610
  if (forceAA !== void 0) {
13935
14611
  textStyle.forceAntiAlias = forceAA;
13936
14612
  }
13937
- const upright = parseBoolAttr("@_upright");
14613
+ const upright = parseBoolAttr2("@_upright");
13938
14614
  if (upright !== void 0) {
13939
14615
  textStyle.upright = upright;
13940
14616
  }
13941
- const fromWordArt = parseBoolAttr("@_fromWordArt");
14617
+ const fromWordArt = parseBoolAttr2("@_fromWordArt");
13942
14618
  if (fromWordArt !== void 0) {
13943
14619
  textStyle.fromWordArt = fromWordArt;
13944
14620
  }
@@ -13961,100 +14637,6 @@ function writeBodyPrBooleanAttrs(bodyPr, textStyle) {
13961
14637
  }
13962
14638
  }
13963
14639
 
13964
- // src/core/utils/theme-override-utils.ts
13965
- var COLOR_MAP_ALIAS_KEYS = [
13966
- "bg1",
13967
- "tx1",
13968
- "bg2",
13969
- "tx2",
13970
- "accent1",
13971
- "accent2",
13972
- "accent3",
13973
- "accent4",
13974
- "accent5",
13975
- "accent6",
13976
- "hlink",
13977
- "folHlink"
13978
- ];
13979
- var DEFAULT_COLOR_MAP = {
13980
- bg1: "lt1",
13981
- tx1: "dk1",
13982
- bg2: "lt2",
13983
- tx2: "dk2",
13984
- accent1: "accent1",
13985
- accent2: "accent2",
13986
- accent3: "accent3",
13987
- accent4: "accent4",
13988
- accent5: "accent5",
13989
- accent6: "accent6",
13990
- hlink: "hlink",
13991
- folHlink: "folHlink"
13992
- };
13993
- function buildClrMapOverrideXml(override) {
13994
- if (!override || Object.keys(override).length === 0) {
13995
- return { "a:masterClrMapping": {} };
13996
- }
13997
- const attrs2 = {};
13998
- for (const key of COLOR_MAP_ALIAS_KEYS) {
13999
- attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
14000
- }
14001
- return { "a:overrideClrMapping": attrs2 };
14002
- }
14003
- function mergeThemeColorOverride(base, override) {
14004
- if (!override || Object.keys(override).length === 0) {
14005
- return { ...base };
14006
- }
14007
- const slotLookup = {};
14008
- for (const key of THEME_COLOR_SCHEME_KEYS) {
14009
- slotLookup[key] = base[key];
14010
- }
14011
- slotLookup["bg1"] = base.lt1;
14012
- slotLookup["tx1"] = base.dk1;
14013
- slotLookup["bg2"] = base.lt2;
14014
- slotLookup["tx2"] = base.dk2;
14015
- const result = { ...base };
14016
- for (const [alias, targetSlot] of Object.entries(override)) {
14017
- const resolvedColor = slotLookup[targetSlot] ?? base[targetSlot];
14018
- if (!resolvedColor) {
14019
- continue;
14020
- }
14021
- switch (alias) {
14022
- case "bg1":
14023
- result.lt1 = resolvedColor;
14024
- break;
14025
- case "tx1":
14026
- result.dk1 = resolvedColor;
14027
- break;
14028
- case "bg2":
14029
- result.lt2 = resolvedColor;
14030
- break;
14031
- case "tx2":
14032
- result.dk2 = resolvedColor;
14033
- break;
14034
- default: {
14035
- const schemeKey = alias;
14036
- if (schemeKey in result) {
14037
- result[schemeKey] = resolvedColor;
14038
- }
14039
- break;
14040
- }
14041
- }
14042
- }
14043
- return result;
14044
- }
14045
- function hasNonTrivialOverride(override) {
14046
- if (!override) {
14047
- return false;
14048
- }
14049
- for (const key of COLOR_MAP_ALIAS_KEYS) {
14050
- const overrideValue = override[key];
14051
- if (overrideValue && overrideValue !== DEFAULT_COLOR_MAP[key]) {
14052
- return true;
14053
- }
14054
- }
14055
- return false;
14056
- }
14057
-
14058
14640
  // src/core/utils/clone-utils.ts
14059
14641
  function cloneTextStyle(style) {
14060
14642
  if (!style) {
@@ -17095,6 +17677,100 @@ function resolveLayoutCategory(layoutType) {
17095
17677
  return "list";
17096
17678
  }
17097
17679
 
17680
+ // src/core/utils/theme-override-utils.ts
17681
+ var COLOR_MAP_ALIAS_KEYS = [
17682
+ "bg1",
17683
+ "tx1",
17684
+ "bg2",
17685
+ "tx2",
17686
+ "accent1",
17687
+ "accent2",
17688
+ "accent3",
17689
+ "accent4",
17690
+ "accent5",
17691
+ "accent6",
17692
+ "hlink",
17693
+ "folHlink"
17694
+ ];
17695
+ var DEFAULT_COLOR_MAP = {
17696
+ bg1: "lt1",
17697
+ tx1: "dk1",
17698
+ bg2: "lt2",
17699
+ tx2: "dk2",
17700
+ accent1: "accent1",
17701
+ accent2: "accent2",
17702
+ accent3: "accent3",
17703
+ accent4: "accent4",
17704
+ accent5: "accent5",
17705
+ accent6: "accent6",
17706
+ hlink: "hlink",
17707
+ folHlink: "folHlink"
17708
+ };
17709
+ function buildClrMapOverrideXml(override) {
17710
+ if (!override || Object.keys(override).length === 0) {
17711
+ return { "a:masterClrMapping": {} };
17712
+ }
17713
+ const attrs2 = {};
17714
+ for (const key of COLOR_MAP_ALIAS_KEYS) {
17715
+ attrs2[`@_${key}`] = override[key] ?? DEFAULT_COLOR_MAP[key];
17716
+ }
17717
+ return { "a:overrideClrMapping": attrs2 };
17718
+ }
17719
+ function mergeThemeColorOverride(base, override) {
17720
+ if (!override || Object.keys(override).length === 0) {
17721
+ return { ...base };
17722
+ }
17723
+ const slotLookup = {};
17724
+ for (const key of THEME_COLOR_SCHEME_KEYS) {
17725
+ slotLookup[key] = base[key];
17726
+ }
17727
+ slotLookup["bg1"] = base.lt1;
17728
+ slotLookup["tx1"] = base.dk1;
17729
+ slotLookup["bg2"] = base.lt2;
17730
+ slotLookup["tx2"] = base.dk2;
17731
+ const result = { ...base };
17732
+ for (const [alias, targetSlot] of Object.entries(override)) {
17733
+ const resolvedColor = slotLookup[targetSlot] ?? base[targetSlot];
17734
+ if (!resolvedColor) {
17735
+ continue;
17736
+ }
17737
+ switch (alias) {
17738
+ case "bg1":
17739
+ result.lt1 = resolvedColor;
17740
+ break;
17741
+ case "tx1":
17742
+ result.dk1 = resolvedColor;
17743
+ break;
17744
+ case "bg2":
17745
+ result.lt2 = resolvedColor;
17746
+ break;
17747
+ case "tx2":
17748
+ result.dk2 = resolvedColor;
17749
+ break;
17750
+ default: {
17751
+ const schemeKey = alias;
17752
+ if (schemeKey in result) {
17753
+ result[schemeKey] = resolvedColor;
17754
+ }
17755
+ break;
17756
+ }
17757
+ }
17758
+ }
17759
+ return result;
17760
+ }
17761
+ function hasNonTrivialOverride(override) {
17762
+ if (!override) {
17763
+ return false;
17764
+ }
17765
+ for (const key of COLOR_MAP_ALIAS_KEYS) {
17766
+ const overrideValue = override[key];
17767
+ if (overrideValue && overrideValue !== DEFAULT_COLOR_MAP[key]) {
17768
+ return true;
17769
+ }
17770
+ }
17771
+ return false;
17772
+ }
17773
+
17098
17774
  // src/core/utils/encryption-detection.ts
17099
17775
  var OLE_MAGIC = new Uint8Array([208, 207, 17, 224, 161, 27, 26, 225]);
17100
17776
  var ZIP_MAGIC = new Uint8Array([80, 75]);
@@ -24319,7 +24995,7 @@ async function repairPptx(buffer) {
24319
24995
  }
24320
24996
 
24321
24997
  // src/core/utils/theme-switching.ts
24322
- function normalizeHex(hex) {
24998
+ function normalizeHex2(hex) {
24323
24999
  if (!hex) {
24324
25000
  return "";
24325
25001
  }
@@ -24329,8 +25005,8 @@ function buildColorRemapTable(oldColorMap, newColorMap) {
24329
25005
  const remap = /* @__PURE__ */ new Map();
24330
25006
  const allKeys = [...THEME_COLOR_SCHEME_KEYS, "tx1", "bg1", "tx2", "bg2"];
24331
25007
  for (const key of allKeys) {
24332
- const oldVal = normalizeHex(oldColorMap[key]);
24333
- const newVal = normalizeHex(newColorMap[key]);
25008
+ const oldVal = normalizeHex2(oldColorMap[key]);
25009
+ const newVal = normalizeHex2(newColorMap[key]);
24334
25010
  if (oldVal && newVal && oldVal !== newVal) {
24335
25011
  remap.set(oldVal, `#${newVal}`);
24336
25012
  remap.set(`#${oldVal}`, `#${newVal}`);
@@ -24342,7 +25018,7 @@ function remapColor(color, remap) {
24342
25018
  if (!color) {
24343
25019
  return color;
24344
25020
  }
24345
- const normalized = normalizeHex(color);
25021
+ const normalized = normalizeHex2(color);
24346
25022
  const remapped = remap.get(normalized) ?? remap.get(`#${normalized}`);
24347
25023
  return remapped ?? color;
24348
25024
  }
@@ -24482,7 +25158,7 @@ function remapSlideColors(slide, remap) {
24482
25158
  function buildThemeColorMap(colorScheme) {
24483
25159
  const map = {};
24484
25160
  for (const key of THEME_COLOR_SCHEME_KEYS) {
24485
- map[key] = normalizeHex(colorScheme[key]);
25161
+ map[key] = normalizeHex2(colorScheme[key]);
24486
25162
  }
24487
25163
  map.tx1 = map.dk1;
24488
25164
  map.bg1 = map.lt1;
@@ -25455,8 +26131,11 @@ function relayoutSmartArt(smartArtData, containerWidth, containerHeight) {
25455
26131
 
25456
26132
  // src/core/core/runtime/PptxHandlerRuntimeSaveParagraphHelpers.ts
25457
26133
  var EMU_PER_PX5 = 9525;
25458
- function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing) {
26134
+ function buildParagraphPropertiesXml(textStyle, paragraphAlign, bulletInfo, spacing, level) {
25459
26135
  const paragraphProps = {};
26136
+ if (typeof level === "number" && Number.isFinite(level) && level > 0) {
26137
+ paragraphProps["@_lvl"] = String(Math.min(Math.max(Math.round(level), 0), 8));
26138
+ }
25460
26139
  if (paragraphAlign) {
25461
26140
  paragraphProps["@_algn"] = paragraphAlign;
25462
26141
  }
@@ -25528,23 +26207,28 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
25528
26207
  paragraphProps["a:buNone"] = {};
25529
26208
  return;
25530
26209
  }
25531
- if (bulletInfo.color) {
26210
+ if (bulletInfo.colorInherit) {
26211
+ paragraphProps["a:buClrTx"] = {};
26212
+ } else if (bulletInfo.color) {
25532
26213
  const colorHex = bulletInfo.color.replace("#", "");
25533
26214
  paragraphProps["a:buClr"] = {
25534
26215
  "a:srgbClr": { "@_val": colorHex }
25535
26216
  };
25536
26217
  }
25537
- if (bulletInfo.sizePercent !== void 0) {
26218
+ if (bulletInfo.sizeInherit) {
26219
+ paragraphProps["a:buSzTx"] = {};
26220
+ } else if (bulletInfo.sizePercent !== void 0) {
25538
26221
  paragraphProps["a:buSzPct"] = {
25539
26222
  "@_val": String(Math.round(bulletInfo.sizePercent * 1e3))
25540
26223
  };
25541
- }
25542
- if (bulletInfo.sizePts !== void 0) {
26224
+ } else if (bulletInfo.sizePts !== void 0) {
25543
26225
  paragraphProps["a:buSzPts"] = {
25544
26226
  "@_val": String(Math.round(bulletInfo.sizePts * 100))
25545
26227
  };
25546
26228
  }
25547
- if (bulletInfo.fontFamily) {
26229
+ if (bulletInfo.fontInherit) {
26230
+ paragraphProps["a:buFontTx"] = {};
26231
+ } else if (bulletInfo.fontFamily) {
25548
26232
  paragraphProps["a:buFont"] = {
25549
26233
  "@_typeface": bulletInfo.fontFamily
25550
26234
  };
@@ -25567,7 +26251,7 @@ function applyBulletProperties(paragraphProps, bulletInfo) {
25567
26251
  };
25568
26252
  }
25569
26253
  }
25570
- function assembleParagraphXml(runs, paragraphProps) {
26254
+ function assembleParagraphXml(runs, paragraphProps, endParaRunProperties) {
25571
26255
  const paragraph = {
25572
26256
  "a:pPr": paragraphProps
25573
26257
  };
@@ -25589,7 +26273,11 @@ function assembleParagraphXml(runs, paragraphProps) {
25589
26273
  if (cleanRegularRuns.length === 0 && fieldRuns.length === 0) {
25590
26274
  paragraph["a:r"] = runs.length > 1 ? runs : runs[0];
25591
26275
  }
25592
- paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
26276
+ if (endParaRunProperties && typeof endParaRunProperties === "object") {
26277
+ paragraph["a:endParaRPr"] = endParaRunProperties;
26278
+ } else {
26279
+ paragraph["a:endParaRPr"] = { "@_lang": "en-US" };
26280
+ }
25593
26281
  return paragraph;
25594
26282
  }
25595
26283
  function computeUniformSegmentOverrides(textStyle, textSegments) {
@@ -25893,6 +26581,140 @@ var PptxHandlerRuntime = class {
25893
26581
  * `p:clrMapOvr / a:overrideClrMapping`.
25894
26582
  */
25895
26583
  currentSlideClrMapOverride = null;
26584
+ /**
26585
+ * Per-master colour map alias dictionaries parsed from each master's
26586
+ * `<p:clrMap>` element (e.g. `bg1 → lt1`, `tx1 → dk1`, `accent1 → accent1`).
26587
+ *
26588
+ * `clrMap` is the *aliasing* layer between logical colour names used in
26589
+ * DrawingML and the raw theme scheme slots. Per ECMA-376 §19.3.1.7 it
26590
+ * lives on each `p:sldMaster`, and slide layouts/slides may further
26591
+ * override it via `p:clrMapOvr`. Resolution must happen at colour-lookup
26592
+ * time, *not* by baking it into {@link themeColorMap}.
26593
+ *
26594
+ * Phase 2 Stream B / C-H4.
26595
+ */
26596
+ masterClrMaps = /* @__PURE__ */ new Map();
26597
+ /**
26598
+ * Per-master theme color maps. Each master may reference its own theme
26599
+ * file via `_rels/slideMasterN.xml.rels`. For multi-master decks, slides
26600
+ * must resolve scheme colours against their *own* master's theme.
26601
+ *
26602
+ * Falls back to {@link themeColorMap} when a master entry is missing.
26603
+ *
26604
+ * Phase 2 Stream B / C-H4.
26605
+ */
26606
+ masterThemeColorMaps = /* @__PURE__ */ new Map();
26607
+ /**
26608
+ * Per-master theme font maps. Same rationale as
26609
+ * {@link masterThemeColorMaps}: multi-master decks may have one font
26610
+ * scheme per theme.
26611
+ */
26612
+ masterThemeFontMaps = /* @__PURE__ */ new Map();
26613
+ /**
26614
+ * Per-master format schemes (fmtScheme). For multi-master decks each
26615
+ * master's slides should resolve fill/line/effect refs against the
26616
+ * matrix from that master's theme.
26617
+ */
26618
+ masterThemeFormatSchemes = /* @__PURE__ */ new Map();
26619
+ /**
26620
+ * Per-master mapping from slide-master path to the theme path it
26621
+ * references via `_rels/slideMasterN.xml.rels`. Populated by
26622
+ * {@link loadPerMasterThemes} during load. Used by the save-side
26623
+ * theme writer to know which themeN.xml to (re)emit for each master.
26624
+ *
26625
+ * Phase 4 Stream A / C-H3.
26626
+ */
26627
+ masterThemePaths = /* @__PURE__ */ new Map();
26628
+ /**
26629
+ * Per-script font tables for major and minor fonts. Captured per master
26630
+ * theme. Keys are master paths; values map `mj`/`mn` -> script tag (e.g.
26631
+ * `Hans`, `Hant`, `Arab`, `Hebr`, `Thai`, `Beng`, …) -> typeface name.
26632
+ *
26633
+ * Phase 4 Stream A / M4.
26634
+ */
26635
+ masterThemeMajorFontScripts = /* @__PURE__ */ new Map();
26636
+ masterThemeMinorFontScripts = /* @__PURE__ */ new Map();
26637
+ /**
26638
+ * Theme name attribute (`<a:theme @name>`) per master theme path.
26639
+ * Captured for byte-stable round-trip.
26640
+ */
26641
+ masterThemeNames = /* @__PURE__ */ new Map();
26642
+ /**
26643
+ * `<a:fontScheme @name>` per master theme path.
26644
+ */
26645
+ masterThemeFontSchemeNames = /* @__PURE__ */ new Map();
26646
+ /**
26647
+ * `<a:clrScheme @name>` per master theme path.
26648
+ */
26649
+ masterThemeColorSchemeNames = /* @__PURE__ */ new Map();
26650
+ /**
26651
+ * Raw original theme XML keyed by theme path. Captured at load-time.
26652
+ * Used by the save pipeline to passthrough the full theme XML when no
26653
+ * in-memory mutation has occurred — preserving fillStyleLst /
26654
+ * lnStyleLst / effectStyleLst / bgFillStyleLst /
26655
+ * extraClrSchemeLst / objectDefaults / extLst exactly as written.
26656
+ *
26657
+ * Phase 4 Stream A / C-H3.
26658
+ */
26659
+ originalThemeXmlByPath = /* @__PURE__ */ new Map();
26660
+ /**
26661
+ * Set of theme paths whose in-memory state has been mutated since
26662
+ * load. Saving a theme path that's NOT dirty is a no-op (the original
26663
+ * file already exists in the ZIP). Saving a dirty theme path
26664
+ * regenerates the part from in-memory state.
26665
+ *
26666
+ * Phase 4 Stream A / C-H3.
26667
+ */
26668
+ dirtyThemePaths = /* @__PURE__ */ new Set();
26669
+ /**
26670
+ * Per-master parsed `<p:txStyles>` (titleStyle/bodyStyle/otherStyle).
26671
+ * Populated by {@link enrichSlideMastersWithTxStyles} during load so the
26672
+ * inheritance chain can find the master text-style cascade without
26673
+ * re-parsing master XML on every lookup. Phase 4 Stream B / P-H1.
26674
+ */
26675
+ masterTxStylesCache = /* @__PURE__ */ new Map();
26676
+ /**
26677
+ * Captured `<a:objectDefaults>` snapshot per master theme path. The
26678
+ * full ECMA-376 inheritance chain (master / layout / placeholder /
26679
+ * objectDefaults) is non-trivial; we store the raw spDef/lnDef/txDef
26680
+ * subtrees and re-emit them verbatim for round-trip.
26681
+ *
26682
+ * Phase 4 Stream A / M5.
26683
+ */
26684
+ masterThemeObjectDefaults = /* @__PURE__ */ new Map();
26685
+ /**
26686
+ * Captured `<a:extraClrSchemeLst>` raw subtree per master theme path
26687
+ * for verbatim round-trip.
26688
+ *
26689
+ * Phase 4 Stream A.
26690
+ */
26691
+ masterThemeExtraClrSchemeLst = /* @__PURE__ */ new Map();
26692
+ /**
26693
+ * Captured `<a:custClrLst>` raw subtree per master theme path
26694
+ * for verbatim round-trip.
26695
+ *
26696
+ * Phase 4 Stream A.
26697
+ */
26698
+ masterThemeCustClrLst = /* @__PURE__ */ new Map();
26699
+ /**
26700
+ * Captured theme-level `<a:extLst>` raw subtree per master theme path.
26701
+ */
26702
+ masterThemeExtLst = /* @__PURE__ */ new Map();
26703
+ /**
26704
+ * Active master's clrMap for the slide currently being parsed. Walked
26705
+ * after `currentSlideClrMapOverride` (slide and layout overrides take
26706
+ * precedence). `null` means "fall through to themeColorMap directly".
26707
+ */
26708
+ currentMasterClrMap = null;
26709
+ /**
26710
+ * Snapshot of the global theme state taken right after
26711
+ * {@link loadThemeData} completes. Used as the fallback when a slide's
26712
+ * master has no per-master theme entry, so per-slide multi-master
26713
+ * switching does not leak the previous slide's master state.
26714
+ */
26715
+ globalThemeColorMapSnapshot = {};
26716
+ globalThemeFontMapSnapshot = {};
26717
+ globalThemeFormatSchemeSnapshot;
25896
26718
  /** Thumbnail image data from `docProps/thumbnail.jpeg` preserved for round-trip. */
25897
26719
  thumbnailData = null;
25898
26720
  /** Raw VBA project binary preserved for macro-enabled (.pptm) round-trip. */
@@ -25903,6 +26725,19 @@ var PptxHandlerRuntime = class {
25903
26725
  signatureDetection = null;
25904
26726
  /** Custom XML data parts parsed from `customXml/` in the OPC package. */
25905
26727
  customXmlParts = [];
26728
+ /**
26729
+ * Maps an element's `rawXml` reference to the `mc:AlternateContent`
26730
+ * envelope that originally wrapped it (CC-4). Populated during slide
26731
+ * (and `p:grpSp`) parsing; consulted at save time to re-emit the
26732
+ * original `<mc:Choice>` / `<mc:Fallback>` shape so legacy renderers
26733
+ * keep their fallback content.
26734
+ *
26735
+ * Multiple sibling elements may share the same `AlternateContentBlock`
26736
+ * value (a single AC envelope often wraps several child shapes — e.g.
26737
+ * `p14:media` + its `p:pic` fallback nest one each). WeakMap so AC
26738
+ * envelopes are GC'd if the parsed XmlObject is dropped.
26739
+ */
26740
+ alternateContentBlockByRawXml = /* @__PURE__ */ new WeakMap();
25906
26741
  /** Embedded fonts extracted during load, preserved for automatic re-embedding on save. */
25907
26742
  loadedEmbeddedFonts = [];
25908
26743
  /** Map of comment author IDs to display names (from `ppt/commentAuthors.xml`). */
@@ -27716,7 +28551,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
27716
28551
  const bgColor = this.parseBackgroundColor(bg);
27717
28552
  const spTree = master["p:cSld"]?.["p:spTree"];
27718
28553
  const placeholders = this.extractPlaceholderList(spTree);
27719
- return { path, backgroundColor: bgColor, placeholders };
28554
+ const result = { path, backgroundColor: bgColor, placeholders };
28555
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
28556
+ if (hf) {
28557
+ result.headerFooter = hf;
28558
+ }
28559
+ return result;
27720
28560
  } catch (e) {
27721
28561
  console.warn("Failed to parse handout master:", e);
27722
28562
  return void 0;
@@ -27742,7 +28582,12 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
27742
28582
  const bgColor = this.parseBackgroundColor(bg);
27743
28583
  const spTree = master["p:cSld"]?.["p:spTree"];
27744
28584
  const placeholders = this.extractPlaceholderList(spTree);
27745
- return { path, backgroundColor: bgColor, placeholders };
28585
+ const result = { path, backgroundColor: bgColor, placeholders };
28586
+ const hf = parseHeaderFooterFlags(master["p:hf"]);
28587
+ if (hf) {
28588
+ result.headerFooter = hf;
28589
+ }
28590
+ return result;
27746
28591
  } catch (e) {
27747
28592
  console.warn("Failed to parse notes master:", e);
27748
28593
  return void 0;
@@ -27787,6 +28632,10 @@ var PptxHandlerRuntime9 = class extends PptxHandlerRuntime8 {
27787
28632
  const uVal = String(userDrawn).trim().toLowerCase();
27788
28633
  layout.userDrawn = uVal === "1" || uVal === "true";
27789
28634
  }
28635
+ const hf = parseHeaderFooterFlags(sldLayout["p:hf"]);
28636
+ if (hf) {
28637
+ layout.headerFooter = hf;
28638
+ }
27790
28639
  const clrMapOvr = sldLayout["p:clrMapOvr"];
27791
28640
  if (clrMapOvr && clrMapOvr["a:masterClrMapping"] === void 0) {
27792
28641
  const overrideNode = clrMapOvr["a:overrideClrMapping"];
@@ -28392,6 +29241,18 @@ function buildClrChangeNode(style) {
28392
29241
  }
28393
29242
 
28394
29243
  // src/core/core/runtime/PptxHandlerRuntimeSaveRunProperties.ts
29244
+ function applyFontMetadata(fontNode, panose, pitchFamily, charset) {
29245
+ if (panose && panose.length > 0) {
29246
+ fontNode["@_panose"] = panose;
29247
+ }
29248
+ if (typeof pitchFamily === "number" && Number.isFinite(pitchFamily)) {
29249
+ fontNode["@_pitchFamily"] = String(pitchFamily);
29250
+ }
29251
+ if (typeof charset === "number" && Number.isFinite(charset)) {
29252
+ fontNode["@_charset"] = String(charset);
29253
+ }
29254
+ return fontNode;
29255
+ }
28395
29256
  var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime11 {
28396
29257
  createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId) {
28397
29258
  const runProps = {
@@ -28449,6 +29310,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
28449
29310
  if (style.bookmark) {
28450
29311
  runProps["@_bmk"] = style.bookmark;
28451
29312
  }
29313
+ if (style.altLanguage) {
29314
+ runProps["@_altLang"] = style.altLanguage;
29315
+ }
29316
+ if (typeof style.smartTagId === "number" && Number.isFinite(style.smartTagId)) {
29317
+ runProps["@_smtId"] = String(style.smartTagId);
29318
+ }
28452
29319
  if (style.textOutlineWidth || style.textOutlineColor) {
28453
29320
  const lnObj = {};
28454
29321
  if (typeof style.textOutlineWidth === "number" && style.textOutlineWidth > 0) {
@@ -28464,11 +29331,12 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
28464
29331
  runProps["a:ln"] = lnObj;
28465
29332
  }
28466
29333
  if (style.color) {
28467
- runProps["a:solidFill"] = {
28468
- "a:srgbClr": {
28469
- "@_val": style.color.replace("#", "")
28470
- }
28471
- };
29334
+ const resolvedOriginalColor = style.colorXml ? this.parseColor(style.colorXml) : void 0;
29335
+ runProps["a:solidFill"] = serializeColorChoice(
29336
+ style.colorXml,
29337
+ resolvedOriginalColor,
29338
+ style.color
29339
+ );
28472
29340
  } else if (style.textFillGradientStops && style.textFillGradientStops.length > 0) {
28473
29341
  const gradStops = style.textFillGradientStops.filter((stop) => Boolean(stop?.color)).map((stop) => {
28474
29342
  const rawPos = (stop.position ?? 0) / 100;
@@ -28541,16 +29409,32 @@ var PptxHandlerRuntime12 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
28541
29409
  };
28542
29410
  }
28543
29411
  if (style.fontFamily) {
28544
- runProps["a:latin"] = { "@_typeface": style.fontFamily };
28545
- runProps["a:ea"] = {
28546
- "@_typeface": style.eastAsiaFont || style.fontFamily
28547
- };
28548
- runProps["a:cs"] = {
28549
- "@_typeface": style.complexScriptFont || style.fontFamily
28550
- };
29412
+ runProps["a:latin"] = applyFontMetadata(
29413
+ { "@_typeface": style.fontFamily },
29414
+ style.latinFontPanose,
29415
+ style.latinFontPitchFamily,
29416
+ style.latinFontCharset
29417
+ );
29418
+ runProps["a:ea"] = applyFontMetadata(
29419
+ { "@_typeface": style.eastAsiaFont || style.fontFamily },
29420
+ style.eastAsiaFontPanose,
29421
+ style.eastAsiaFontPitchFamily,
29422
+ style.eastAsiaFontCharset
29423
+ );
29424
+ runProps["a:cs"] = applyFontMetadata(
29425
+ { "@_typeface": style.complexScriptFont || style.fontFamily },
29426
+ style.complexScriptFontPanose,
29427
+ style.complexScriptFontPitchFamily,
29428
+ style.complexScriptFontCharset
29429
+ );
28551
29430
  }
28552
29431
  if (style.symbolFont) {
28553
- runProps["a:sym"] = { "@_typeface": style.symbolFont };
29432
+ runProps["a:sym"] = applyFontMetadata(
29433
+ { "@_typeface": style.symbolFont },
29434
+ style.symbolFontPanose,
29435
+ style.symbolFontPitchFamily,
29436
+ style.symbolFontCharset
29437
+ );
28554
29438
  }
28555
29439
  if (style.hyperlink && resolveHyperlinkRelationshipId) {
28556
29440
  const hyperlinkTarget = String(style.hyperlink).trim();
@@ -28625,14 +29509,15 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
28625
29509
  lineSpacing: this.createLineSpacingXmlFromMultiplier(textStyle?.lineSpacing),
28626
29510
  lineSpacingExactPt: textStyle?.lineSpacingExactPt
28627
29511
  };
28628
- const createParagraph = (runs, bulletInfo) => {
29512
+ const createParagraph = (runs, bulletInfo, level, endParaRunProperties) => {
28629
29513
  const paragraphProps = buildParagraphPropertiesXml(
28630
29514
  textStyle,
28631
29515
  paragraphAlign,
28632
29516
  bulletInfo,
28633
- spacing
29517
+ spacing,
29518
+ level
28634
29519
  );
28635
- return assembleParagraphXml(runs, paragraphProps);
29520
+ return assembleParagraphXml(runs, paragraphProps, endParaRunProperties);
28636
29521
  };
28637
29522
  const createRun = (runText, style) => ({
28638
29523
  "a:rPr": this.createRunPropertiesFromTextStyle(style, resolveHyperlinkRelationshipId),
@@ -28680,13 +29565,19 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
28680
29565
  const paragraphs = [];
28681
29566
  let currentRuns = [];
28682
29567
  let currentBulletInfo;
29568
+ let currentLevel;
29569
+ let currentEndParaRunProperties;
28683
29570
  const pushParagraph = () => {
28684
29571
  if (currentRuns.length === 0) {
28685
29572
  currentRuns.push(createRun("", textStyle));
28686
29573
  }
28687
- paragraphs.push(createParagraph(currentRuns, currentBulletInfo));
29574
+ paragraphs.push(
29575
+ createParagraph(currentRuns, currentBulletInfo, currentLevel, currentEndParaRunProperties)
29576
+ );
28688
29577
  currentRuns = [];
28689
29578
  currentBulletInfo = void 0;
29579
+ currentLevel = void 0;
29580
+ currentEndParaRunProperties = void 0;
28690
29581
  };
28691
29582
  if (textSegments && textSegments.length > 0) {
28692
29583
  const uniformSegmentOverrides = computeUniformSegmentOverrides(textStyle, textSegments);
@@ -28698,8 +29589,16 @@ var PptxHandlerRuntime13 = class extends PptxHandlerRuntime12 {
28698
29589
  };
28699
29590
  const segmentText = String(segment.text ?? "");
28700
29591
  const lineParts = segmentText.split("\n");
28701
- if (currentRuns.length === 0 && segment.bulletInfo) {
28702
- currentBulletInfo = segment.bulletInfo;
29592
+ if (currentRuns.length === 0) {
29593
+ if (segment.bulletInfo) {
29594
+ currentBulletInfo = segment.bulletInfo;
29595
+ }
29596
+ if (segment.paragraphLevel !== void 0) {
29597
+ currentLevel = segment.paragraphLevel;
29598
+ }
29599
+ if (segment.endParaRunProperties) {
29600
+ currentEndParaRunProperties = segment.endParaRunProperties;
29601
+ }
28703
29602
  }
28704
29603
  lineParts.forEach((linePart, lineIndex) => {
28705
29604
  if (segment.rubyText !== void 0) {
@@ -28797,7 +29696,201 @@ var PptxHandlerRuntime14 = class extends PptxHandlerRuntime13 {
28797
29696
  };
28798
29697
 
28799
29698
  // src/core/core/runtime/PptxHandlerRuntimeSaveShapeXml.ts
29699
+ var OLE_OBJECT_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject";
29700
+ var IMAGE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
29701
+ var OLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/presentationml/2006/ole";
28800
29702
  var PptxHandlerRuntime15 = class _PptxHandlerRuntime extends PptxHandlerRuntime14 {
29703
+ /**
29704
+ * Build a `p:graphicFrame` XML skeleton for an SDK-created table.
29705
+ *
29706
+ * Tables round-trip as `<p:graphicFrame>/<a:graphic>/<a:graphicData
29707
+ * uri=".../drawingml/2006/table">/<a:tbl>` inside `p:spTree`. When the
29708
+ * element was loaded from an existing file, `el.rawXml` already contains
29709
+ * this envelope and the downstream `serializeTableDataToXml` path
29710
+ * populates cells in place. When the element was created via the SDK
29711
+ * (`SlideBuilder.addTable`), there is no `rawXml`, so this method
29712
+ * fabricates a minimal envelope with an empty `a:tbl`. The element
29713
+ * writer then calls `serializeTableDataToXml`, which triggers
29714
+ * `rebuildTableXmlFromData` and fills in `a:tblGrid` / `a:tr` children.
29715
+ */
29716
+ createTableGraphicFrameXml(el) {
29717
+ const EMU = _PptxHandlerRuntime.EMU_PER_PX;
29718
+ const offX = String(Math.round(el.x * EMU));
29719
+ const offY = String(Math.round(el.y * EMU));
29720
+ const extCx = String(Math.round(Math.max(el.width, 1) * EMU));
29721
+ const extCy = String(Math.round(Math.max(el.height, 1) * EMU));
29722
+ const tblPr = {
29723
+ "@_firstRow": el.tableData?.firstRowHeader ? "1" : "0",
29724
+ "@_bandRow": el.tableData?.bandedRows ? "1" : "0"
29725
+ };
29726
+ if (el.tableData?.tableStyleId) {
29727
+ tblPr["a:tableStyleId"] = el.tableData.tableStyleId;
29728
+ }
29729
+ return {
29730
+ "p:nvGraphicFramePr": {
29731
+ "p:cNvPr": { "@_id": "0", "@_name": el.name || "Table" },
29732
+ "p:cNvGraphicFramePr": {
29733
+ "a:graphicFrameLocks": { "@_noGrp": "1" }
29734
+ },
29735
+ "p:nvPr": {}
29736
+ },
29737
+ "p:xfrm": {
29738
+ "a:off": { "@_x": offX, "@_y": offY },
29739
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
29740
+ },
29741
+ "a:graphic": {
29742
+ "a:graphicData": {
29743
+ "@_uri": "http://schemas.openxmlformats.org/drawingml/2006/table",
29744
+ "a:tbl": {
29745
+ "a:tblPr": tblPr,
29746
+ "a:tblGrid": {}
29747
+ }
29748
+ }
29749
+ }
29750
+ };
29751
+ }
29752
+ /**
29753
+ * Build a `p:graphicFrame` XML skeleton for an OLE object element.
29754
+ *
29755
+ * Used both for SDK-created OLE elements (no `rawXml`) and to refresh
29756
+ * a few key attributes on a loaded element when the typed fields have
29757
+ * been mutated. The output is the canonical
29758
+ * `p:graphicFrame > a:graphic > a:graphicData uri="…/ole" > p:oleObj`
29759
+ * shape per ECMA-376 §19.3.1.34 / §13.3.4.
29760
+ *
29761
+ * The caller (`processSlideElement`) is responsible for ensuring the
29762
+ * embed / preview-image relationships referenced from `r:id` / `r:embed`
29763
+ * exist in the slide's rels file. This method does not register them
29764
+ * itself because the typed model does not currently carry the binary
29765
+ * payload — the binary part must already be in the package (loaded from
29766
+ * the original file). A fully-fabricated SDK OLE element therefore
29767
+ * still requires the consumer to attach the binary out-of-band; this
29768
+ * method simply emits a schema-valid envelope referencing the
29769
+ * specified relationship ID.
29770
+ */
29771
+ createOleGraphicFrameXml(el, embedRelationshipId) {
29772
+ const EMU = _PptxHandlerRuntime.EMU_PER_PX;
29773
+ const offX = String(Math.round(el.x * EMU));
29774
+ const offY = String(Math.round(el.y * EMU));
29775
+ const extCx = String(Math.round(Math.max(el.width, 1) * EMU));
29776
+ const extCy = String(Math.round(Math.max(el.height, 1) * EMU));
29777
+ const oleObj = {
29778
+ "@_showAsIcon": "0",
29779
+ "@_imgW": extCx,
29780
+ "@_imgH": extCy
29781
+ };
29782
+ if (el.oleProgId) {
29783
+ oleObj["@_progId"] = el.oleProgId;
29784
+ }
29785
+ if (el.oleName) {
29786
+ oleObj["@_name"] = el.oleName;
29787
+ }
29788
+ if (el.oleClsId) {
29789
+ oleObj["@_classid"] = el.oleClsId;
29790
+ }
29791
+ if (embedRelationshipId) {
29792
+ oleObj["@_r:id"] = embedRelationshipId;
29793
+ }
29794
+ if (el.isLinked) {
29795
+ oleObj["p:link"] = {
29796
+ "@_r:id": embedRelationshipId,
29797
+ "@_updateAutomatic": "1"
29798
+ };
29799
+ } else {
29800
+ oleObj["p:embed"] = {};
29801
+ }
29802
+ oleObj["p:pic"] = {
29803
+ "p:nvPicPr": {
29804
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || "OleObject" },
29805
+ "p:cNvPicPr": {},
29806
+ "p:nvPr": {}
29807
+ },
29808
+ "p:blipFill": {
29809
+ "a:blip": {},
29810
+ "a:stretch": { "a:fillRect": {} }
29811
+ },
29812
+ "p:spPr": {
29813
+ "a:xfrm": {
29814
+ "a:off": { "@_x": offX, "@_y": offY },
29815
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
29816
+ },
29817
+ "a:prstGeom": { "@_prst": "rect", "a:avLst": {} }
29818
+ }
29819
+ };
29820
+ return {
29821
+ "p:nvGraphicFramePr": {
29822
+ "p:cNvPr": { "@_id": "0", "@_name": el.oleName || el.fileName || "OleObject" },
29823
+ "p:cNvGraphicFramePr": {
29824
+ "a:graphicFrameLocks": { "@_noChangeAspect": "1" }
29825
+ },
29826
+ "p:nvPr": {}
29827
+ },
29828
+ "p:xfrm": {
29829
+ "a:off": { "@_x": offX, "@_y": offY },
29830
+ "a:ext": { "@_cx": extCx, "@_cy": extCy }
29831
+ },
29832
+ "a:graphic": {
29833
+ "a:graphicData": {
29834
+ "@_uri": OLE_GRAPHIC_DATA_URI,
29835
+ "p:oleObj": oleObj
29836
+ }
29837
+ }
29838
+ };
29839
+ }
29840
+ /**
29841
+ * Refresh editable typed-field attributes on a loaded OLE graphicFrame's
29842
+ * raw XML. Only attributes that round-trip through the typed model
29843
+ * (`progId`, `name`, `classid`) are touched so unknown extension data
29844
+ * passes through verbatim.
29845
+ */
29846
+ applyOleTypedFieldUpdates(shape, el) {
29847
+ const oleObj = shape["a:graphic"]?.["a:graphicData"]?.["p:oleObj"];
29848
+ if (!oleObj) {
29849
+ return;
29850
+ }
29851
+ if (el.oleProgId) {
29852
+ oleObj["@_progId"] = el.oleProgId;
29853
+ }
29854
+ if (el.oleName !== void 0) {
29855
+ if (el.oleName.length > 0) {
29856
+ oleObj["@_name"] = el.oleName;
29857
+ } else {
29858
+ delete oleObj["@_name"];
29859
+ }
29860
+ }
29861
+ if (el.oleClsId) {
29862
+ oleObj["@_classid"] = el.oleClsId;
29863
+ }
29864
+ }
29865
+ /** Look up the existing OLE binary relationship ID for this slide, if any. */
29866
+ resolveOleEmbedRelationshipId(slideRelationships, oleTarget) {
29867
+ if (!oleTarget) {
29868
+ return void 0;
29869
+ }
29870
+ const normalisedTarget = oleTarget.replace(/^ppt\//, "../").replace(/^\/+/, "");
29871
+ const lowerTarget = normalisedTarget.toLowerCase();
29872
+ for (const rel of slideRelationships) {
29873
+ const relType = String(rel?.["@_Type"] || "");
29874
+ if (relType !== OLE_OBJECT_RELATIONSHIP_TYPE) {
29875
+ continue;
29876
+ }
29877
+ const target = String(rel?.["@_Target"] || "").toLowerCase().trim();
29878
+ if (target === lowerTarget || target.endsWith(lowerTarget) || lowerTarget.endsWith(target)) {
29879
+ const relId = String(rel?.["@_Id"] || "").trim();
29880
+ if (relId.length > 0) {
29881
+ return relId;
29882
+ }
29883
+ }
29884
+ }
29885
+ const fallback = slideRelationships.find(
29886
+ (rel) => String(rel?.["@_Type"] || "") === OLE_OBJECT_RELATIONSHIP_TYPE
29887
+ );
29888
+ const fallbackId = String(fallback?.["@_Id"] || "").trim();
29889
+ return fallbackId.length > 0 ? fallbackId : void 0;
29890
+ }
29891
+ /** Constants are exposed so the element-writer mixin can reuse them. */
29892
+ static OLE_OBJECT_RELATIONSHIP_TYPE = OLE_OBJECT_RELATIONSHIP_TYPE;
29893
+ static OLE_IMAGE_RELATIONSHIP_TYPE = IMAGE_RELATIONSHIP_TYPE;
28801
29894
  /**
28802
29895
  * Build a p:sp XML object for an ink annotation element.
28803
29896
  * Each ink path becomes a separate a:path within a:pathLst,
@@ -29114,6 +30207,9 @@ var PptxHandlerRuntime16 = class extends PptxHandlerRuntime15 {
29114
30207
  buildOuterShadowXml(shapeStyle) {
29115
30208
  return this.colorStyleCodec.buildOuterShadowXml(shapeStyle);
29116
30209
  }
30210
+ buildPresetShadowXml(shapeStyle) {
30211
+ return this.colorStyleCodec.buildPresetShadowXml(shapeStyle);
30212
+ }
29117
30213
  buildInnerShadowXml(shapeStyle) {
29118
30214
  return this.colorStyleCodec.buildInnerShadowXml(shapeStyle);
29119
30215
  }
@@ -29664,6 +30760,10 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
29664
30760
  const solidFill = runProperties["a:solidFill"];
29665
30761
  if (solidFill) {
29666
30762
  style.color = this.parseColor(solidFill);
30763
+ const colorXml = extractColorChoiceXml(solidFill);
30764
+ if (colorXml) {
30765
+ style.colorXml = colorXml;
30766
+ }
29667
30767
  }
29668
30768
  this.applyHyperlinkStyle(style, runProperties, relationshipMap);
29669
30769
  const capAttr = String(runProperties["@_cap"] || "").trim().toLowerCase();
@@ -29711,12 +30811,86 @@ var PptxHandlerRuntime19 = class _PptxHandlerRuntime extends PptxHandlerRuntime1
29711
30811
  if (bmk) {
29712
30812
  style.bookmark = bmk;
29713
30813
  }
30814
+ const altLang = String(runProperties["@_altLang"] || "").trim();
30815
+ if (altLang) {
30816
+ style.altLanguage = altLang;
30817
+ }
30818
+ if (runProperties["@_smtId"] !== void 0) {
30819
+ const smtIdRaw = Number.parseInt(String(runProperties["@_smtId"]), 10);
30820
+ if (Number.isFinite(smtIdRaw)) {
30821
+ style.smartTagId = smtIdRaw;
30822
+ }
30823
+ }
30824
+ this.applyTextFontMetadata(style, latin, "latin");
30825
+ this.applyTextFontMetadata(style, eastAsian, "eastAsia");
30826
+ this.applyTextFontMetadata(style, complexScript, "complexScript");
30827
+ this.applyTextFontMetadata(style, runProperties["a:sym"], "symbol");
29714
30828
  const runEffectList = runProperties["a:effectLst"];
29715
30829
  if (runEffectList) {
29716
30830
  this.applyTextRunEffects(style, runEffectList);
29717
30831
  }
29718
30832
  return style;
29719
30833
  }
30834
+ /**
30835
+ * Copy `@panose` / `@pitchFamily` / `@charset` from a font child node
30836
+ * (`a:latin`, `a:ea`, `a:cs`, `a:sym`) onto the matching `*Font*`
30837
+ * fields of `style`.
30838
+ */
30839
+ applyTextFontMetadata(style, fontNode, kind) {
30840
+ if (!fontNode) {
30841
+ return;
30842
+ }
30843
+ const panose = String(fontNode["@_panose"] || "").trim();
30844
+ const pitchRaw = fontNode["@_pitchFamily"];
30845
+ const charsetRaw = fontNode["@_charset"];
30846
+ const pitch = pitchRaw !== void 0 && pitchRaw !== null ? Number.parseInt(String(pitchRaw), 10) : void 0;
30847
+ const charset = charsetRaw !== void 0 && charsetRaw !== null ? Number.parseInt(String(charsetRaw), 10) : void 0;
30848
+ if (kind === "latin") {
30849
+ if (panose) {
30850
+ style.latinFontPanose = panose;
30851
+ }
30852
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
30853
+ style.latinFontPitchFamily = pitch;
30854
+ }
30855
+ if (typeof charset === "number" && Number.isFinite(charset)) {
30856
+ style.latinFontCharset = charset;
30857
+ }
30858
+ return;
30859
+ }
30860
+ if (kind === "eastAsia") {
30861
+ if (panose) {
30862
+ style.eastAsiaFontPanose = panose;
30863
+ }
30864
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
30865
+ style.eastAsiaFontPitchFamily = pitch;
30866
+ }
30867
+ if (typeof charset === "number" && Number.isFinite(charset)) {
30868
+ style.eastAsiaFontCharset = charset;
30869
+ }
30870
+ return;
30871
+ }
30872
+ if (kind === "complexScript") {
30873
+ if (panose) {
30874
+ style.complexScriptFontPanose = panose;
30875
+ }
30876
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
30877
+ style.complexScriptFontPitchFamily = pitch;
30878
+ }
30879
+ if (typeof charset === "number" && Number.isFinite(charset)) {
30880
+ style.complexScriptFontCharset = charset;
30881
+ }
30882
+ return;
30883
+ }
30884
+ if (panose) {
30885
+ style.symbolFontPanose = panose;
30886
+ }
30887
+ if (typeof pitch === "number" && Number.isFinite(pitch)) {
30888
+ style.symbolFontPitchFamily = pitch;
30889
+ }
30890
+ if (typeof charset === "number" && Number.isFinite(charset)) {
30891
+ style.symbolFontCharset = charset;
30892
+ }
30893
+ }
29720
30894
  };
29721
30895
 
29722
30896
  // src/core/core/runtime/PptxHandlerRuntimeTextEditing.ts
@@ -30152,7 +31326,7 @@ var PptxHandlerRuntime21 = class extends PptxHandlerRuntime20 {
30152
31326
  };
30153
31327
 
30154
31328
  // src/core/core/runtime/table-cell-save-helpers.ts
30155
- function writeCellFill(tcPr, style) {
31329
+ function writeCellFill(tcPr, style, resolveColorXml) {
30156
31330
  if (style.fillMode === "gradient" && style.gradientFillStops && style.gradientFillStops.length > 0) {
30157
31331
  delete tcPr["a:solidFill"];
30158
31332
  delete tcPr["a:pattFill"];
@@ -30231,11 +31405,12 @@ function writeCellFill(tcPr, style) {
30231
31405
  } else if (style.backgroundColor) {
30232
31406
  delete tcPr["a:gradFill"];
30233
31407
  delete tcPr["a:pattFill"];
30234
- tcPr["a:solidFill"] = {
30235
- "a:srgbClr": {
30236
- "@_val": style.backgroundColor.replace("#", "")
30237
- }
30238
- };
31408
+ const resolvedOriginal = style.backgroundColorXml && resolveColorXml ? resolveColorXml(style.backgroundColorXml) : void 0;
31409
+ tcPr["a:solidFill"] = serializeColorChoice(
31410
+ style.backgroundColorXml,
31411
+ resolvedOriginal,
31412
+ style.backgroundColor
31413
+ );
30239
31414
  }
30240
31415
  }
30241
31416
  function writeDiagonalBorders(tcPr, style, emuPerPx) {
@@ -30279,14 +31454,15 @@ function writeCellTextFormatting(xmlCell, style, ensureArray6) {
30279
31454
  const paragraphs = ensureArray6(xmlCell["a:txBody"]?.["a:p"]);
30280
31455
  for (const paragraph of paragraphs) {
30281
31456
  const runs = ensureArray6(paragraph?.["a:r"]);
31457
+ const rebuiltRuns = [];
31458
+ let runsChanged = false;
30282
31459
  for (const run of runs) {
30283
31460
  if (!run) {
31461
+ rebuiltRuns.push(run);
30284
31462
  continue;
30285
31463
  }
30286
- if (!run["a:rPr"]) {
30287
- run["a:rPr"] = {};
30288
- }
30289
- const rPr = run["a:rPr"];
31464
+ const existingRPr = run["a:rPr"] ?? {};
31465
+ const rPr = { ...existingRPr };
30290
31466
  if (style.bold !== void 0) {
30291
31467
  rPr["@_b"] = style.bold ? "1" : "0";
30292
31468
  }
@@ -30306,6 +31482,18 @@ function writeCellTextFormatting(xmlCell, style, ensureArray6) {
30306
31482
  }
30307
31483
  };
30308
31484
  }
31485
+ const rebuilt = { "a:rPr": rPr };
31486
+ for (const key of Object.keys(run)) {
31487
+ if (key === "a:rPr") {
31488
+ continue;
31489
+ }
31490
+ rebuilt[key] = run[key];
31491
+ }
31492
+ rebuiltRuns.push(rebuilt);
31493
+ runsChanged = true;
31494
+ }
31495
+ if (runsChanged && rebuiltRuns.length > 0) {
31496
+ paragraph["a:r"] = rebuiltRuns.length === 1 ? rebuiltRuns[0] : rebuiltRuns;
30309
31497
  }
30310
31498
  }
30311
31499
  }
@@ -30327,6 +31515,9 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30327
31515
  const existingParagraphs = this.ensureArray(txBody["a:p"]);
30328
31516
  const firstRPr = this.ensureArray(existingParagraphs[0]?.["a:r"])[0]?.["a:rPr"];
30329
31517
  const firstPPr = existingParagraphs[0]?.["a:pPr"];
31518
+ const firstEndParaRPr = existingParagraphs[0]?.["a:endParaRPr"];
31519
+ const rPrForRun = firstRPr ? { ...firstRPr } : { "@_lang": "en-US", "@_dirty": "0" };
31520
+ const endParaRPr = firstEndParaRPr ? { ...firstEndParaRPr } : { "@_lang": "en-US", "@_dirty": "0" };
30330
31521
  const lines = text.split("\n");
30331
31522
  const paragraphs = lines.map((line) => {
30332
31523
  const paragraph = {};
@@ -30334,9 +31525,10 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30334
31525
  paragraph["a:pPr"] = firstPPr;
30335
31526
  }
30336
31527
  paragraph["a:r"] = {
30337
- ...firstRPr ? { "a:rPr": firstRPr } : {},
31528
+ "a:rPr": rPrForRun,
30338
31529
  "a:t": line
30339
31530
  };
31531
+ paragraph["a:endParaRPr"] = endParaRPr;
30340
31532
  return paragraph;
30341
31533
  });
30342
31534
  txBody["a:p"] = paragraphs.length === 1 ? paragraphs[0] : paragraphs;
@@ -30349,7 +31541,7 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30349
31541
  xmlCell["a:tcPr"] = {};
30350
31542
  }
30351
31543
  const tcPr = xmlCell["a:tcPr"];
30352
- writeCellFill(tcPr, style);
31544
+ writeCellFill(tcPr, style, (colorXml) => this.parseColor(colorXml));
30353
31545
  if (style.vAlign) {
30354
31546
  const vAlignMap = {
30355
31547
  top: "t",
@@ -30431,35 +31623,29 @@ var PptxHandlerRuntime22 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30431
31623
  }
30432
31624
  }
30433
31625
  }
30434
- if (style.marginLeft !== void 0 || style.marginRight !== void 0 || style.marginTop !== void 0 || style.marginBottom !== void 0) {
30435
- const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
30436
- if (!tcPr["a:tcMar"]) {
30437
- tcPr["a:tcMar"] = {};
30438
- }
30439
- const tcMar = tcPr["a:tcMar"];
30440
- if (style.marginLeft !== void 0) {
30441
- tcMar["a:marL"] = {
30442
- "@_w": String(Math.round(style.marginLeft * emuPerPx))
30443
- };
30444
- }
30445
- if (style.marginRight !== void 0) {
30446
- tcMar["a:marR"] = {
30447
- "@_w": String(Math.round(style.marginRight * emuPerPx))
30448
- };
30449
- }
30450
- if (style.marginTop !== void 0) {
30451
- tcMar["a:marT"] = {
30452
- "@_w": String(Math.round(style.marginTop * emuPerPx))
30453
- };
30454
- }
30455
- if (style.marginBottom !== void 0) {
30456
- tcMar["a:marB"] = {
30457
- "@_w": String(Math.round(style.marginBottom * emuPerPx))
30458
- };
30459
- }
31626
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
31627
+ if (style.marginLeft !== void 0) {
31628
+ tcPr["@_marL"] = String(Math.round(style.marginLeft * emuPerPx));
31629
+ }
31630
+ if (style.marginRight !== void 0) {
31631
+ tcPr["@_marR"] = String(Math.round(style.marginRight * emuPerPx));
31632
+ }
31633
+ if (style.marginTop !== void 0) {
31634
+ tcPr["@_marT"] = String(Math.round(style.marginTop * emuPerPx));
30460
31635
  }
31636
+ if (style.marginBottom !== void 0) {
31637
+ tcPr["@_marB"] = String(Math.round(style.marginBottom * emuPerPx));
31638
+ }
31639
+ delete tcPr["a:tcMar"];
30461
31640
  writeDiagonalBorders(tcPr, style, _PptxHandlerRuntime.EMU_PER_PX);
30462
31641
  writeCellTextFormatting(xmlCell, style, this.ensureArray.bind(this));
31642
+ const reordered = reorderObjectKeys(tcPr, TC_PR_BORDERS_ORDER);
31643
+ for (const key of Object.keys(tcPr)) {
31644
+ delete tcPr[key];
31645
+ }
31646
+ for (const key of Object.keys(reordered)) {
31647
+ tcPr[key] = reordered[key];
31648
+ }
30463
31649
  }
30464
31650
  };
30465
31651
 
@@ -30486,14 +31672,27 @@ function serializeCellMergeAttributes(xmlCell, cell) {
30486
31672
  delete xmlCell["@_vMerge"];
30487
31673
  }
30488
31674
  }
31675
+ var DEFAULT_POWERPOINT_TABLE_STYLE_ID = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
30489
31676
  function serializeTablePropertyFlags(tbl, tableData) {
30490
31677
  const tblPr = tbl["a:tblPr"] ?? {};
30491
- tblPr["@_bandRow"] = tableData.bandedRows ? "1" : "0";
30492
- tblPr["@_bandCol"] = tableData.bandedColumns ? "1" : "0";
30493
- tblPr["@_firstRow"] = tableData.firstRowHeader ? "1" : "0";
30494
- tblPr["@_lastRow"] = tableData.lastRow ? "1" : "0";
30495
- tblPr["@_firstCol"] = tableData.firstCol ? "1" : "0";
30496
- tblPr["@_lastCol"] = tableData.lastCol ? "1" : "0";
31678
+ const setOrDelete = (key, truthy) => {
31679
+ if (truthy) {
31680
+ tblPr[key] = "1";
31681
+ } else {
31682
+ delete tblPr[key];
31683
+ }
31684
+ };
31685
+ setOrDelete("@_bandRow", tableData.bandedRows);
31686
+ setOrDelete("@_bandCol", tableData.bandedColumns);
31687
+ setOrDelete("@_firstRow", tableData.firstRowHeader);
31688
+ setOrDelete("@_lastRow", tableData.lastRow);
31689
+ setOrDelete("@_firstCol", tableData.firstCol);
31690
+ setOrDelete("@_lastCol", tableData.lastCol);
31691
+ if (tableData.tableStyleId) {
31692
+ tblPr["a:tableStyleId"] = tableData.tableStyleId;
31693
+ } else if (!tblPr["a:tableStyleId"]) {
31694
+ tblPr["a:tableStyleId"] = DEFAULT_POWERPOINT_TABLE_STYLE_ID;
31695
+ }
30497
31696
  tbl["a:tblPr"] = tblPr;
30498
31697
  }
30499
31698
  function replaceFirstTextValueInTree(node, localName, newValue, getXmlLocalName) {
@@ -30538,7 +31737,9 @@ function createDefaultXmlCell() {
30538
31737
  "a:bodyPr": {},
30539
31738
  "a:lstStyle": {},
30540
31739
  "a:p": {
30541
- "a:endParaRPr": { "@_lang": "en-US" }
31740
+ // Match PowerPoint's "Insert Table" default: every paragraph-end
31741
+ // run carries `lang="en-US" dirty="0"`.
31742
+ "a:endParaRPr": { "@_lang": "en-US", "@_dirty": "0" }
30542
31743
  }
30543
31744
  },
30544
31745
  "a:tcPr": {}
@@ -30546,6 +31747,54 @@ function createDefaultXmlCell() {
30546
31747
  }
30547
31748
 
30548
31749
  // src/core/core/runtime/table-xml-rebuild.ts
31750
+ var GRID_COL_ID_EXT_URI = "{9D8B030D-6E8A-4147-A177-3AD203B41FA5}";
31751
+ var A16_NAMESPACE = "http://schemas.microsoft.com/office/drawing/2014/main";
31752
+ function ensureA16NamespaceOnSlideRoot(slideRoot) {
31753
+ if (!slideRoot["@_xmlns:a16"]) {
31754
+ slideRoot["@_xmlns:a16"] = A16_NAMESPACE;
31755
+ }
31756
+ if (!slideRoot["@_xmlns:mc"]) {
31757
+ slideRoot["@_xmlns:mc"] = "http://schemas.openxmlformats.org/markup-compatibility/2006";
31758
+ }
31759
+ const existingIgnorable = String(slideRoot["@_mc:Ignorable"] || "").trim();
31760
+ if (existingIgnorable.length === 0) {
31761
+ slideRoot["@_mc:Ignorable"] = "a16";
31762
+ return;
31763
+ }
31764
+ const tokens = existingIgnorable.split(/\s+/).filter((token) => token.length > 0);
31765
+ if (!tokens.includes("a16")) {
31766
+ tokens.push("a16");
31767
+ slideRoot["@_mc:Ignorable"] = tokens.join(" ");
31768
+ }
31769
+ }
31770
+ function slideContainsA16Element(node) {
31771
+ if (node === null || node === void 0) {
31772
+ return false;
31773
+ }
31774
+ if (Array.isArray(node)) {
31775
+ for (const entry of node) {
31776
+ if (slideContainsA16Element(entry)) {
31777
+ return true;
31778
+ }
31779
+ }
31780
+ return false;
31781
+ }
31782
+ if (typeof node !== "object") {
31783
+ return false;
31784
+ }
31785
+ for (const [key, value] of Object.entries(node)) {
31786
+ if (key.startsWith("a16:")) {
31787
+ return true;
31788
+ }
31789
+ if (slideContainsA16Element(value)) {
31790
+ return true;
31791
+ }
31792
+ }
31793
+ return false;
31794
+ }
31795
+ function randomColumnId() {
31796
+ return String(Math.floor(Math.random() * 4294967295));
31797
+ }
30549
31798
  function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
30550
31799
  const existingXmlRows = ensureArrayFn(tbl["a:tr"]);
30551
31800
  const existingGridCols = ensureArrayFn(
@@ -30554,8 +31803,33 @@ function rebuildTableXmlFromData(tbl, tableData, emuPerPx, ensureArrayFn) {
30554
31803
  const totalWidthEmu = existingGridCols.reduce((sum, col) => {
30555
31804
  return sum + (parseInt(String(col?.["@_w"] || "0"), 10) || 0);
30556
31805
  }, 0) || 9144e3;
30557
- const newGridCols = tableData.columnWidths.map((w) => ({
30558
- "@_w": String(Math.round(w * totalWidthEmu))
31806
+ const existingColIds = existingGridCols.map((col) => {
31807
+ const extList = col?.["a:extLst"];
31808
+ const exts = Array.isArray(extList?.["a:ext"]) ? extList["a:ext"] : extList?.["a:ext"] ? [extList["a:ext"]] : [];
31809
+ for (const ext of exts) {
31810
+ if (ext?.["@_uri"] === GRID_COL_ID_EXT_URI) {
31811
+ const colId = ext["a16:colId"];
31812
+ const val = colId?.["@_val"];
31813
+ if (typeof val === "string" && val.length > 0) {
31814
+ return val;
31815
+ }
31816
+ }
31817
+ }
31818
+ return "";
31819
+ });
31820
+ const newGridCols = tableData.columnWidths.map((w, i) => ({
31821
+ "@_w": String(Math.round(w * totalWidthEmu)),
31822
+ "a:extLst": {
31823
+ "a:ext": {
31824
+ "@_uri": GRID_COL_ID_EXT_URI,
31825
+ // `xmlns:a16` is declared on the slide root by
31826
+ // `ensureA16NamespaceOnSlideRoot`; emitting it here too is
31827
+ // schema-redundant and PowerPoint flags it.
31828
+ "a16:colId": {
31829
+ "@_val": existingColIds[i] || randomColumnId()
31830
+ }
31831
+ }
31832
+ }
30559
31833
  }));
30560
31834
  if (!tbl["a:tblGrid"]) {
30561
31835
  tbl["a:tblGrid"] = {};
@@ -30910,26 +32184,64 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30910
32184
  continue;
30911
32185
  }
30912
32186
  const scalingNode = this.xmlLookupService.getChildByLocalName(axisNode, "scaling");
30913
- if (!scalingNode) {
30914
- continue;
32187
+ if (scalingNode) {
32188
+ if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
32189
+ const logBaseKey = Object.keys(scalingNode).find(
32190
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
32191
+ );
32192
+ if (logBaseKey) {
32193
+ scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
32194
+ } else {
32195
+ scalingNode["c:logBase"] = {
32196
+ "@_val": String(matchingAxis.logBase)
32197
+ };
32198
+ }
32199
+ } else if (matchingAxis.logScale === false) {
32200
+ const logBaseKey = Object.keys(scalingNode).find(
32201
+ (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
32202
+ );
32203
+ if (logBaseKey) {
32204
+ delete scalingNode[logBaseKey];
32205
+ }
32206
+ }
32207
+ this.upsertChartAxisChild(
32208
+ scalingNode,
32209
+ "min",
32210
+ matchingAxis.min !== void 0 ? String(matchingAxis.min) : void 0
32211
+ );
32212
+ this.upsertChartAxisChild(
32213
+ scalingNode,
32214
+ "max",
32215
+ matchingAxis.max !== void 0 ? String(matchingAxis.max) : void 0
32216
+ );
30915
32217
  }
30916
- if (matchingAxis.logBase !== void 0 && matchingAxis.logBase > 0) {
30917
- const logBaseKey = Object.keys(scalingNode).find(
30918
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
32218
+ if (matchingAxis.numFmt) {
32219
+ const numFmtKey = Object.keys(axisNode).find(
32220
+ (k) => this.compatibilityService.getXmlLocalName(k) === "numFmt"
30919
32221
  );
30920
- if (logBaseKey) {
30921
- scalingNode[logBaseKey]["@_val"] = String(matchingAxis.logBase);
32222
+ const numFmtAttrs = {
32223
+ "@_formatCode": matchingAxis.numFmt.formatCode,
32224
+ "@_sourceLinked": matchingAxis.numFmt.sourceLinked ? "1" : "0"
32225
+ };
32226
+ if (numFmtKey) {
32227
+ axisNode[numFmtKey] = numFmtAttrs;
30922
32228
  } else {
30923
- scalingNode["c:logBase"] = {
30924
- "@_val": String(matchingAxis.logBase)
30925
- };
32229
+ axisNode["c:numFmt"] = numFmtAttrs;
30926
32230
  }
30927
- } else if (matchingAxis.logScale === false) {
30928
- const logBaseKey = Object.keys(scalingNode).find(
30929
- (k) => this.compatibilityService.getXmlLocalName(k) === "logBase"
32231
+ }
32232
+ this.upsertChartAxisChild(
32233
+ axisNode,
32234
+ "majorUnit",
32235
+ matchingAxis.majorUnit !== void 0 ? String(matchingAxis.majorUnit) : void 0
32236
+ );
32237
+ if (matchingAxis.tickLblPos !== void 0) {
32238
+ const tickLblKey = Object.keys(axisNode).find(
32239
+ (k) => this.compatibilityService.getXmlLocalName(k) === "tickLblPos"
30930
32240
  );
30931
- if (logBaseKey) {
30932
- delete scalingNode[logBaseKey];
32241
+ if (tickLblKey) {
32242
+ axisNode[tickLblKey]["@_val"] = matchingAxis.tickLblPos;
32243
+ } else {
32244
+ axisNode["c:tickLblPos"] = { "@_val": matchingAxis.tickLblPos };
30933
32245
  }
30934
32246
  }
30935
32247
  }
@@ -30942,6 +32254,18 @@ var PptxHandlerRuntime23 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
30942
32254
  }
30943
32255
  this.pendingChartUpdates = void 0;
30944
32256
  }
32257
+ /**
32258
+ * Upsert a `c:<localName>` child with `@_val` on an axis or scaling node.
32259
+ * When `value` is undefined, removes any existing child of that local name.
32260
+ */
32261
+ upsertChartAxisChild(parent, localName, value) {
32262
+ upsertChartAxisChild(
32263
+ parent,
32264
+ localName,
32265
+ value,
32266
+ (key) => this.compatibilityService.getXmlLocalName(key)
32267
+ );
32268
+ }
30945
32269
  /**
30946
32270
  * Update the cached point values in a chart reference node
30947
32271
  * (numRef/strRef or numLit/strLit).
@@ -32221,17 +33545,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
32221
33545
  delete spPr["a:noFill"];
32222
33546
  delete spPr["a:gradFill"];
32223
33547
  delete spPr["a:blipFill"];
32224
- const solidFill = {
32225
- "a:srgbClr": {
32226
- "@_val": fillColor.replace("#", "")
32227
- }
32228
- };
32229
- if (typeof shapeStyle.fillOpacity === "number" && shapeStyle.fillOpacity >= 0 && shapeStyle.fillOpacity < 1) {
32230
- solidFill["a:srgbClr"]["a:alpha"] = {
32231
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.fillOpacity) * 1e5))
32232
- };
32233
- }
32234
- spPr["a:solidFill"] = solidFill;
33548
+ const resolvedOriginal = shapeStyle.fillColorXml ? this.parseColor(shapeStyle.fillColorXml) : void 0;
33549
+ spPr["a:solidFill"] = serializeColorChoice(
33550
+ shapeStyle.fillColorXml,
33551
+ resolvedOriginal,
33552
+ fillColor,
33553
+ shapeStyle.fillOpacity
33554
+ );
32235
33555
  }
32236
33556
  }
32237
33557
  if (shapeStyle.strokeColor !== void 0) {
@@ -32246,17 +33566,13 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
32246
33566
  delete lineNode["a:solidFill"];
32247
33567
  } else {
32248
33568
  delete lineNode["a:noFill"];
32249
- const lineFill = {
32250
- "a:srgbClr": {
32251
- "@_val": shapeStyle.strokeColor.replace("#", "")
32252
- }
32253
- };
32254
- if (typeof shapeStyle.strokeOpacity === "number" && shapeStyle.strokeOpacity >= 0 && shapeStyle.strokeOpacity < 1) {
32255
- lineFill["a:srgbClr"]["a:alpha"] = {
32256
- "@_val": String(Math.round(this.clampUnitInterval(shapeStyle.strokeOpacity) * 1e5))
32257
- };
32258
- }
32259
- lineNode["a:solidFill"] = lineFill;
33569
+ const resolvedStrokeOriginal = shapeStyle.strokeColorXml ? this.parseColor(shapeStyle.strokeColorXml) : void 0;
33570
+ lineNode["a:solidFill"] = serializeColorChoice(
33571
+ shapeStyle.strokeColorXml,
33572
+ resolvedStrokeOriginal,
33573
+ shapeStyle.strokeColor,
33574
+ shapeStyle.strokeOpacity
33575
+ );
32260
33576
  }
32261
33577
  }
32262
33578
  if (shapeStyle.strokeDash !== void 0) {
@@ -32337,7 +33653,11 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
32337
33653
  } else if (shapeStyle.lineJoin === "bevel") {
32338
33654
  lineNode["a:bevel"] = {};
32339
33655
  } else if (shapeStyle.lineJoin === "miter") {
32340
- lineNode["a:miter"] = { "@_lim": "800000" };
33656
+ const miterNode = {};
33657
+ if (typeof shapeStyle.miterLimit === "number" && Number.isFinite(shapeStyle.miterLimit) && shapeStyle.miterLimit !== 8e5) {
33658
+ miterNode["@_lim"] = String(Math.round(shapeStyle.miterLimit));
33659
+ }
33660
+ lineNode["a:miter"] = miterNode;
32341
33661
  }
32342
33662
  }
32343
33663
  if (shapeStyle.lineCap !== void 0) {
@@ -32357,6 +33677,82 @@ var PptxHandlerRuntime28 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
32357
33677
  spPr["a:ln"]["a:effectLst"] = lineEffectListXml;
32358
33678
  }
32359
33679
  }
33680
+ /**
33681
+ * Serialize the shape's `<p:style>` block (CT_ShapeStyle §20.1.2.2.36)
33682
+ * from the persisted ref indices/colour XML. Emits children in spec
33683
+ * order: `lnRef → fillRef → effectRef → fontRef`.
33684
+ *
33685
+ * When the original shape XML already contained a `<p:style>` we mutate
33686
+ * that node in place so any unmodelled attributes/children are preserved.
33687
+ * When it didn't, we create one. When the shape no longer has any ref
33688
+ * data we leave the existing `<p:style>` (if any) untouched — silently
33689
+ * dropping it would break round-tripping.
33690
+ *
33691
+ * Phase 2 Stream B / C-H2.
33692
+ */
33693
+ applyShapeStyleRefs(shape, shapeStyle) {
33694
+ const hasAnyRef = shapeStyle.lnRefIdx !== void 0 || shapeStyle.fillRefIdx !== void 0 || shapeStyle.effectRefIdx !== void 0 || shapeStyle.fontRefIdx !== void 0;
33695
+ if (!hasAnyRef) {
33696
+ return;
33697
+ }
33698
+ const existing = shape["p:style"];
33699
+ const styleNode = existing ?? {};
33700
+ if (shapeStyle.lnRefIdx !== void 0) {
33701
+ const lnRef = styleNode["a:lnRef"] ?? {};
33702
+ lnRef["@_idx"] = String(shapeStyle.lnRefIdx);
33703
+ this.replaceRefColorChoice(lnRef, shapeStyle.lnRefColorXml);
33704
+ styleNode["a:lnRef"] = lnRef;
33705
+ }
33706
+ if (shapeStyle.fillRefIdx !== void 0) {
33707
+ const fillRef = styleNode["a:fillRef"] ?? {};
33708
+ fillRef["@_idx"] = String(shapeStyle.fillRefIdx);
33709
+ this.replaceRefColorChoice(fillRef, shapeStyle.fillRefColorXml);
33710
+ styleNode["a:fillRef"] = fillRef;
33711
+ }
33712
+ if (shapeStyle.effectRefIdx !== void 0) {
33713
+ const effectRef = styleNode["a:effectRef"] ?? {};
33714
+ effectRef["@_idx"] = String(shapeStyle.effectRefIdx);
33715
+ this.replaceRefColorChoice(effectRef, shapeStyle.effectRefColorXml);
33716
+ styleNode["a:effectRef"] = effectRef;
33717
+ }
33718
+ if (shapeStyle.fontRefIdx !== void 0) {
33719
+ const fontRef = styleNode["a:fontRef"] ?? {};
33720
+ fontRef["@_idx"] = shapeStyle.fontRefIdx;
33721
+ this.replaceRefColorChoice(fontRef, shapeStyle.fontRefColorXml);
33722
+ styleNode["a:fontRef"] = fontRef;
33723
+ }
33724
+ const reordered = reorderObjectKeys(styleNode, SHAPE_STYLE_ORDER);
33725
+ for (const key of Object.keys(styleNode)) {
33726
+ delete styleNode[key];
33727
+ }
33728
+ for (const key of Object.keys(reordered)) {
33729
+ styleNode[key] = reordered[key];
33730
+ }
33731
+ shape["p:style"] = styleNode;
33732
+ }
33733
+ /**
33734
+ * Replace any existing colour-choice child on a style-matrix-reference
33735
+ * element with the given preserved XML, or strip all colour children
33736
+ * when the override is undefined.
33737
+ */
33738
+ replaceRefColorChoice(refNode, colorXml) {
33739
+ for (const key of [
33740
+ "a:scrgbClr",
33741
+ "a:srgbClr",
33742
+ "a:hslClr",
33743
+ "a:sysClr",
33744
+ "a:schemeClr",
33745
+ "a:prstClr"
33746
+ ]) {
33747
+ delete refNode[key];
33748
+ }
33749
+ if (!colorXml) {
33750
+ return;
33751
+ }
33752
+ for (const [key, value] of Object.entries(colorXml)) {
33753
+ refNode[key] = value;
33754
+ }
33755
+ }
32360
33756
  };
32361
33757
 
32362
33758
  // src/core/core/runtime/PptxHandlerRuntimeSaveEffectsWriter.ts
@@ -32366,17 +33762,22 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
32366
33762
  * effectDag, 3D scene, and 3D shape properties to the given spPr XML object.
32367
33763
  */
32368
33764
  applyEffectsAndThreeD(spPr, shapeStyle) {
32369
- const outerShadowXml = this.buildOuterShadowXml(shapeStyle);
33765
+ const presetShadowXml = shapeStyle.presetShadowName ? this.buildPresetShadowXml(shapeStyle) : void 0;
33766
+ const outerShadowXml = presetShadowXml ? void 0 : this.buildOuterShadowXml(shapeStyle);
32370
33767
  const innerShadowXml = this.buildInnerShadowXml(shapeStyle);
32371
33768
  const glowXml = this.buildGlowXml(shapeStyle);
32372
33769
  const softEdgeXml = this.buildSoftEdgeXml(shapeStyle);
32373
33770
  const reflectionXml = this.buildReflectionXml(shapeStyle);
32374
33771
  const blurXml = this.buildBlurXml(shapeStyle);
32375
- const hasAnyEffect = outerShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
33772
+ const hasAnyEffect = outerShadowXml || presetShadowXml || innerShadowXml || glowXml || softEdgeXml || reflectionXml || blurXml;
32376
33773
  if (hasAnyEffect) {
32377
33774
  const effectList = spPr["a:effectLst"] || {};
32378
- if (outerShadowXml) {
33775
+ if (presetShadowXml) {
33776
+ effectList["a:prstShdw"] = presetShadowXml;
33777
+ delete effectList["a:outerShdw"];
33778
+ } else if (outerShadowXml) {
32379
33779
  effectList["a:outerShdw"] = outerShadowXml;
33780
+ delete effectList["a:prstShdw"];
32380
33781
  }
32381
33782
  if (innerShadowXml) {
32382
33783
  effectList["a:innerShdw"] = innerShadowXml;
@@ -32393,12 +33794,13 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
32393
33794
  if (blurXml) {
32394
33795
  effectList["a:blur"] = blurXml;
32395
33796
  }
32396
- spPr["a:effectLst"] = effectList;
33797
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
32397
33798
  } else {
32398
33799
  const effectList = spPr["a:effectLst"];
32399
33800
  if (effectList) {
32400
- if (shapeStyle.shadowColor !== void 0 && !outerShadowXml) {
33801
+ if (shapeStyle.shadowColor !== void 0 && !outerShadowXml && !presetShadowXml) {
32401
33802
  delete effectList["a:outerShdw"];
33803
+ delete effectList["a:prstShdw"];
32402
33804
  }
32403
33805
  if (shapeStyle.innerShadowColor !== void 0 && !innerShadowXml) {
32404
33806
  delete effectList["a:innerShdw"];
@@ -32417,6 +33819,8 @@ var PptxHandlerRuntime29 = class extends PptxHandlerRuntime28 {
32417
33819
  }
32418
33820
  if (Object.keys(effectList).length === 0) {
32419
33821
  delete spPr["a:effectLst"];
33822
+ } else {
33823
+ spPr["a:effectLst"] = reorderObjectKeys(effectList, EFFECT_LST_ORDER);
32420
33824
  }
32421
33825
  }
32422
33826
  }
@@ -32577,7 +33981,8 @@ var PptxHandlerRuntime30 = class _PptxHandlerRuntime extends PptxHandlerRuntime2
32577
33981
  );
32578
33982
  }
32579
33983
  if (el.textStyle?.hOverflow) {
32580
- bodyPr["@_hOverflow"] = el.textStyle.hOverflow;
33984
+ bodyPr["@_horzOverflow"] = el.textStyle.hOverflow;
33985
+ delete bodyPr["@_hOverflow"];
32581
33986
  }
32582
33987
  if (el.textStyle?.vertOverflow) {
32583
33988
  bodyPr["@_vertOverflow"] = el.textStyle.vertOverflow;
@@ -32808,7 +34213,10 @@ var PptxHandlerRuntime31 = class extends PptxHandlerRuntime30 {
32808
34213
  const elWithPaths = el;
32809
34214
  if (elWithPaths.customGeometryPaths && elWithPaths.customGeometryPaths.length > 0) {
32810
34215
  delete spPr["a:prstGeom"];
32811
- spPr["a:custGeom"] = customGeometryPathsToXml(elWithPaths.customGeometryPaths);
34216
+ spPr["a:custGeom"] = customGeometryPathsToXml(
34217
+ elWithPaths.customGeometryPaths,
34218
+ elWithPaths.customGeometryRawData
34219
+ );
32812
34220
  } else if (spPr["a:prstGeom"]) {
32813
34221
  const presetGeometry = el.type === "connector" ? this.normalizePresetGeometry(el.shapeType || "straightConnector1") : this.normalizePresetGeometry(el.shapeType);
32814
34222
  const prstGeom = spPr["a:prstGeom"];
@@ -32933,6 +34341,42 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
32933
34341
  isGraphicFrameShape(shape) {
32934
34342
  return Boolean(shape["p:nvGraphicFramePr"] || shape["a:graphic"] && shape["p:xfrm"]);
32935
34343
  }
34344
+ /**
34345
+ * Reorder children of `p:spPr` to match CT_ShapeProperties (§20.1.2.2.35).
34346
+ * Also reorders any nested `a:blipFill` per CT_BlipFillProperties.
34347
+ * fast-xml-parser preserves insertion order; PowerPoint validates against
34348
+ * the schema's required order, so save-side mutations must be re-sorted.
34349
+ */
34350
+ finalizeSpPrSchemaOrder(shape) {
34351
+ const spPr = shape["p:spPr"];
34352
+ if (!spPr) {
34353
+ return;
34354
+ }
34355
+ const blipFill = spPr["a:blipFill"];
34356
+ if (blipFill) {
34357
+ this.reorderInPlace(blipFill, BLIP_FILL_ORDER);
34358
+ }
34359
+ this.reorderInPlace(spPr, SP_PR_ORDER);
34360
+ }
34361
+ /**
34362
+ * Reorder children of the picture-level `p:blipFill` (CT_BlipFillProperties).
34363
+ * Picture elements carry their blip data on the `p:pic` root, not under spPr.
34364
+ */
34365
+ finalizePictureBlipFillOrder(shape) {
34366
+ const pBlipFill = shape["p:blipFill"];
34367
+ if (pBlipFill) {
34368
+ this.reorderInPlace(pBlipFill, BLIP_FILL_ORDER);
34369
+ }
34370
+ }
34371
+ reorderInPlace(target, schemaOrder) {
34372
+ const reordered = reorderObjectKeys(target, schemaOrder);
34373
+ for (const key of Object.keys(target)) {
34374
+ delete target[key];
34375
+ }
34376
+ for (const key of Object.keys(reordered)) {
34377
+ target[key] = reordered[key];
34378
+ }
34379
+ }
32936
34380
  /** Whether an element ID indicates a template (layout/master) element. */
32937
34381
  isTemplateElementId(elementId) {
32938
34382
  return elementId.startsWith("layout-") || elementId.startsWith("master-");
@@ -32956,14 +34400,50 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
32956
34400
  }
32957
34401
  return;
32958
34402
  }
34403
+ if (el.type === "contentPart") {
34404
+ if (shape) {
34405
+ this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
34406
+ collectors.contentParts.push(shape);
34407
+ } else {
34408
+ this.compatibilityService.reportWarning({
34409
+ code: "SAVE_ELEMENT_SKIPPED",
34410
+ message: `Content part '${el.id}' has no rawXml and was skipped during save.`,
34411
+ scope: "save",
34412
+ slideId: ctx.slide.id,
34413
+ elementId: el.id
34414
+ });
34415
+ }
34416
+ return;
34417
+ }
32959
34418
  if (!shape && (el.type === "text" || el.type === "shape")) {
32960
34419
  shape = this.createElementXml(el);
32961
34420
  }
32962
34421
  if (!shape && el.type === "connector") {
32963
34422
  shape = this.createConnectorXml(el);
32964
34423
  }
32965
- if (!shape && el.type === "ink") {
32966
- shape = this.createInkShapeXml(el);
34424
+ if (el.type === "ink") {
34425
+ if (!shape) {
34426
+ shape = this.createInkShapeXml(el);
34427
+ this.compatibilityService.reportWarning({
34428
+ code: "SAVE_INK_ENCODED_AS_CUSTGEOM",
34429
+ message: "SDK-created ink element serialized as custGeom shape; pressure/tool metadata not represented in OOXML aink format.",
34430
+ scope: "save",
34431
+ slideId: ctx.slide.id,
34432
+ elementId: el.id
34433
+ });
34434
+ }
34435
+ }
34436
+ if (!shape && el.type === "table") {
34437
+ shape = this.createTableGraphicFrameXml(el);
34438
+ }
34439
+ if (el.type === "ole") {
34440
+ const oleEl = el;
34441
+ if (shape) {
34442
+ this.applyOleTypedFieldUpdates(shape, oleEl);
34443
+ } else {
34444
+ const embedRid = this.resolveOleEmbedRelationshipId(ctx.slideRelationships, oleEl.oleTarget) || ctx.slideRelationshipRegistry.nextRelationshipId();
34445
+ shape = this.createOleGraphicFrameXml(oleEl, embedRid);
34446
+ }
32967
34447
  }
32968
34448
  if (!shape) {
32969
34449
  this.compatibilityService.reportWarning({
@@ -32977,11 +34457,14 @@ var PptxHandlerRuntime32 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
32977
34457
  }
32978
34458
  this.elementTransformUpdater.applyTransform(shape, el, _PptxHandlerRuntime.EMU_PER_PX);
32979
34459
  this.applyImageProperties(shape, el);
34460
+ this.finalizePictureBlipFillOrder(shape);
32980
34461
  this.applyGeometryUpdate(shape, el);
32981
34462
  if (hasShapeProperties(el) && el.shapeStyle && shape["p:spPr"]) {
32982
34463
  const spPr = shape["p:spPr"];
32983
34464
  this.applyFillAndStroke(spPr, el.shapeStyle);
32984
34465
  this.applyEffectsAndThreeD(spPr, el.shapeStyle);
34466
+ this.finalizeSpPrSchemaOrder(shape);
34467
+ this.applyShapeStyleRefs(shape, el.shapeStyle);
32985
34468
  }
32986
34469
  if (hasTextProperties(el)) {
32987
34470
  this.applyTextBodyContent(
@@ -33139,7 +34622,8 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
33139
34622
  connectors: [],
33140
34623
  graphicFrames: [],
33141
34624
  groups: [],
33142
- model3ds: []
34625
+ model3ds: [],
34626
+ contentParts: []
33143
34627
  };
33144
34628
  const ctx = {
33145
34629
  slide,
@@ -33171,6 +34655,12 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
33171
34655
  } else {
33172
34656
  delete spTree["p16:model3D"];
33173
34657
  }
34658
+ if (collectors.contentParts.length > 0) {
34659
+ spTree["p:contentPart"] = collectors.contentParts;
34660
+ } else {
34661
+ delete spTree["p:contentPart"];
34662
+ }
34663
+ this.reapplyAlternateContentEnvelopes(spTree, collectors);
33174
34664
  const reassigned = shapeIdValidator.validateAndDeduplicateIds(
33175
34665
  spTree,
33176
34666
  (v) => this.ensureArray(v)
@@ -33188,12 +34678,426 @@ var PptxHandlerRuntime33 = class extends PptxHandlerRuntime32 {
33188
34678
  this.zip.file(slideRelsPath, this.builder.build(slideRelsData));
33189
34679
  this.applySlideDrawingGuides(slideNode, slide);
33190
34680
  this.deduplicateExtensionLists(xmlObj);
34681
+ if (slideContainsA16Element(slideNode)) {
34682
+ ensureA16NamespaceOnSlideRoot(slideNode);
34683
+ }
33191
34684
  this.zip.file(slide.id, this.builder.build(xmlObj));
33192
34685
  }
34686
+ /**
34687
+ * Re-wrap selected children with their original `<mc:AlternateContent>`
34688
+ * envelope (CC-4).
34689
+ *
34690
+ * Parsing merged the selected branch (Choice when supported, otherwise
34691
+ * Fallback) into the spTree's tag arrays. Without re-wrapping, dirty
34692
+ * save would emit flat `<p:sp>`/`<p:pic>` etc. and drop the
34693
+ * `<mc:Fallback>` branch — losing legacy rendering for files originally
34694
+ * authored with newer-namespace features.
34695
+ *
34696
+ * Strategy: for each XmlObject in `collectors.*` that traces back to a
34697
+ * known AC block, group by block and:
34698
+ * 1. Remove the node from its flat collector / spTree array.
34699
+ * 2. Clone the original AC envelope.
34700
+ * 3. Replace the selected branch's `<{tag}>` children with the
34701
+ * live (possibly edited) nodes from the collectors.
34702
+ * 4. Leave the unselected branch verbatim.
34703
+ *
34704
+ * Final envelopes are appended to `spTree['mc:AlternateContent']`.
34705
+ */
34706
+ reapplyAlternateContentEnvelopes(spTree, collectors) {
34707
+ const TAG_TO_COLLECTOR = {
34708
+ "p:sp": collectors.shapes,
34709
+ "p:pic": collectors.pics,
34710
+ "p:cxnSp": collectors.connectors,
34711
+ "p:graphicFrame": collectors.graphicFrames,
34712
+ "p:grpSp": collectors.groups,
34713
+ "p:contentPart": collectors.contentParts,
34714
+ // `model3d` does not flow through SHAPE_TREE_ELEMENT_TAGS, but the
34715
+ // AC pathway in OpenXML decks frequently uses Choice = p16:model3D
34716
+ // + Fallback = p:pic, so map it for completeness.
34717
+ "p16:model3D": collectors.model3ds
34718
+ };
34719
+ const blockGroups = /* @__PURE__ */ new Map();
34720
+ for (const tag of Object.keys(TAG_TO_COLLECTOR)) {
34721
+ const collector = TAG_TO_COLLECTOR[tag];
34722
+ if (!collector) {
34723
+ continue;
34724
+ }
34725
+ for (const node of collector) {
34726
+ const block = this.alternateContentBlockByRawXml.get(node);
34727
+ if (!block) {
34728
+ continue;
34729
+ }
34730
+ let entries = blockGroups.get(block);
34731
+ if (!entries) {
34732
+ entries = [];
34733
+ blockGroups.set(block, entries);
34734
+ }
34735
+ entries.push({ tag, node, collector });
34736
+ }
34737
+ }
34738
+ if (blockGroups.size === 0) {
34739
+ return;
34740
+ }
34741
+ const envelopes = [];
34742
+ for (const [block, entries] of blockGroups) {
34743
+ for (const entry of entries) {
34744
+ const idx = entry.collector.indexOf(entry.node);
34745
+ if (idx !== -1) {
34746
+ entry.collector.splice(idx, 1);
34747
+ }
34748
+ }
34749
+ const clonedAc = { ...block.rawAc };
34750
+ const liveByTag = /* @__PURE__ */ new Map();
34751
+ for (const entry of entries) {
34752
+ let arr = liveByTag.get(entry.tag);
34753
+ if (!arr) {
34754
+ arr = [];
34755
+ liveByTag.set(entry.tag, arr);
34756
+ }
34757
+ arr.push(entry.node);
34758
+ }
34759
+ if (block.selectedBranch === "choice") {
34760
+ const choices = this.ensureArray(clonedAc["mc:Choice"]);
34761
+ const targetIdx = block.choiceIndex ?? 0;
34762
+ const original = choices[targetIdx];
34763
+ if (original) {
34764
+ const rebuilt = { ...original };
34765
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
34766
+ delete rebuilt[tag];
34767
+ }
34768
+ for (const [tag, nodes] of liveByTag) {
34769
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
34770
+ }
34771
+ choices[targetIdx] = rebuilt;
34772
+ clonedAc["mc:Choice"] = choices.length === 1 ? choices[0] : choices;
34773
+ }
34774
+ } else {
34775
+ const fallback = clonedAc["mc:Fallback"];
34776
+ if (fallback) {
34777
+ const rebuilt = { ...fallback };
34778
+ for (const tag of SHAPE_TREE_ELEMENT_TAGS) {
34779
+ delete rebuilt[tag];
34780
+ }
34781
+ for (const [tag, nodes] of liveByTag) {
34782
+ rebuilt[tag] = nodes.length === 1 ? nodes[0] : nodes;
34783
+ }
34784
+ clonedAc["mc:Fallback"] = rebuilt;
34785
+ }
34786
+ }
34787
+ envelopes.push(clonedAc);
34788
+ }
34789
+ spTree["p:sp"] = collectors.shapes;
34790
+ spTree["p:pic"] = collectors.pics;
34791
+ spTree["p:cxnSp"] = collectors.connectors;
34792
+ spTree["p:graphicFrame"] = collectors.graphicFrames;
34793
+ if (collectors.groups.length > 0) {
34794
+ spTree["p:grpSp"] = collectors.groups;
34795
+ } else {
34796
+ delete spTree["p:grpSp"];
34797
+ }
34798
+ if (collectors.contentParts.length > 0) {
34799
+ spTree["p:contentPart"] = collectors.contentParts;
34800
+ } else {
34801
+ delete spTree["p:contentPart"];
34802
+ }
34803
+ if (collectors.model3ds.length > 0) {
34804
+ spTree["p16:model3D"] = collectors.model3ds;
34805
+ } else {
34806
+ delete spTree["p16:model3D"];
34807
+ }
34808
+ spTree["mc:AlternateContent"] = envelopes.length === 1 ? envelopes[0] : envelopes;
34809
+ }
34810
+ };
34811
+
34812
+ // src/core/core/runtime/PptxHandlerRuntimeSaveTheme.ts
34813
+ var PptxHandlerRuntime34 = class extends PptxHandlerRuntime33 {
34814
+ /**
34815
+ * Mark a theme path as dirty so the save pipeline will regenerate
34816
+ * the theme XML from in-memory state. Optional — without this the
34817
+ * original XML is preserved verbatim on save (C-H3).
34818
+ */
34819
+ markThemeDirty(themePath) {
34820
+ this.dirtyThemePaths.add(themePath);
34821
+ }
34822
+ /**
34823
+ * Mark all known theme paths dirty in one call.
34824
+ */
34825
+ markAllThemesDirty() {
34826
+ for (const themePath of this.originalThemeXmlByPath.keys()) {
34827
+ this.dirtyThemePaths.add(themePath);
34828
+ }
34829
+ for (const themePath of this.masterThemePaths.values()) {
34830
+ this.dirtyThemePaths.add(themePath);
34831
+ }
34832
+ }
34833
+ /**
34834
+ * Persist all theme parts during save. Called from the save pipeline
34835
+ * after master / layout XML have been flushed and before
34836
+ * presentation.xml is serialized.
34837
+ *
34838
+ * Order of operations per theme path:
34839
+ *
34840
+ * 1. If the path is *not* in {@link dirtyThemePaths}, the existing
34841
+ * ZIP entry is already correct — no-op. (Original XML was placed
34842
+ * into the ZIP at load time.)
34843
+ * 2. If the path is dirty, build a fresh `<a:theme>` document from
34844
+ * in-memory state and the captured raw subtrees, then overwrite
34845
+ * the ZIP entry.
34846
+ */
34847
+ async persistThemeParts() {
34848
+ const seenThemePaths = /* @__PURE__ */ new Set();
34849
+ for (const [masterPath, themePath] of this.masterThemePaths.entries()) {
34850
+ if (!themePath) {
34851
+ continue;
34852
+ }
34853
+ seenThemePaths.add(themePath);
34854
+ if (!this.dirtyThemePaths.has(themePath)) {
34855
+ continue;
34856
+ }
34857
+ const themeXml2 = this.buildThemeXml(themePath, masterPath);
34858
+ if (themeXml2) {
34859
+ this.zip.file(themePath, themeXml2);
34860
+ }
34861
+ }
34862
+ for (const [themePath] of this.originalThemeXmlByPath.entries()) {
34863
+ if (seenThemePaths.has(themePath)) {
34864
+ continue;
34865
+ }
34866
+ if (!this.dirtyThemePaths.has(themePath)) {
34867
+ continue;
34868
+ }
34869
+ const themeXml2 = this.buildThemeXml(themePath, void 0);
34870
+ if (themeXml2) {
34871
+ this.zip.file(themePath, themeXml2);
34872
+ }
34873
+ }
34874
+ }
34875
+ /**
34876
+ * Build a complete `<a:theme>` XML document from in-memory state.
34877
+ * Returns the serialized XML string (with XML prolog), or `undefined`
34878
+ * if there is no source data to emit.
34879
+ *
34880
+ * - Color scheme: built from per-master color map (or global fallback).
34881
+ * - Font scheme: built from per-master font map + per-script entries.
34882
+ * - Format scheme: re-emit the original XML subtree if available; else
34883
+ * build a minimal scheme from {@link themeFormatScheme}.
34884
+ * - objectDefaults / extraClrSchemeLst / custClrLst / extLst: re-emit
34885
+ * captured raw subtrees.
34886
+ */
34887
+ buildThemeXml(themePath, masterPath) {
34888
+ const colorMap = masterPath && this.masterThemeColorMaps.get(masterPath) || this.globalThemeColorMapSnapshot || this.themeColorMap;
34889
+ const fontMap = masterPath && this.masterThemeFontMaps.get(masterPath) || this.globalThemeFontMapSnapshot || this.themeFontMap;
34890
+ const themeName = this.masterThemeNames.get(themePath) || "Office Theme";
34891
+ const colorSchemeName = this.masterThemeColorSchemeNames.get(themePath) || themeName;
34892
+ const fontSchemeName = this.masterThemeFontSchemeNames.get(themePath) || themeName;
34893
+ const majorScripts = this.masterThemeMajorFontScripts.get(themePath) || {};
34894
+ const minorScripts = this.masterThemeMinorFontScripts.get(themePath) || {};
34895
+ const clrScheme = this.buildClrSchemeObject(colorSchemeName, colorMap);
34896
+ const fontScheme = this.buildFontSchemeObject(
34897
+ fontSchemeName,
34898
+ fontMap,
34899
+ majorScripts,
34900
+ minorScripts
34901
+ );
34902
+ const fmtScheme = this.extractRawSubtreeFromOriginal(themePath, [
34903
+ "a:theme",
34904
+ "a:themeElements",
34905
+ "a:fmtScheme"
34906
+ ]);
34907
+ const themeElements = {
34908
+ "a:clrScheme": clrScheme,
34909
+ "a:fontScheme": fontScheme
34910
+ };
34911
+ if (fmtScheme !== void 0) {
34912
+ themeElements["a:fmtScheme"] = fmtScheme;
34913
+ } else {
34914
+ themeElements["a:fmtScheme"] = this.buildMinimalFmtScheme(themeName);
34915
+ }
34916
+ const themeRoot = {
34917
+ "@_xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
34918
+ "@_name": themeName,
34919
+ "a:themeElements": themeElements
34920
+ };
34921
+ const objectDefaults = this.masterThemeObjectDefaults.get(themePath);
34922
+ if (objectDefaults && (objectDefaults.spDef || objectDefaults.lnDef || objectDefaults.txDef)) {
34923
+ const od = {};
34924
+ if (objectDefaults.spDef !== void 0) {
34925
+ od["a:spDef"] = objectDefaults.spDef;
34926
+ }
34927
+ if (objectDefaults.lnDef !== void 0) {
34928
+ od["a:lnDef"] = objectDefaults.lnDef;
34929
+ }
34930
+ if (objectDefaults.txDef !== void 0) {
34931
+ od["a:txDef"] = objectDefaults.txDef;
34932
+ }
34933
+ themeRoot["a:objectDefaults"] = od;
34934
+ } else {
34935
+ themeRoot["a:objectDefaults"] = {};
34936
+ }
34937
+ const extraClr = this.masterThemeExtraClrSchemeLst.get(themePath);
34938
+ themeRoot["a:extraClrSchemeLst"] = extraClr !== void 0 ? extraClr : {};
34939
+ const custClr = this.masterThemeCustClrLst.get(themePath);
34940
+ if (custClr !== void 0) {
34941
+ themeRoot["a:custClrLst"] = custClr;
34942
+ }
34943
+ const themeExt = this.masterThemeExtLst.get(themePath);
34944
+ if (themeExt !== void 0) {
34945
+ themeRoot["a:extLst"] = themeExt;
34946
+ }
34947
+ const doc = {
34948
+ "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
34949
+ "a:theme": themeRoot
34950
+ };
34951
+ try {
34952
+ return this.builder.build(doc);
34953
+ } catch (error) {
34954
+ console.warn(`Failed to build theme XML for ${themePath}:`, error);
34955
+ return void 0;
34956
+ }
34957
+ }
34958
+ /**
34959
+ * Build the `a:clrScheme` XmlObject from a colour map. Each slot
34960
+ * value is interpreted as either a `#RRGGBB` srgb hex or a known
34961
+ * sysClr token (currently always emitted as srgbClr — the in-memory
34962
+ * map is hex-typed; sysClr round-trip belongs to the broader C-H3
34963
+ * fix to preserve original color XML and is out of scope here).
34964
+ */
34965
+ buildClrSchemeObject(schemeName, colorMap) {
34966
+ const slot = (key) => {
34967
+ const hex = String(colorMap[key] || "").replace(/^#/, "");
34968
+ const srgb = hex.length === 6 ? hex.toUpperCase() : "000000";
34969
+ return { "a:srgbClr": { "@_val": srgb } };
34970
+ };
34971
+ return {
34972
+ "@_name": schemeName,
34973
+ "a:dk1": slot("dk1"),
34974
+ "a:lt1": slot("lt1"),
34975
+ "a:dk2": slot("dk2"),
34976
+ "a:lt2": slot("lt2"),
34977
+ "a:accent1": slot("accent1"),
34978
+ "a:accent2": slot("accent2"),
34979
+ "a:accent3": slot("accent3"),
34980
+ "a:accent4": slot("accent4"),
34981
+ "a:accent5": slot("accent5"),
34982
+ "a:accent6": slot("accent6"),
34983
+ "a:hlink": slot("hlink"),
34984
+ "a:folHlink": slot("folHlink")
34985
+ };
34986
+ }
34987
+ /**
34988
+ * Build the `a:fontScheme` XmlObject from a font map plus per-script
34989
+ * font tables.
34990
+ *
34991
+ * Phase 4 Stream A / M4.
34992
+ */
34993
+ buildFontSchemeObject(schemeName, fontMap, majorScripts, minorScripts) {
34994
+ const buildFontGroup = (latinKey, eaKey, csKey, scripts) => {
34995
+ const group = {
34996
+ "a:latin": { "@_typeface": fontMap[latinKey] || "Calibri" },
34997
+ "a:ea": { "@_typeface": fontMap[eaKey] || "" },
34998
+ "a:cs": { "@_typeface": fontMap[csKey] || "" }
34999
+ };
35000
+ const scriptKeys = Object.keys(scripts);
35001
+ if (scriptKeys.length > 0) {
35002
+ const fontEntries = scriptKeys.map((script) => ({
35003
+ "@_script": script,
35004
+ "@_typeface": scripts[script]
35005
+ }));
35006
+ group["a:font"] = fontEntries.length === 1 ? fontEntries[0] : fontEntries;
35007
+ }
35008
+ return group;
35009
+ };
35010
+ return {
35011
+ "@_name": schemeName,
35012
+ "a:majorFont": buildFontGroup("mj-lt", "mj-ea", "mj-cs", majorScripts),
35013
+ "a:minorFont": buildFontGroup("mn-lt", "mn-ea", "mn-cs", minorScripts)
35014
+ };
35015
+ }
35016
+ /**
35017
+ * Re-parse the original theme XML and pluck out a subtree by path,
35018
+ * returning the raw parser object. Returns `undefined` when the
35019
+ * original is missing or the path doesn't exist.
35020
+ *
35021
+ * Used to preserve `a:fmtScheme` byte-for-byte through a regenerate
35022
+ * round-trip, since the in-memory PptxThemeFormatScheme is lossy.
35023
+ */
35024
+ extractRawSubtreeFromOriginal(themePath, path) {
35025
+ const original = this.originalThemeXmlByPath.get(themePath);
35026
+ if (!original) {
35027
+ return void 0;
35028
+ }
35029
+ try {
35030
+ const parsed = this.parser.parse(original);
35031
+ let cursor = parsed;
35032
+ for (const segment of path) {
35033
+ if (cursor && typeof cursor === "object" && segment in cursor) {
35034
+ cursor = cursor[segment];
35035
+ } else {
35036
+ return void 0;
35037
+ }
35038
+ }
35039
+ return cursor;
35040
+ } catch {
35041
+ return void 0;
35042
+ }
35043
+ }
35044
+ /**
35045
+ * Last-resort minimal `<a:fmtScheme>` body. Mirrors the SDK new-deck
35046
+ * builder's output for new presentations, scaled down to the smallest
35047
+ * schema-valid form.
35048
+ */
35049
+ buildMinimalFmtScheme(name) {
35050
+ const phClrSolid = { "a:solidFill": { "a:schemeClr": { "@_val": "phClr" } } };
35051
+ return {
35052
+ "@_name": name,
35053
+ "a:fillStyleLst": {
35054
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
35055
+ "a:gradFill": []
35056
+ },
35057
+ "a:lnStyleLst": {
35058
+ "a:ln": [
35059
+ {
35060
+ "@_w": "6350",
35061
+ "@_cap": "flat",
35062
+ "@_cmpd": "sng",
35063
+ "@_algn": "ctr",
35064
+ ...phClrSolid,
35065
+ "a:prstDash": { "@_val": "solid" },
35066
+ "a:miter": { "@_lim": "800000" }
35067
+ },
35068
+ {
35069
+ "@_w": "12700",
35070
+ "@_cap": "flat",
35071
+ "@_cmpd": "sng",
35072
+ "@_algn": "ctr",
35073
+ ...phClrSolid,
35074
+ "a:prstDash": { "@_val": "solid" },
35075
+ "a:miter": { "@_lim": "800000" }
35076
+ },
35077
+ {
35078
+ "@_w": "19050",
35079
+ "@_cap": "flat",
35080
+ "@_cmpd": "sng",
35081
+ "@_algn": "ctr",
35082
+ ...phClrSolid,
35083
+ "a:prstDash": { "@_val": "solid" },
35084
+ "a:miter": { "@_lim": "800000" }
35085
+ }
35086
+ ]
35087
+ },
35088
+ "a:effectStyleLst": {
35089
+ "a:effectStyle": [{ "a:effectLst": {} }, { "a:effectLst": {} }, { "a:effectLst": {} }]
35090
+ },
35091
+ "a:bgFillStyleLst": {
35092
+ "a:solidFill": [{ "a:schemeClr": { "@_val": "phClr" } }],
35093
+ "a:gradFill": []
35094
+ }
35095
+ };
35096
+ }
33193
35097
  };
33194
35098
 
33195
35099
  // src/core/core/runtime/PptxHandlerRuntimeSavePipeline.ts
33196
- var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime33 {
35100
+ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
33197
35101
  /**
33198
35102
  * Resolve the effective conformance class for this save operation.
33199
35103
  *
@@ -33280,6 +35184,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
33280
35184
  for (const [masterPath, masterXmlObj] of this.masterXmlMap.entries()) {
33281
35185
  this.zip.file(masterPath, this.builder.build(masterXmlObj));
33282
35186
  }
35187
+ await this.persistThemeParts();
33283
35188
  await this.applyEmbeddedFontPreservation(options?.embeddedFonts);
33284
35189
  if (this.presentationData) {
33285
35190
  this.presentationSaveBuilder.applySaveOptions({
@@ -33369,7 +35274,7 @@ var PptxHandlerRuntime34 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
33369
35274
  };
33370
35275
 
33371
35276
  // src/core/core/runtime/PptxHandlerRuntimeElementParsing.ts
33372
- var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime34 {
35277
+ var PptxHandlerRuntime36 = class _PptxHandlerRuntime extends PptxHandlerRuntime35 {
33373
35278
  /**
33374
35279
  * Parse media data (video/audio path and MIME type) from graphic frame data.
33375
35280
  */
@@ -33591,7 +35496,7 @@ var PptxHandlerRuntime35 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
33591
35496
  };
33592
35497
 
33593
35498
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderLookup.ts
33594
- var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
35499
+ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
33595
35500
  findPlaceholderInShapeTree(spTree, expected) {
33596
35501
  if (!spTree) {
33597
35502
  return void 0;
@@ -33744,7 +35649,7 @@ var PptxHandlerRuntime36 = class extends PptxHandlerRuntime35 {
33744
35649
  };
33745
35650
 
33746
35651
  // src/core/core/runtime/PptxHandlerRuntimeGeometryParsing.ts
33747
- var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
35652
+ var PptxHandlerRuntime38 = class extends PptxHandlerRuntime37 {
33748
35653
  parseGeometryAdjustments(prstGeom) {
33749
35654
  if (!prstGeom) {
33750
35655
  return void 0;
@@ -33853,6 +35758,103 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
33853
35758
  }
33854
35759
  return handles.length > 0 ? handles : void 0;
33855
35760
  }
35761
+ /**
35762
+ * Extract `a:gdLst`/`a:ahLst`/`a:cxnLst`/`a:rect` raw XML from a `a:custGeom`
35763
+ * node so they can be re-emitted on save when the geometry is edited.
35764
+ * Returns `undefined` when none of these auxiliary children carry data.
35765
+ */
35766
+ extractCustomGeometryRawData(custGeom) {
35767
+ if (!custGeom) {
35768
+ return void 0;
35769
+ }
35770
+ const isNonEmpty = (node) => {
35771
+ if (node === void 0 || node === null) {
35772
+ return false;
35773
+ }
35774
+ if (typeof node !== "object") {
35775
+ return true;
35776
+ }
35777
+ return Object.keys(node).length > 0;
35778
+ };
35779
+ const result = {};
35780
+ const gdLst = custGeom["a:gdLst"];
35781
+ if (isNonEmpty(gdLst)) {
35782
+ result.gdLstXml = gdLst;
35783
+ }
35784
+ const ahLst = custGeom["a:ahLst"];
35785
+ if (isNonEmpty(ahLst)) {
35786
+ result.ahLstXml = ahLst;
35787
+ }
35788
+ const cxnLst = custGeom["a:cxnLst"];
35789
+ if (isNonEmpty(cxnLst)) {
35790
+ result.cxnLstXml = cxnLst;
35791
+ }
35792
+ const rect = custGeom["a:rect"];
35793
+ if (isNonEmpty(rect)) {
35794
+ result.rectXml = rect;
35795
+ }
35796
+ return Object.keys(result).length > 0 ? result : void 0;
35797
+ }
35798
+ /**
35799
+ * Build structured `CustomGeometryPath[]` from a parsed `a:custGeom` node,
35800
+ * including per-path `@fill`/`@stroke`/`@extrusionOk` attributes so they
35801
+ * survive a round-trip when the path list is later regenerated.
35802
+ *
35803
+ * Falls back to SVG → structured-path conversion when no structured path
35804
+ * info is otherwise available.
35805
+ */
35806
+ buildStructuredCustomGeometryPaths(custGeom, pathData, pathWidth, pathHeight) {
35807
+ if (!custGeom) {
35808
+ return void 0;
35809
+ }
35810
+ const pathLst = custGeom["a:pathLst"];
35811
+ if (!pathLst) {
35812
+ return void 0;
35813
+ }
35814
+ const pathNodes = this.ensureArray(pathLst["a:path"]);
35815
+ if (pathNodes.length === 0) {
35816
+ return void 0;
35817
+ }
35818
+ const segments = svgToCustomGeometryPaths(pathData, pathWidth, pathHeight);
35819
+ if (segments.length === 0) {
35820
+ return void 0;
35821
+ }
35822
+ const validFillModes = /* @__PURE__ */ new Set([
35823
+ "norm",
35824
+ "lighten",
35825
+ "lightenLess",
35826
+ "darken",
35827
+ "darkenLess",
35828
+ "none"
35829
+ ]);
35830
+ const parseBoolAttr2 = (value) => {
35831
+ if (value === void 0 || value === null || value === "") {
35832
+ return void 0;
35833
+ }
35834
+ if (value === "1" || value === "true" || value === true) {
35835
+ return true;
35836
+ }
35837
+ if (value === "0" || value === "false" || value === false) {
35838
+ return false;
35839
+ }
35840
+ return void 0;
35841
+ };
35842
+ const target = segments[0];
35843
+ const firstNode = pathNodes[0];
35844
+ const fillAttr = String(firstNode["@_fill"] ?? "").trim();
35845
+ if (validFillModes.has(fillAttr)) {
35846
+ target.fillMode = fillAttr;
35847
+ }
35848
+ const strokeAttr = parseBoolAttr2(firstNode["@_stroke"]);
35849
+ if (strokeAttr !== void 0) {
35850
+ target.stroke = strokeAttr;
35851
+ }
35852
+ const extrusionAttr = parseBoolAttr2(firstNode["@_extrusionOk"]);
35853
+ if (extrusionAttr !== void 0) {
35854
+ target.extrusionOk = extrusionAttr;
35855
+ }
35856
+ return segments;
35857
+ }
33856
35858
  parseCustomGeometry(custGeom, shapeWidth, shapeHeight) {
33857
35859
  if (!custGeom || !custGeom["a:pathLst"] || !custGeom["a:pathLst"]?.["a:path"]) {
33858
35860
  return null;
@@ -33921,7 +35923,7 @@ var PptxHandlerRuntime37 = class extends PptxHandlerRuntime36 {
33921
35923
  };
33922
35924
 
33923
35925
  // src/core/core/runtime/PptxHandlerRuntimeShapeImageFill.ts
33924
- var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime37 {
35926
+ var PptxHandlerRuntime39 = class _PptxHandlerRuntime extends PptxHandlerRuntime38 {
33925
35927
  /**
33926
35928
  * Parse a shape that has an image fill (a:blipFill inside spPr)
33927
35929
  * This handles shapes like rectangles filled with images (e.g., wood texture backgrounds)
@@ -34121,7 +36123,7 @@ var PptxHandlerRuntime38 = class _PptxHandlerRuntime extends PptxHandlerRuntime3
34121
36123
  };
34122
36124
 
34123
36125
  // src/core/core/runtime/PptxHandlerRuntimeTextDefaults.ts
34124
- var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
36126
+ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34125
36127
  /**
34126
36128
  * Apply {@link PlaceholderDefaults} body-level properties to a
34127
36129
  * {@link TextStyle} as fallback values (only sets fields that are
@@ -34245,7 +36247,7 @@ var PptxHandlerRuntime39 = class extends PptxHandlerRuntime38 {
34245
36247
  };
34246
36248
 
34247
36249
  // src/core/core/runtime/PptxHandlerRuntimeBulletParsing.ts
34248
- var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
36250
+ var PptxHandlerRuntime41 = class extends PptxHandlerRuntime40 {
34249
36251
  resolveParagraphBulletInfo(paragraph, paragraphIndex, txBody, inheritedTxBody, isBodyPlaceholder = false, slidePath) {
34250
36252
  if (!paragraph) {
34251
36253
  return null;
@@ -34276,7 +36278,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34276
36278
  if (candidate["a:buNone"]) {
34277
36279
  return { none: true };
34278
36280
  }
34279
- if (candidate["a:buChar"] || candidate["a:buAutoNum"] || candidate["a:buBlip"]) {
36281
+ 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) {
34280
36282
  resolvedBulletProps = candidate;
34281
36283
  break;
34282
36284
  }
@@ -34290,6 +36292,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34290
36292
  }
34291
36293
  const buFont = resolvedBulletProps["a:buFont"];
34292
36294
  const fontFamily = buFont?.["@_typeface"] ? String(buFont["@_typeface"]) : void 0;
36295
+ const fontInherit = resolvedBulletProps["a:buFontTx"] !== void 0;
34293
36296
  const buSzPct = resolvedBulletProps["a:buSzPct"];
34294
36297
  let sizePercent;
34295
36298
  if (buSzPct?.["@_val"] !== void 0) {
@@ -34306,6 +36309,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34306
36309
  sizePts = ptsRaw / 100;
34307
36310
  }
34308
36311
  }
36312
+ const sizeInherit = resolvedBulletProps["a:buSzTx"] !== void 0;
34309
36313
  const buClr = resolvedBulletProps["a:buClr"];
34310
36314
  let color;
34311
36315
  if (buClr) {
@@ -34314,6 +36318,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34314
36318
  color = String(srgb["@_val"]);
34315
36319
  }
34316
36320
  }
36321
+ const colorInherit = resolvedBulletProps["a:buClrTx"] !== void 0;
34317
36322
  const bulletChar = String(
34318
36323
  resolvedBulletProps["a:buChar"]?.["@_char"] || ""
34319
36324
  );
@@ -34323,7 +36328,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34323
36328
  fontFamily,
34324
36329
  sizePercent,
34325
36330
  sizePts,
34326
- color
36331
+ color,
36332
+ ...fontInherit ? { fontInherit: true } : {},
36333
+ ...colorInherit ? { colorInherit: true } : {},
36334
+ ...sizeInherit ? { sizeInherit: true } : {}
34327
36335
  };
34328
36336
  }
34329
36337
  const autoNum = resolvedBulletProps["a:buAutoNum"];
@@ -34338,7 +36346,10 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34338
36346
  fontFamily,
34339
36347
  sizePercent,
34340
36348
  sizePts,
34341
- color
36349
+ color,
36350
+ ...fontInherit ? { fontInherit: true } : {},
36351
+ ...colorInherit ? { colorInherit: true } : {},
36352
+ ...sizeInherit ? { sizeInherit: true } : {}
34342
36353
  };
34343
36354
  }
34344
36355
  const buBlip = resolvedBulletProps["a:buBlip"];
@@ -34450,7 +36461,7 @@ var PptxHandlerRuntime40 = class extends PptxHandlerRuntime39 {
34450
36461
  };
34451
36462
 
34452
36463
  // src/core/core/runtime/PptxHandlerRuntimeShapeBodyParsing.ts
34453
- var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime40 {
36464
+ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
34454
36465
  /**
34455
36466
  * Parse `a:spLocks` attributes into a structured {@link PptxShapeLocks} object.
34456
36467
  * Returns `undefined` when the node is absent or contains no lock attributes.
@@ -34545,7 +36556,8 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34545
36556
  if (Number.isFinite(spcColRaw) && spcColRaw > 0) {
34546
36557
  textStyle.columnSpacing = spcColRaw / _PptxHandlerRuntime.EMU_PER_PX;
34547
36558
  }
34548
- const hOverflow = String(bodyPr["@_hOverflow"] || "").trim();
36559
+ const hOverflowRaw = bodyPr["@_horzOverflow"] ?? bodyPr["@_hOverflow"];
36560
+ const hOverflow = String(hOverflowRaw || "").trim();
34549
36561
  if (hOverflow === "overflow" || hOverflow === "clip") {
34550
36562
  textStyle.hOverflow = hOverflow;
34551
36563
  }
@@ -34728,7 +36740,7 @@ var PptxHandlerRuntime41 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34728
36740
  };
34729
36741
 
34730
36742
  // src/core/core/runtime/PptxHandlerRuntimeShapeTextParsing.ts
34731
- var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime41 {
36743
+ var PptxHandlerRuntime43 = class _PptxHandlerRuntime extends PptxHandlerRuntime42 {
34732
36744
  /**
34733
36745
  * Resolve paragraph-level styles (alignment, spacing, margins, tabs,
34734
36746
  * level styles) for a single paragraph. Modifies `textStyle` in place
@@ -34910,7 +36922,7 @@ var PptxHandlerRuntime42 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
34910
36922
  };
34911
36923
 
34912
36924
  // src/core/core/runtime/PptxHandlerRuntimeShapeParagraphContentParsing.ts
34913
- var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
36925
+ var PptxHandlerRuntime44 = class extends PptxHandlerRuntime43 {
34914
36926
  /**
34915
36927
  * Collect text content (runs, fields, equations, bullets) for a single
34916
36928
  * paragraph and return text parts + segments. The returned `seedStyle`
@@ -35093,6 +37105,23 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
35093
37105
  parts.push("\n");
35094
37106
  segments.push({ text: "\n", style: { ...mergedDefaultRunStyle } });
35095
37107
  }
37108
+ const firstSegmentIndex = segments.length === 0 ? -1 : 0;
37109
+ if (firstSegmentIndex >= 0) {
37110
+ const pPrRaw = p["a:pPr"];
37111
+ const lvlRaw = pPrRaw?.["@_lvl"];
37112
+ if (lvlRaw !== void 0) {
37113
+ const lvlParsed = Number.parseInt(String(lvlRaw), 10);
37114
+ if (Number.isFinite(lvlParsed) && lvlParsed > 0) {
37115
+ segments[firstSegmentIndex].paragraphLevel = Math.min(Math.max(lvlParsed, 0), 8);
37116
+ }
37117
+ }
37118
+ const endParaRPrRaw = p["a:endParaRPr"];
37119
+ if (endParaRPrRaw && typeof endParaRPrRaw === "object") {
37120
+ segments[firstSegmentIndex].endParaRunProperties = {
37121
+ ...endParaRPrRaw
37122
+ };
37123
+ }
37124
+ }
35096
37125
  return { parts, segments, seedStyle };
35097
37126
  }
35098
37127
  /**
@@ -35202,7 +37231,7 @@ var PptxHandlerRuntime43 = class extends PptxHandlerRuntime42 {
35202
37231
  };
35203
37232
 
35204
37233
  // src/core/core/runtime/PptxHandlerRuntimeShapeParsing.ts
35205
- var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime43 {
37234
+ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
35206
37235
  parseShape(shape, id, slidePath) {
35207
37236
  try {
35208
37237
  const spPr = shape["p:spPr"];
@@ -35238,6 +37267,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
35238
37267
  let pathData;
35239
37268
  let pathWidth;
35240
37269
  let pathHeight;
37270
+ let customGeometryRawData;
35241
37271
  const custGeom = effectiveSpPr?.["a:custGeom"];
35242
37272
  if (custGeom) {
35243
37273
  const customPath = this.parseCustomGeometry(
@@ -35250,6 +37280,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
35250
37280
  pathData = customPath.pathData;
35251
37281
  pathWidth = customPath.pathWidth;
35252
37282
  pathHeight = customPath.pathHeight;
37283
+ customGeometryRawData = this.extractCustomGeometryRawData(custGeom);
35253
37284
  }
35254
37285
  }
35255
37286
  const geomNode = custGeom ?? effectiveSpPr?.["a:prstGeom"];
@@ -35407,7 +37438,8 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
35407
37438
  type: "shape",
35408
37439
  pathData,
35409
37440
  pathWidth,
35410
- pathHeight
37441
+ pathHeight,
37442
+ customGeometryRawData
35411
37443
  };
35412
37444
  } catch (e) {
35413
37445
  console.warn(`[pptx] Skipping shape element (${id}):`, e);
@@ -35417,7 +37449,7 @@ var PptxHandlerRuntime44 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
35417
37449
  };
35418
37450
 
35419
37451
  // src/core/core/runtime/PptxHandlerRuntimePictureParsing.ts
35420
- var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime44 {
37452
+ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
35421
37453
  async parsePicture(pic, id, slidePath) {
35422
37454
  try {
35423
37455
  const spPr = pic["p:spPr"];
@@ -35656,7 +37688,7 @@ var PptxHandlerRuntime45 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
35656
37688
  };
35657
37689
 
35658
37690
  // src/core/core/runtime/PptxHandlerRuntimeSpTreeParsing.ts
35659
- var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime45 {
37691
+ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
35660
37692
  /**
35661
37693
  * Known element tag names that appear as direct children of `p:spTree`
35662
37694
  * (or `p:grpSp`) and represent renderable shapes/objects.
@@ -36001,9 +38033,18 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
36001
38033
  * Unwrap mc:AlternateContent elements within a shape tree (or group),
36002
38034
  * merging selected branch children into the parent element arrays.
36003
38035
  * Delegates to the standalone alternate-content utility.
38036
+ *
38037
+ * Records each consumed AC envelope in {@link alternateContentBlockByRawXml}
38038
+ * so the save layer can re-emit the original `<mc:Choice>` /
38039
+ * `<mc:Fallback>` shape on dirty save (CC-4).
36004
38040
  */
36005
38041
  unwrapAlternateContent(container) {
36006
- unwrapAlternateContent(container);
38042
+ const blocks = unwrapAlternateContent(container);
38043
+ for (const block of blocks) {
38044
+ for (const ref of block.childRefs) {
38045
+ this.alternateContentBlockByRawXml.set(ref.node, block);
38046
+ }
38047
+ }
36007
38048
  }
36008
38049
  /**
36009
38050
  * Forward declaration – implemented in PptxHandlerRuntimeGroupParsing.
@@ -36014,7 +38055,7 @@ var PptxHandlerRuntime46 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
36014
38055
  };
36015
38056
 
36016
38057
  // src/core/core/runtime/PptxHandlerRuntimeGroupParsing.ts
36017
- var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime46 {
38058
+ var PptxHandlerRuntime48 = class _PptxHandlerRuntime extends PptxHandlerRuntime47 {
36018
38059
  async parseGroupShape(group, baseId, slidePath, rawXmlStr) {
36019
38060
  const grpSpPr = group["p:grpSpPr"];
36020
38061
  const xfrm = grpSpPr?.["a:xfrm"];
@@ -36187,7 +38228,7 @@ var PptxHandlerRuntime47 = class _PptxHandlerRuntime extends PptxHandlerRuntime4
36187
38228
  };
36188
38229
 
36189
38230
  // src/core/core/runtime/PptxHandlerRuntimeSlideParsing.ts
36190
- var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
38231
+ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
36191
38232
  /**
36192
38233
  * Parse text body from a connector shape (p:cxnSp > p:txBody).
36193
38234
  * Uses the same text run extraction logic as regular shapes but
@@ -36299,7 +38340,7 @@ var PptxHandlerRuntime48 = class extends PptxHandlerRuntime47 {
36299
38340
  };
36300
38341
 
36301
38342
  // src/core/core/runtime/PptxHandlerRuntimeColorAndEffects.ts
36302
- var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
38343
+ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
36303
38344
  /**
36304
38345
  * Forward declaration – implemented in PptxHandlerRuntimeThemeProcessing.
36305
38346
  * Re-resolves gradient stops by substituting `phClr` with the given colour.
@@ -36340,8 +38381,9 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
36340
38381
  return void 0;
36341
38382
  }
36342
38383
  const resolvedKey = normalized === "phclr" ? "accent1" : normalized;
36343
- if (this.currentSlideClrMapOverride) {
36344
- const remapped = this.currentSlideClrMapOverride[resolvedKey];
38384
+ const overrideMap = this.currentSlideClrMapOverride ?? this.currentMasterClrMap;
38385
+ if (overrideMap) {
38386
+ const remapped = overrideMap[resolvedKey];
36345
38387
  if (remapped) {
36346
38388
  return this.themeColorMap[remapped] || this.getDefaultSchemeColorMap()[remapped];
36347
38389
  }
@@ -36424,7 +38466,7 @@ var PptxHandlerRuntime49 = class extends PptxHandlerRuntime48 {
36424
38466
  };
36425
38467
 
36426
38468
  // src/core/core/runtime/PptxHandlerRuntimeBackgroundParsing.ts
36427
- var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
38469
+ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
36428
38470
  async extractBackgroundImage(slideXml2, slidePath, rootElement = "p:sld") {
36429
38471
  try {
36430
38472
  const bg = slideXml2[rootElement]?.["p:cSld"]?.["p:bg"];
@@ -36635,7 +38677,7 @@ var PptxHandlerRuntime50 = class extends PptxHandlerRuntime49 {
36635
38677
  };
36636
38678
 
36637
38679
  // src/core/core/runtime/PptxHandlerRuntimeSlideUtils.ts
36638
- var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
38680
+ var PptxHandlerRuntime52 = class extends PptxHandlerRuntime51 {
36639
38681
  /**
36640
38682
  * Retrieve the background gradient from a layout, falling back to master.
36641
38683
  */
@@ -36706,6 +38748,64 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
36706
38748
  }
36707
38749
  return void 0;
36708
38750
  }
38751
+ /**
38752
+ * Find the master file path referenced by a layout via its relationships.
38753
+ */
38754
+ findMasterPathForLayoutBase(layoutPath) {
38755
+ const layoutRels = this.slideRelsMap.get(layoutPath);
38756
+ if (!layoutRels) {
38757
+ return void 0;
38758
+ }
38759
+ for (const [, target] of layoutRels.entries()) {
38760
+ if (target.includes("slideMaster")) {
38761
+ const layoutDir = layoutPath.substring(0, layoutPath.lastIndexOf("/") + 1);
38762
+ return target.startsWith("..") ? this.resolvePath(layoutDir, target) : `ppt/${target.replace("../", "")}`;
38763
+ }
38764
+ }
38765
+ return void 0;
38766
+ }
38767
+ /**
38768
+ * Switch the active master state (clrMap + theme color/font/format
38769
+ * scheme) so that scheme-colour resolution for the slide currently
38770
+ * being parsed walks through the correct master.
38771
+ *
38772
+ * Multi-master decks must resolve scheme colours against each slide's
38773
+ * own master rather than always against `masterFiles[0]`.
38774
+ *
38775
+ * Phase 2 Stream B / C-H4.
38776
+ */
38777
+ async setActiveMasterForSlide(slidePath) {
38778
+ const layoutPath = this.findLayoutPathForSlide(slidePath);
38779
+ if (!layoutPath) {
38780
+ this.currentMasterClrMap = null;
38781
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
38782
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
38783
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
38784
+ return;
38785
+ }
38786
+ if (!this.slideRelsMap.has(layoutPath)) {
38787
+ const layoutRelsPath = `${layoutPath.replace("slideLayouts/", "slideLayouts/_rels/")}.rels`;
38788
+ try {
38789
+ await this.loadSlideRelationships(layoutPath, layoutRelsPath);
38790
+ } catch {
38791
+ }
38792
+ }
38793
+ const masterPath = this.findMasterPathForLayoutBase(layoutPath);
38794
+ if (!masterPath) {
38795
+ this.currentMasterClrMap = null;
38796
+ this.themeColorMap = { ...this.globalThemeColorMapSnapshot };
38797
+ this.themeFontMap = { ...this.globalThemeFontMapSnapshot };
38798
+ this.themeFormatScheme = this.globalThemeFormatSchemeSnapshot;
38799
+ return;
38800
+ }
38801
+ this.currentMasterClrMap = this.masterClrMaps.get(masterPath) ?? null;
38802
+ const masterColorMap = this.masterThemeColorMaps.get(masterPath);
38803
+ this.themeColorMap = masterColorMap ? { ...masterColorMap } : { ...this.globalThemeColorMapSnapshot };
38804
+ const masterFontMap = this.masterThemeFontMaps.get(masterPath);
38805
+ this.themeFontMap = masterFontMap ? { ...masterFontMap } : { ...this.globalThemeFontMapSnapshot };
38806
+ const masterFormatScheme = this.masterThemeFormatSchemes.get(masterPath);
38807
+ this.themeFormatScheme = masterFormatScheme ?? this.globalThemeFormatSchemeSnapshot;
38808
+ }
36709
38809
  /**
36710
38810
  * Extract the `p:bg/@showAnimation` flag from a slide's XML.
36711
38811
  * Returns `true` when the background should animate, `false` when
@@ -36846,7 +38946,7 @@ var PptxHandlerRuntime51 = class extends PptxHandlerRuntime50 {
36846
38946
  };
36847
38947
 
36848
38948
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderStyles.ts
36849
- var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime51 {
38949
+ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
36850
38950
  /**
36851
38951
  * Parse a single `a:lvlXpPr` node into a structured
36852
38952
  * {@link PlaceholderTextLevelStyle}.
@@ -36967,7 +39067,7 @@ var PptxHandlerRuntime52 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
36967
39067
  };
36968
39068
 
36969
39069
  // src/core/core/runtime/PptxHandlerRuntimePlaceholderDefaults.ts
36970
- var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime52 {
39070
+ var PptxHandlerRuntime54 = class _PptxHandlerRuntime extends PptxHandlerRuntime53 {
36971
39071
  /**
36972
39072
  * Extract structured {@link PlaceholderDefaults} from a layout or master
36973
39073
  * shape that carries a `p:ph` element.
@@ -37110,7 +39210,118 @@ var PptxHandlerRuntime53 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
37110
39210
  };
37111
39211
 
37112
39212
  // src/core/core/runtime/PptxHandlerRuntimeMasterElements.ts
37113
- var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
39213
+ function parseHeaderFooterFlags(hf) {
39214
+ if (!hf) {
39215
+ return void 0;
39216
+ }
39217
+ const result = {};
39218
+ if (hf["@_hdr"] !== void 0) {
39219
+ result.hasHeader = String(hf["@_hdr"]) !== "0";
39220
+ }
39221
+ if (hf["@_ftr"] !== void 0) {
39222
+ result.hasFooter = String(hf["@_ftr"]) !== "0";
39223
+ }
39224
+ if (hf["@_dt"] !== void 0) {
39225
+ result.hasDateTime = String(hf["@_dt"]) !== "0";
39226
+ }
39227
+ if (hf["@_sldNum"] !== void 0) {
39228
+ result.hasSlideNumber = String(hf["@_sldNum"]) !== "0";
39229
+ }
39230
+ return Object.keys(result).length > 0 ? result : void 0;
39231
+ }
39232
+ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
39233
+ /**
39234
+ * Parse a `CT_TextListStyle` node (`a:defPPr` + `a:lvl1pPr` … `a:lvl9pPr`)
39235
+ * into a level-keyed style map. Used for `<p:txStyles>` children
39236
+ * (`p:titleStyle`, `p:bodyStyle`, `p:otherStyle`) — see ECMA-376 §19.3.1.52.
39237
+ */
39238
+ parseTextListStyle(node) {
39239
+ if (!node) {
39240
+ return void 0;
39241
+ }
39242
+ const levels = {};
39243
+ const defParsed = this.parsePlaceholderLevelStyle(node["a:defPPr"]);
39244
+ if (defParsed) {
39245
+ levels[-1] = defParsed;
39246
+ }
39247
+ for (let lvl = 1; lvl <= 9; lvl++) {
39248
+ const parsed = this.parsePlaceholderLevelStyle(
39249
+ node[`a:lvl${lvl}pPr`]
39250
+ );
39251
+ if (parsed) {
39252
+ levels[lvl - 1] = parsed;
39253
+ }
39254
+ }
39255
+ return Object.keys(levels).length > 0 ? levels : void 0;
39256
+ }
39257
+ /**
39258
+ * Parse `<p:txStyles>` from a slide-master XML object into a structured
39259
+ * {@link PptxMasterTextStyles}. Used to populate `PptxSlideMaster.txStyles`
39260
+ * so the title/body/other text-style cascade (P-H1) is visible on the
39261
+ * typed model.
39262
+ */
39263
+ parseMasterTxStyles(masterXml) {
39264
+ const txStyles = masterXml?.["p:txStyles"];
39265
+ if (!txStyles) {
39266
+ return void 0;
39267
+ }
39268
+ const titleStyle = this.parseTextListStyle(txStyles["p:titleStyle"]);
39269
+ const bodyStyle = this.parseTextListStyle(txStyles["p:bodyStyle"]);
39270
+ const otherStyle = this.parseTextListStyle(txStyles["p:otherStyle"]);
39271
+ if (!titleStyle && !bodyStyle && !otherStyle) {
39272
+ return void 0;
39273
+ }
39274
+ const result = {};
39275
+ if (titleStyle) {
39276
+ result.titleStyle = titleStyle;
39277
+ }
39278
+ if (bodyStyle) {
39279
+ result.bodyStyle = bodyStyle;
39280
+ }
39281
+ if (otherStyle) {
39282
+ result.otherStyle = otherStyle;
39283
+ }
39284
+ return result;
39285
+ }
39286
+ /**
39287
+ * Enrich an array of {@link PptxSlideMaster} entries (already produced by
39288
+ * `parseSlideMasters`) with parsed `<p:txStyles>`. Loads each master's XML
39289
+ * once, parses, and caches it in `masterXmlMap` for downstream consumers.
39290
+ *
39291
+ * Also stores the parsed result on the per-master cache so that the
39292
+ * inheritance chain in `applyMasterTextStyleCascade` can find it without
39293
+ * re-parsing.
39294
+ */
39295
+ async enrichSlideMastersWithTxStyles(slideMasters) {
39296
+ for (const master of slideMasters) {
39297
+ try {
39298
+ let masterXmlObj = this.masterXmlMap.get(master.path);
39299
+ if (!masterXmlObj) {
39300
+ const xmlStr = await this.zip.file(master.path)?.async("string");
39301
+ if (!xmlStr) {
39302
+ continue;
39303
+ }
39304
+ masterXmlObj = this.parser.parse(xmlStr);
39305
+ this.masterXmlMap.set(master.path, masterXmlObj);
39306
+ }
39307
+ const sldMaster = masterXmlObj["p:sldMaster"];
39308
+ if (!sldMaster) {
39309
+ continue;
39310
+ }
39311
+ const parsed = this.parseMasterTxStyles(sldMaster);
39312
+ if (parsed) {
39313
+ master.txStyles = parsed;
39314
+ this.masterTxStylesCache.set(master.path, parsed);
39315
+ }
39316
+ const hf = parseHeaderFooterFlags(sldMaster["p:hf"]);
39317
+ if (hf) {
39318
+ master.headerFooter = hf;
39319
+ }
39320
+ } catch (e) {
39321
+ console.warn("Failed to parse master txStyles:", e);
39322
+ }
39323
+ }
39324
+ }
37114
39325
  parsePresentationDefaultTextStyle() {
37115
39326
  const presentation = this.presentationData?.["p:presentation"];
37116
39327
  const defaultTextStyle = presentation?.["p:defaultTextStyle"];
@@ -37266,7 +39477,7 @@ var PptxHandlerRuntime54 = class extends PptxHandlerRuntime53 {
37266
39477
  };
37267
39478
 
37268
39479
  // src/core/core/runtime/PptxHandlerRuntimeLayoutElements.ts
37269
- var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
39480
+ var PptxHandlerRuntime56 = class extends PptxHandlerRuntime55 {
37270
39481
  async getLayoutElements(slidePath) {
37271
39482
  const slideRels = this.slideRelsMap.get(slidePath);
37272
39483
  if (!slideRels) {
@@ -37390,10 +39601,10 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37390
39601
  }
37391
39602
  }
37392
39603
  }
37393
- this.currentSlideClrMapOverride = prevClrMapOverride;
37394
39604
  const layoutShowMasterSp = layoutXmlObj["p:sldLayout"]?.["@_showMasterSp"];
37395
39605
  const showMasterSp = layoutShowMasterSp === void 0 || String(layoutShowMasterSp).trim().toLowerCase() !== "0" && String(layoutShowMasterSp).trim().toLowerCase() !== "false";
37396
39606
  const masterElements = showMasterSp ? await this.getMasterElements(layoutPath) : [];
39607
+ this.currentSlideClrMapOverride = prevClrMapOverride;
37397
39608
  const allElements = [...masterElements, ...elements];
37398
39609
  this.layoutCache.set(layoutPath, allElements);
37399
39610
  return allElements;
@@ -37405,7 +39616,7 @@ var PptxHandlerRuntime55 = class extends PptxHandlerRuntime54 {
37405
39616
  };
37406
39617
 
37407
39618
  // src/core/core/runtime/PptxHandlerRuntimeThemeFormatScheme.ts
37408
- var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime55 {
39619
+ var PptxHandlerRuntime57 = class _PptxHandlerRuntime extends PptxHandlerRuntime56 {
37409
39620
  /**
37410
39621
  * Collect fill-style children from a style list node, preserving
37411
39622
  * document order. Handles `a:solidFill`, `a:gradFill`, `a:pattFill`,
@@ -37647,7 +39858,7 @@ var PptxHandlerRuntime56 = class _PptxHandlerRuntime extends PptxHandlerRuntime5
37647
39858
  };
37648
39859
 
37649
39860
  // src/core/core/runtime/PptxHandlerRuntimeThemeOverrides.ts
37650
- var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
39861
+ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
37651
39862
  /**
37652
39863
  * Parse the `a:fmtScheme` element from the theme into a structured
37653
39864
  * {@link PptxThemeFormatScheme}. Each sub-list (fillStyleLst, lnStyleLst,
@@ -37844,8 +40055,10 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
37844
40055
  }
37845
40056
  const fontScheme = root["a:fontScheme"];
37846
40057
  if (fontScheme) {
37847
- const majorLatin = fontScheme["a:majorFont"]?.["a:latin"];
37848
- const minorLatin = fontScheme["a:minorFont"]?.["a:latin"];
40058
+ const majorFontNode = fontScheme["a:majorFont"];
40059
+ const minorFontNode = fontScheme["a:minorFont"];
40060
+ const majorLatin = majorFontNode?.["a:latin"];
40061
+ const minorLatin = minorFontNode?.["a:latin"];
37849
40062
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
37850
40063
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
37851
40064
  if (!result.colorOverrides) {
@@ -37868,13 +40081,46 @@ var PptxHandlerRuntime57 = class extends PptxHandlerRuntime56 {
37868
40081
  };
37869
40082
 
37870
40083
  // src/core/core/runtime/PptxHandlerRuntimeThemeRefResolution.ts
37871
- var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
40084
+ var COLOR_CHOICE_KEYS2 = [
40085
+ "a:scrgbClr",
40086
+ "a:srgbClr",
40087
+ "a:hslClr",
40088
+ "a:sysClr",
40089
+ "a:schemeClr",
40090
+ "a:prstClr"
40091
+ ];
40092
+ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
40093
+ /**
40094
+ * Pull the verbatim colour-choice child out of a style-matrix-reference
40095
+ * element (`a:lnRef`/`a:fillRef`/`a:effectRef`/`a:fontRef`). Returns the
40096
+ * full child object so it can be round-tripped at save time, preserving
40097
+ * any contained colour transforms (`a:lumMod`, `a:tint`, etc.).
40098
+ */
40099
+ extractRefColorXml(refNode) {
40100
+ if (!refNode) {
40101
+ return void 0;
40102
+ }
40103
+ for (const key of COLOR_CHOICE_KEYS2) {
40104
+ const child = refNode[key];
40105
+ if (child !== void 0) {
40106
+ return { [key]: child };
40107
+ }
40108
+ }
40109
+ return void 0;
40110
+ }
37872
40111
  /**
37873
40112
  * Resolve a `a:effectRef` element into concrete effect properties
37874
40113
  * by looking up `@_idx` in the theme format scheme's effect style list.
37875
40114
  */
37876
40115
  resolveThemeEffectRef(refNode, style) {
37877
40116
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
40117
+ if (Number.isFinite(idx) && idx > 0) {
40118
+ style.effectRefIdx = idx;
40119
+ }
40120
+ const overrideColorXml = this.extractRefColorXml(refNode);
40121
+ if (overrideColorXml) {
40122
+ style.effectRefColorXml = overrideColorXml;
40123
+ }
37878
40124
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.effectStyles.length) {
37879
40125
  return;
37880
40126
  }
@@ -37927,6 +40173,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
37927
40173
  resolveThemeLineRef(refNode, style) {
37928
40174
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
37929
40175
  const overrideColor = this.parseColor(refNode);
40176
+ if (Number.isFinite(idx) && idx > 0) {
40177
+ style.lnRefIdx = idx;
40178
+ }
40179
+ const overrideColorXml = this.extractRefColorXml(refNode);
40180
+ if (overrideColorXml) {
40181
+ style.lnRefColorXml = overrideColorXml;
40182
+ }
37930
40183
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme || idx > this.themeFormatScheme.lineStyles.length) {
37931
40184
  style.strokeColor = overrideColor;
37932
40185
  if (overrideColor) {
@@ -37998,6 +40251,13 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
37998
40251
  */
37999
40252
  resolveThemeFillRef(refNode, style) {
38000
40253
  const idx = parseInt(String(refNode["@_idx"] || "0"), 10);
40254
+ if (Number.isFinite(idx) && idx > 0) {
40255
+ style.fillRefIdx = idx;
40256
+ }
40257
+ const overrideColorXml = this.extractRefColorXml(refNode);
40258
+ if (overrideColorXml) {
40259
+ style.fillRefColorXml = overrideColorXml;
40260
+ }
38001
40261
  if (!Number.isFinite(idx) || idx <= 0 || !this.themeFormatScheme) {
38002
40262
  style.fillMode = "theme";
38003
40263
  style.fillColor = this.parseColor(refNode);
@@ -38063,7 +40323,7 @@ var PptxHandlerRuntime58 = class extends PptxHandlerRuntime57 {
38063
40323
  };
38064
40324
 
38065
40325
  // src/core/core/runtime/PptxHandlerRuntimeThemeLoading.ts
38066
- var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
40326
+ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
38067
40327
  async resolvePrimaryThemePath() {
38068
40328
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
38069
40329
  if (!masterFiles || masterFiles.length === 0) {
@@ -38173,62 +40433,97 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38173
40433
  formatScheme: this.themeFormatScheme
38174
40434
  };
38175
40435
  }
38176
- async applySlideMasterColorMap(defaultMap) {
40436
+ /**
40437
+ * Parse every slide master's `<p:clrMap>` element and store the alias
40438
+ * dictionaries on {@link masterClrMaps}. Do *not* mutate
40439
+ * {@link themeColorMap} — alias resolution happens at colour-lookup
40440
+ * time so that:
40441
+ *
40442
+ * 1. The raw theme scheme stays the source of truth (clrMap is a
40443
+ * routing layer, not a colour table).
40444
+ * 2. Multi-master decks resolve each slide against its own master's
40445
+ * clrMap rather than always against `masterFiles[0]`.
40446
+ * 3. Layout `clrMapOvr` semantics work correctly when a slide's master
40447
+ * differs from the deck's first master.
40448
+ *
40449
+ * Phase 2 Stream B / C-H4.
40450
+ */
40451
+ async applySlideMasterColorMap(_defaultMap) {
38177
40452
  const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
38178
40453
  if (!masterFiles || masterFiles.length === 0) {
38179
40454
  return;
38180
40455
  }
38181
- try {
38182
- const masterXml = await masterFiles[0].async("string");
38183
- const masterData = this.parser.parse(masterXml);
38184
- const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
38185
- if (!clrMap) {
38186
- return;
38187
- }
38188
- const aliasKeys = [
38189
- "bg1",
38190
- "tx1",
38191
- "bg2",
38192
- "tx2",
38193
- "accent1",
38194
- "accent2",
38195
- "accent3",
38196
- "accent4",
38197
- "accent5",
38198
- "accent6",
38199
- "hlink",
38200
- "folHlink"
38201
- ];
38202
- for (const aliasKey of aliasKeys) {
38203
- const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
38204
- if (!mappedKey) {
40456
+ const aliasKeys = [
40457
+ "bg1",
40458
+ "tx1",
40459
+ "bg2",
40460
+ "tx2",
40461
+ "accent1",
40462
+ "accent2",
40463
+ "accent3",
40464
+ "accent4",
40465
+ "accent5",
40466
+ "accent6",
40467
+ "hlink",
40468
+ "folHlink"
40469
+ ];
40470
+ for (const file of masterFiles) {
40471
+ try {
40472
+ const masterXml = await file.async("string");
40473
+ const masterData = this.parser.parse(masterXml);
40474
+ const clrMap = masterData?.["p:sldMaster"]?.["p:clrMap"];
40475
+ if (!clrMap) {
38205
40476
  continue;
38206
40477
  }
38207
- const mappedColor = this.themeColorMap[mappedKey] || defaultMap[mappedKey];
38208
- if (mappedColor) {
38209
- this.themeColorMap[aliasKey] = mappedColor;
40478
+ const aliasMap = {};
40479
+ for (const aliasKey of aliasKeys) {
40480
+ const mappedKey = String(clrMap[`@_${aliasKey}`] || "").trim().toLowerCase();
40481
+ if (mappedKey) {
40482
+ aliasMap[aliasKey] = mappedKey;
40483
+ }
40484
+ }
40485
+ if (Object.keys(aliasMap).length > 0) {
40486
+ this.masterClrMaps.set(file.name, aliasMap);
38210
40487
  }
40488
+ } catch (error) {
40489
+ console.warn(`Failed to parse slide master color map for ${file.name}:`, error);
38211
40490
  }
38212
- } catch (error) {
38213
- console.warn("Failed to parse slide master color map:", error);
38214
40491
  }
38215
40492
  }
38216
- async loadThemeData() {
38217
- const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
38218
- if (!themeFiles || themeFiles.length === 0) {
38219
- return;
40493
+ /**
40494
+ * Parse a single theme part into structured colour, font, and format
40495
+ * scheme dictionaries. Used both for the global default theme and for
40496
+ * each master's per-master theme (multi-master support).
40497
+ *
40498
+ * Phase 2 Stream B / C-H4.
40499
+ */
40500
+ async parseThemePart(themePath) {
40501
+ const themeFile = this.zip.file(themePath);
40502
+ if (!themeFile) {
40503
+ return null;
38220
40504
  }
38221
- const preferredThemePath = await this.resolvePrimaryThemePath();
38222
- const preferredThemeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) : void 0;
38223
- const themeFile = preferredThemeFile ?? themeFiles[0];
38224
40505
  const themeXml2 = await themeFile.async("string");
40506
+ this.originalThemeXmlByPath.set(themePath, themeXml2);
38225
40507
  const themeData = this.parser.parse(themeXml2);
38226
40508
  const themeRoot = themeData["a:theme"];
38227
40509
  const themeElements = themeRoot?.["a:themeElements"];
38228
40510
  const colorScheme = themeElements?.["a:clrScheme"];
38229
40511
  const fontScheme = themeElements?.["a:fontScheme"];
40512
+ const fmtScheme = themeElements?.["a:fmtScheme"];
40513
+ const themeName = String(themeRoot?.["@_name"] || "").trim();
40514
+ if (themeName) {
40515
+ this.masterThemeNames.set(themePath, themeName);
40516
+ }
40517
+ const colorSchemeName = String(colorScheme?.["@_name"] || "").trim();
40518
+ if (colorSchemeName) {
40519
+ this.masterThemeColorSchemeNames.set(themePath, colorSchemeName);
40520
+ }
40521
+ const fontSchemeName = String(fontScheme?.["@_name"] || "").trim();
40522
+ if (fontSchemeName) {
40523
+ this.masterThemeFontSchemeNames.set(themePath, fontSchemeName);
40524
+ }
38230
40525
  const defaultMap = this.getDefaultSchemeColorMap();
38231
- this.themeColorMap = { ...defaultMap };
40526
+ const colorMap = { ...defaultMap };
38232
40527
  if (colorScheme) {
38233
40528
  const schemeKeys = [
38234
40529
  "dk1",
@@ -38248,39 +40543,196 @@ var PptxHandlerRuntime59 = class extends PptxHandlerRuntime58 {
38248
40543
  const colorNode = colorScheme[`a:${key}`];
38249
40544
  const parsed = this.parseColorChoice(colorNode);
38250
40545
  if (parsed) {
38251
- this.themeColorMap[key] = parsed;
40546
+ colorMap[key] = parsed;
38252
40547
  }
38253
40548
  }
38254
40549
  }
38255
- this.themeColorMap["tx1"] = this.themeColorMap["dk1"] || defaultMap["dk1"];
38256
- this.themeColorMap["bg1"] = this.themeColorMap["lt1"] || defaultMap["lt1"];
38257
- this.themeColorMap["tx2"] = this.themeColorMap["dk2"] || defaultMap["dk2"];
38258
- this.themeColorMap["bg2"] = this.themeColorMap["lt2"] || defaultMap["lt2"];
38259
- await this.applySlideMasterColorMap(defaultMap);
38260
- const majorLatin = fontScheme?.["a:majorFont"]?.["a:latin"];
38261
- const minorLatin = fontScheme?.["a:minorFont"]?.["a:latin"];
40550
+ colorMap["tx1"] = colorMap["dk1"] || defaultMap["dk1"];
40551
+ colorMap["bg1"] = colorMap["lt1"] || defaultMap["lt1"];
40552
+ colorMap["tx2"] = colorMap["dk2"] || defaultMap["dk2"];
40553
+ colorMap["bg2"] = colorMap["lt2"] || defaultMap["lt2"];
40554
+ const majorFontNode = fontScheme?.["a:majorFont"];
40555
+ const minorFontNode = fontScheme?.["a:minorFont"];
40556
+ const majorLatin = majorFontNode?.["a:latin"];
40557
+ const minorLatin = minorFontNode?.["a:latin"];
38262
40558
  const majorFont = this.normalizeTypefaceToken(String(majorLatin?.["@_typeface"] || ""));
38263
40559
  const minorFont = this.normalizeTypefaceToken(String(minorLatin?.["@_typeface"] || ""));
38264
- this.themeFontMap = {};
40560
+ const fontMap = {};
38265
40561
  if (majorFont) {
38266
- this.themeFontMap["mj-lt"] = majorFont;
38267
- this.themeFontMap["mj-ea"] = majorFont;
38268
- this.themeFontMap["mj-cs"] = majorFont;
40562
+ fontMap["mj-lt"] = majorFont;
40563
+ fontMap["mj-ea"] = majorFont;
40564
+ fontMap["mj-cs"] = majorFont;
38269
40565
  }
38270
40566
  if (minorFont) {
38271
- this.themeFontMap["mn-lt"] = minorFont;
38272
- this.themeFontMap["mn-ea"] = minorFont;
38273
- this.themeFontMap["mn-cs"] = minorFont;
40567
+ fontMap["mn-lt"] = minorFont;
40568
+ fontMap["mn-ea"] = minorFont;
40569
+ fontMap["mn-cs"] = minorFont;
38274
40570
  }
38275
- const fmtScheme = themeElements?.["a:fmtScheme"];
38276
- if (fmtScheme) {
38277
- this.themeFormatScheme = this.parseFormatScheme(fmtScheme);
40571
+ const majorEa = this.normalizeTypefaceToken(
40572
+ String(majorFontNode?.["a:ea"]?.["@_typeface"] || "")
40573
+ );
40574
+ if (majorEa) {
40575
+ fontMap["mj-ea"] = majorEa;
40576
+ }
40577
+ const majorCs = this.normalizeTypefaceToken(
40578
+ String(majorFontNode?.["a:cs"]?.["@_typeface"] || "")
40579
+ );
40580
+ if (majorCs) {
40581
+ fontMap["mj-cs"] = majorCs;
40582
+ }
40583
+ const minorEa = this.normalizeTypefaceToken(
40584
+ String(minorFontNode?.["a:ea"]?.["@_typeface"] || "")
40585
+ );
40586
+ if (minorEa) {
40587
+ fontMap["mn-ea"] = minorEa;
40588
+ }
40589
+ const minorCs = this.normalizeTypefaceToken(
40590
+ String(minorFontNode?.["a:cs"]?.["@_typeface"] || "")
40591
+ );
40592
+ if (minorCs) {
40593
+ fontMap["mn-cs"] = minorCs;
40594
+ }
40595
+ const majorScripts = this.collectFontScriptOverrides(majorFontNode);
40596
+ if (Object.keys(majorScripts).length > 0) {
40597
+ this.masterThemeMajorFontScripts.set(themePath, majorScripts);
40598
+ }
40599
+ const minorScripts = this.collectFontScriptOverrides(minorFontNode);
40600
+ if (Object.keys(minorScripts).length > 0) {
40601
+ this.masterThemeMinorFontScripts.set(themePath, minorScripts);
40602
+ }
40603
+ const objectDefaultsNode = themeRoot?.["a:objectDefaults"];
40604
+ if (objectDefaultsNode) {
40605
+ const od = {
40606
+ spDef: objectDefaultsNode["a:spDef"],
40607
+ lnDef: objectDefaultsNode["a:lnDef"],
40608
+ txDef: objectDefaultsNode["a:txDef"]
40609
+ };
40610
+ if (od.spDef !== void 0 || od.lnDef !== void 0 || od.txDef !== void 0) {
40611
+ this.masterThemeObjectDefaults.set(themePath, od);
40612
+ }
40613
+ }
40614
+ const extraClrSchemeLst = themeRoot?.["a:extraClrSchemeLst"];
40615
+ if (extraClrSchemeLst !== void 0) {
40616
+ this.masterThemeExtraClrSchemeLst.set(themePath, extraClrSchemeLst);
40617
+ }
40618
+ const custClrLst = themeRoot?.["a:custClrLst"];
40619
+ if (custClrLst !== void 0) {
40620
+ this.masterThemeCustClrLst.set(themePath, custClrLst);
40621
+ }
40622
+ const themeExtLst = themeRoot?.["a:extLst"];
40623
+ if (themeExtLst !== void 0) {
40624
+ this.masterThemeExtLst.set(themePath, themeExtLst);
40625
+ }
40626
+ const formatScheme = fmtScheme ? this.parseFormatScheme(fmtScheme) : void 0;
40627
+ return { colorMap, fontMap, formatScheme };
40628
+ }
40629
+ /**
40630
+ * Parse `<a:font script="…" typeface="…"/>` children of a major or
40631
+ * minor font node into a `script -> typeface` dictionary.
40632
+ *
40633
+ * fast-xml-parser collapses repeated tags into arrays, so iterate
40634
+ * over the array form regardless of how many siblings are present.
40635
+ *
40636
+ * Phase 4 Stream A / M4.
40637
+ */
40638
+ collectFontScriptOverrides(fontNode) {
40639
+ const overrides = {};
40640
+ if (!fontNode) {
40641
+ return overrides;
40642
+ }
40643
+ const fontEntries = this.ensureArray(fontNode["a:font"]);
40644
+ for (const entry of fontEntries) {
40645
+ const script = String(entry?.["@_script"] || "").trim();
40646
+ const typeface = this.normalizeTypefaceToken(String(entry?.["@_typeface"] || ""));
40647
+ if (script && typeface) {
40648
+ overrides[script] = typeface;
40649
+ }
40650
+ }
40651
+ return overrides;
40652
+ }
40653
+ /**
40654
+ * Resolve the theme file path referenced by a given master's `.rels`.
40655
+ * Returns `undefined` when the master has no theme relationship.
40656
+ */
40657
+ async resolveThemePathForMaster(masterPath) {
40658
+ const relsPath = masterPath.replace(
40659
+ /ppt\/slideMasters\/(slideMaster\d+)\.xml/,
40660
+ "ppt/slideMasters/_rels/$1.xml.rels"
40661
+ );
40662
+ const relsXml = this.zip.file(relsPath);
40663
+ if (!relsXml) {
40664
+ return void 0;
40665
+ }
40666
+ const relsData = this.parser.parse(await relsXml.async("string"));
40667
+ const relNodes = this.ensureArray(relsData?.Relationships?.Relationship);
40668
+ for (const rel of relNodes) {
40669
+ const target = String(rel["@_Target"] || "");
40670
+ if (!target.includes("theme")) {
40671
+ continue;
40672
+ }
40673
+ const themePath = target.startsWith("..") ? this.resolvePath(masterPath.substring(0, masterPath.lastIndexOf("/") + 1), target) : target.startsWith("/") ? target.slice(1) : `ppt/${target.replace(/^\.?\//, "")}`;
40674
+ if (themePath.startsWith("ppt/theme/")) {
40675
+ return themePath;
40676
+ }
40677
+ }
40678
+ return void 0;
40679
+ }
40680
+ /**
40681
+ * Populate {@link masterThemeColorMaps}, {@link masterThemeFontMaps},
40682
+ * and {@link masterThemeFormatSchemes} for every slide master in the
40683
+ * deck. Multi-master support — Phase 2 Stream B / C-H4.
40684
+ */
40685
+ async loadPerMasterThemes() {
40686
+ const masterFiles = this.zip.file(/^ppt\/slideMasters\/slideMaster\d+\.xml$/);
40687
+ if (!masterFiles || masterFiles.length === 0) {
40688
+ return;
40689
+ }
40690
+ for (const file of masterFiles) {
40691
+ try {
40692
+ const themePath = await this.resolveThemePathForMaster(file.name);
40693
+ if (!themePath) {
40694
+ continue;
40695
+ }
40696
+ this.masterThemePaths.set(file.name, themePath);
40697
+ const parsed = await this.parseThemePart(themePath);
40698
+ if (!parsed) {
40699
+ continue;
40700
+ }
40701
+ this.masterThemeColorMaps.set(file.name, parsed.colorMap);
40702
+ this.masterThemeFontMaps.set(file.name, parsed.fontMap);
40703
+ if (parsed.formatScheme) {
40704
+ this.masterThemeFormatSchemes.set(file.name, parsed.formatScheme);
40705
+ }
40706
+ } catch (error) {
40707
+ console.warn(`Failed to load per-master theme for ${file.name}:`, error);
40708
+ }
40709
+ }
40710
+ }
40711
+ async loadThemeData() {
40712
+ const themeFiles = this.zip.file(/^ppt\/theme\/theme\d+\.xml$/);
40713
+ if (!themeFiles || themeFiles.length === 0) {
40714
+ return;
38278
40715
  }
40716
+ const preferredThemePath = await this.resolvePrimaryThemePath();
40717
+ const themeFile = preferredThemePath ? themeFiles.find((file) => file.name === preferredThemePath) ?? themeFiles[0] : themeFiles[0];
40718
+ const parsed = await this.parseThemePart(themeFile.name);
40719
+ if (parsed) {
40720
+ this.themeColorMap = parsed.colorMap;
40721
+ this.themeFontMap = parsed.fontMap;
40722
+ if (parsed.formatScheme) {
40723
+ this.themeFormatScheme = parsed.formatScheme;
40724
+ }
40725
+ }
40726
+ await this.applySlideMasterColorMap(this.getDefaultSchemeColorMap());
40727
+ await this.loadPerMasterThemes();
40728
+ this.globalThemeColorMapSnapshot = { ...this.themeColorMap };
40729
+ this.globalThemeFontMapSnapshot = { ...this.themeFontMap };
40730
+ this.globalThemeFormatSchemeSnapshot = this.themeFormatScheme;
38279
40731
  }
38280
40732
  };
38281
40733
 
38282
40734
  // src/core/core/runtime/PptxHandlerRuntimeThemeProcessing.ts
38283
- var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
40735
+ var PptxHandlerRuntime61 = class extends PptxHandlerRuntime60 {
38284
40736
  // ---------------------------------------------------------------------------
38285
40737
  // Theme editing — update colour scheme, font scheme, and name in the zip
38286
40738
  // ---------------------------------------------------------------------------
@@ -38430,7 +40882,7 @@ var PptxHandlerRuntime60 = class extends PptxHandlerRuntime59 {
38430
40882
  };
38431
40883
 
38432
40884
  // src/core/core/runtime/PptxHandlerRuntimeComments.ts
38433
- var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime60 {
40885
+ var PptxHandlerRuntime62 = class _PptxHandlerRuntime extends PptxHandlerRuntime61 {
38434
40886
  /**
38435
40887
  * Parse modern threaded comments (PowerPoint 2019+ / Office 365).
38436
40888
  * Modern comments use `p188:cmLst`/`p15:cmLst` roots within
@@ -38663,7 +41115,7 @@ var PptxHandlerRuntime61 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
38663
41115
  };
38664
41116
 
38665
41117
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtXmlUtils.ts
38666
- var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
41118
+ var PptxHandlerRuntime63 = class extends PptxHandlerRuntime62 {
38667
41119
  async readXmlPartByRelationshipId(slidePath, relationshipId) {
38668
41120
  const normalizedRelationshipId = String(relationshipId || "").trim();
38669
41121
  if (normalizedRelationshipId.length === 0) {
@@ -38818,7 +41270,7 @@ var PptxHandlerRuntime62 = class extends PptxHandlerRuntime61 {
38818
41270
  };
38819
41271
 
38820
41272
  // src/core/core/runtime/PptxHandlerRuntimeSmartArtParsing.ts
38821
- var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime62 {
41273
+ var PptxHandlerRuntime64 = class _PptxHandlerRuntime extends PptxHandlerRuntime63 {
38822
41274
  /**
38823
41275
  * Parse quick style from `ppt/diagrams/quickStyles*.xml`.
38824
41276
  */
@@ -38977,7 +41429,7 @@ var PptxHandlerRuntime63 = class _PptxHandlerRuntime extends PptxHandlerRuntime6
38977
41429
  };
38978
41430
 
38979
41431
  // src/core/core/runtime/PptxHandlerRuntimeSmartArt.ts
38980
- var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
41432
+ var PptxHandlerRuntime65 = class _PptxHandlerRuntime extends PptxHandlerRuntime64 {
38981
41433
  async getSmartArtDataForGraphicFrame(slidePath, graphicFrame) {
38982
41434
  const graphicData = this.xmlLookupService.getChildByLocalName(
38983
41435
  this.xmlLookupService.getChildByLocalName(graphicFrame, "graphic"),
@@ -39028,10 +41480,14 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
39028
41480
  const layoutPart = layoutRelationshipId.length > 0 ? await this.readXmlPartByRelationshipId(slidePath, layoutRelationshipId) : void 0;
39029
41481
  const layoutType = layoutPart?.partPath?.split("/").pop()?.replace(/\.[^.]+$/, "") || void 0;
39030
41482
  const chrome = this.parseSmartArtChrome(dataModel);
39031
- const drawingRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
39032
- const drawingShapes = await this.parseSmartArtDrawingShapes(slidePath, drawingRelationshipId);
39033
41483
  const colorsRelationshipId = String(relationshipIds["@_r:cs"] || "").trim();
39034
41484
  const colorTransform = await this.parseSmartArtColorTransform(slidePath, colorsRelationshipId);
41485
+ const drawingResolution = await this.resolveSmartArtDrawingPart(
41486
+ slidePath,
41487
+ diagramDataRelationshipId
41488
+ );
41489
+ const drawingShapes = drawingResolution ? await this.parseSmartArtDrawingShapesFromPath(drawingResolution.path) : [];
41490
+ const drawingRelationshipId = drawingResolution?.relId;
39035
41491
  const styleRelationshipId = String(relationshipIds["@_r:qs"] || "").trim();
39036
41492
  const quickStyle = await this.parseSmartArtQuickStyle(slidePath, styleRelationshipId);
39037
41493
  return {
@@ -39043,11 +41499,87 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
39043
41499
  colorTransform,
39044
41500
  quickStyle,
39045
41501
  dataRelId: diagramDataRelationshipId,
39046
- drawingRelId: drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
41502
+ drawingRelId: drawingRelationshipId && drawingRelationshipId.length > 0 ? drawingRelationshipId : void 0,
39047
41503
  colorsRelId: colorsRelationshipId.length > 0 ? colorsRelationshipId : void 0,
39048
41504
  styleRelId: styleRelationshipId.length > 0 ? styleRelationshipId : void 0
39049
41505
  };
39050
41506
  }
41507
+ /**
41508
+ * Resolve the SmartArt drawing-shapes part path + relationship id.
41509
+ *
41510
+ * Strategy:
41511
+ * 1. Locate the data-model part's rels file
41512
+ * (`ppt/diagrams/_rels/data*.xml.rels`) via `slideRelsMap`.
41513
+ * 2. Find a relationship whose `Type` matches the `…/diagramDrawing`
41514
+ * URI. PowerPoint emits this for any deck that has had its
41515
+ * SmartArt rendered to drawing shapes.
41516
+ * 3. Return the matched part path (so the caller can load it
41517
+ * directly) and the relationship id (for round-trip preservation).
41518
+ */
41519
+ async resolveSmartArtDrawingPart(slidePath, diagramDataRelationshipId) {
41520
+ if (diagramDataRelationshipId.length === 0) {
41521
+ return void 0;
41522
+ }
41523
+ const slideRels = this.slideRelsMap.get(slidePath);
41524
+ const dataTarget = slideRels?.get(diagramDataRelationshipId);
41525
+ if (!dataTarget) {
41526
+ return void 0;
41527
+ }
41528
+ const dataPath = this.resolveImagePath(slidePath, dataTarget);
41529
+ const dataDir = dataPath.replace(/\/[^/]+$/, "");
41530
+ const dataFile = dataPath.split("/").pop() ?? "";
41531
+ const dataRelsPath = `${dataDir}/_rels/${dataFile}.rels`;
41532
+ const relsXml = await this.zip.file(dataRelsPath)?.async("string");
41533
+ if (!relsXml) {
41534
+ return void 0;
41535
+ }
41536
+ try {
41537
+ const parsed = this.parser.parse(relsXml);
41538
+ const relsRoot = parsed["Relationships"];
41539
+ if (!relsRoot) {
41540
+ return void 0;
41541
+ }
41542
+ const rels = this.ensureArray(relsRoot["Relationship"]);
41543
+ const drawingRel = rels.find(
41544
+ (rel) => String(rel?.["@_Type"] || "").endsWith("/diagramDrawing")
41545
+ );
41546
+ const id = String(drawingRel?.["@_Id"] || "").trim();
41547
+ const target = String(drawingRel?.["@_Target"] || "").trim();
41548
+ if (id.length === 0 || target.length === 0) {
41549
+ return void 0;
41550
+ }
41551
+ const drawingPath = this.resolveImagePath(dataPath, target);
41552
+ return { relId: id, path: drawingPath };
41553
+ } catch {
41554
+ return void 0;
41555
+ }
41556
+ }
41557
+ /**
41558
+ * Parse SmartArt drawing shapes given an absolute part path.
41559
+ *
41560
+ * Wraps `parseSmartArtDrawingShapes` (which expects a slide-relative
41561
+ * relationship id) with a path-based lookup so the resolution layer
41562
+ * can pull the part from anywhere in the package.
41563
+ */
41564
+ async parseSmartArtDrawingShapesFromPath(drawingPath) {
41565
+ const xmlString = await this.zip.file(drawingPath)?.async("string");
41566
+ if (!xmlString) {
41567
+ return [];
41568
+ }
41569
+ try {
41570
+ const xml = this.parser.parse(xmlString);
41571
+ const drawing = this.xmlLookupService.getChildByLocalName(xml, "drawing");
41572
+ const spTree = this.xmlLookupService.getChildByLocalName(drawing || xml, "spTree");
41573
+ if (!spTree) {
41574
+ return [];
41575
+ }
41576
+ const shapes = this.xmlLookupService.getChildrenArrayByLocalName(spTree, "sp");
41577
+ const emuPerPx = _PptxHandlerRuntime.EMU_PER_PX;
41578
+ return shapes.map((sp, index) => this.parseDrawingShape(sp, index, emuPerPx)).filter((entry) => entry !== null);
41579
+ } catch {
41580
+ return [];
41581
+ }
41582
+ }
39051
41583
  parseSmartArtConnections(dataModel) {
39052
41584
  const connectionList = this.xmlLookupService.getChildByLocalName(dataModel, "cxnLst");
39053
41585
  const rawConnections = this.xmlLookupService.getChildrenArrayByLocalName(connectionList, "cxn");
@@ -39078,7 +41610,7 @@ var PptxHandlerRuntime64 = class extends PptxHandlerRuntime63 {
39078
41610
  };
39079
41611
 
39080
41612
  // src/core/core/runtime/PptxHandlerRuntimeChartDetection.ts
39081
- var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
41613
+ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
39082
41614
  detectChartType(plotArea) {
39083
41615
  if (!plotArea) {
39084
41616
  return "unknown";
@@ -39187,7 +41719,7 @@ var PptxHandlerRuntime65 = class extends PptxHandlerRuntime64 {
39187
41719
  };
39188
41720
 
39189
41721
  // src/core/core/runtime/PptxHandlerRuntimeChartParsingHelpers.ts
39190
- var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
41722
+ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
39191
41723
  /**
39192
41724
  * Parse `c:plotVisOnly` from the chart root element.
39193
41725
  *
@@ -39248,7 +41780,7 @@ var PptxHandlerRuntime66 = class extends PptxHandlerRuntime65 {
39248
41780
  };
39249
41781
 
39250
41782
  // src/core/core/runtime/PptxHandlerRuntimeChartExternalData.ts
39251
- var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
41783
+ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
39252
41784
  /**
39253
41785
  * Parse `c:externalData` from the chart's `c:chartSpace` and resolve
39254
41786
  * the external relationship target from the chart part's .rels file.
@@ -39364,7 +41896,7 @@ var PptxHandlerRuntime67 = class extends PptxHandlerRuntime66 {
39364
41896
  };
39365
41897
 
39366
41898
  // src/core/core/runtime/PptxHandlerRuntimeChartColorStyle.ts
39367
- var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
41899
+ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
39368
41900
  /**
39369
41901
  * Parse the Office 2013+ chart color style part (`chartColorStyle*.xml`)
39370
41902
  * referenced from the chart's relationships.
@@ -39471,7 +42003,7 @@ var PptxHandlerRuntime68 = class extends PptxHandlerRuntime67 {
39471
42003
  };
39472
42004
 
39473
42005
  // src/core/core/runtime/PptxHandlerRuntimeChartParsing.ts
39474
- var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
42006
+ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
39475
42007
  /**
39476
42008
  * Parse chart data from a graphic frame element on a slide.
39477
42009
  *
@@ -39718,7 +42250,7 @@ var PptxHandlerRuntime69 = class extends PptxHandlerRuntime68 {
39718
42250
  };
39719
42251
 
39720
42252
  // src/core/core/runtime/PptxHandlerRuntimePresentationStructure.ts
39721
- var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
42253
+ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
39722
42254
  parseEditorAnimations(slideXml2) {
39723
42255
  return this.editorAnimationService.parseEditorAnimations(slideXml2);
39724
42256
  }
@@ -39963,7 +42495,7 @@ var PptxHandlerRuntime70 = class extends PptxHandlerRuntime69 {
39963
42495
  };
39964
42496
 
39965
42497
  // src/core/core/runtime/PptxHandlerRuntimeEmbeddedFonts.ts
39966
- var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
42498
+ var PptxHandlerRuntime72 = class extends PptxHandlerRuntime71 {
39967
42499
  async getEmbeddedFonts() {
39968
42500
  const embeddedFontEntries = this.ensureArray(
39969
42501
  this.presentationData?.["p:presentation"]?.["p:embeddedFontLst"]?.["p:embeddedFont"]
@@ -40135,7 +42667,7 @@ var PptxHandlerRuntime71 = class extends PptxHandlerRuntime70 {
40135
42667
  };
40136
42668
 
40137
42669
  // src/core/core/runtime/PptxHandlerRuntimeLoadSession.ts
40138
- var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime71 {
42670
+ var PptxHandlerRuntime73 = class _PptxHandlerRuntime extends PptxHandlerRuntime72 {
40139
42671
  isZipContainer(data) {
40140
42672
  const bytes = new Uint8Array(data);
40141
42673
  if (bytes.byteLength < 4) {
@@ -40178,6 +42710,23 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
40178
42710
  this.imageDataCache.clear();
40179
42711
  this.themeColorMap = {};
40180
42712
  this.themeFontMap = {};
42713
+ this.masterClrMaps.clear();
42714
+ this.masterThemeColorMaps.clear();
42715
+ this.masterThemeFontMaps.clear();
42716
+ this.masterThemeFormatSchemes.clear();
42717
+ this.masterThemePaths.clear();
42718
+ this.masterThemeMajorFontScripts.clear();
42719
+ this.masterThemeMinorFontScripts.clear();
42720
+ this.masterThemeNames.clear();
42721
+ this.masterThemeFontSchemeNames.clear();
42722
+ this.masterThemeColorSchemeNames.clear();
42723
+ this.originalThemeXmlByPath.clear();
42724
+ this.dirtyThemePaths.clear();
42725
+ this.masterThemeObjectDefaults.clear();
42726
+ this.masterThemeExtraClrSchemeLst.clear();
42727
+ this.masterThemeCustClrLst.clear();
42728
+ this.masterThemeExtLst.clear();
42729
+ this.currentMasterClrMap = null;
40181
42730
  this.presentationDefaultTextStyle = void 0;
40182
42731
  this.commentAuthorMap.clear();
40183
42732
  this.commentAuthorDetails.clear();
@@ -40332,6 +42881,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
40332
42881
  setCurrentSlideClrMapOverride: (override) => {
40333
42882
  this.currentSlideClrMapOverride = override;
40334
42883
  },
42884
+ setActiveMasterForSlide: (slidePath) => this.setActiveMasterForSlide(slidePath),
40335
42885
  findLayoutPathForSlide: (slidePath) => this.findLayoutPathForSlide(slidePath),
40336
42886
  loadThemeOverride: (partBasePath) => this.loadThemeOverride(partBasePath),
40337
42887
  applyThemeOverrideState: (override) => this.applyThemeOverrideState(override),
@@ -40362,7 +42912,7 @@ var PptxHandlerRuntime72 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
40362
42912
  };
40363
42913
 
40364
42914
  // src/core/core/runtime/PptxHandlerRuntimeLoadPipeline.ts
40365
- var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
42915
+ var PptxHandlerRuntime74 = class extends PptxHandlerRuntime73 {
40366
42916
  async buildLoadData(presentationState, slidesWithWarnings) {
40367
42917
  const headerFooter = this.extractHeaderFooter();
40368
42918
  const presentationProperties = await this.parsePresentationProperties();
@@ -40374,6 +42924,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
40374
42924
  const notesMaster = await this.parseNotesMaster();
40375
42925
  const handoutMaster = await this.parseHandoutMaster();
40376
42926
  const slideMasters = await this.parseSlideMasters();
42927
+ await this.enrichSlideMastersWithTxStyles(slideMasters);
40377
42928
  const tags = await this.parseTags();
40378
42929
  const customProperties = await this.parseCustomProperties();
40379
42930
  const coreProperties = await this.parseCoreProperties();
@@ -40708,7 +43259,7 @@ var PptxHandlerRuntime73 = class extends PptxHandlerRuntime72 {
40708
43259
  };
40709
43260
 
40710
43261
  // src/core/core/runtime/PptxHandlerRuntimeImplementation.ts
40711
- var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime73 {
43262
+ var PptxHandlerRuntime75 = class _PptxHandlerRuntime extends PptxHandlerRuntime74 {
40712
43263
  constructor(dependencyFactory = new PptxRuntimeDependencyFactory()) {
40713
43264
  super();
40714
43265
  this.dependencyFactory = dependencyFactory;
@@ -40756,6 +43307,9 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
40756
43307
  extractGradientPathType: (gradFill) => this.colorStyleCodec.extractGradientPathType(gradFill),
40757
43308
  extractGradientFocalPoint: (gradFill) => this.colorStyleCodec.extractGradientFocalPoint(gradFill),
40758
43309
  extractGradientFillToRect: (gradFill) => this.colorStyleCodec.extractGradientFillToRect(gradFill),
43310
+ extractGradientFlip: (gradFill) => this.colorStyleCodec.extractGradientFlip(gradFill),
43311
+ extractGradientRotWithShape: (gradFill) => this.colorStyleCodec.extractGradientRotWithShape(gradFill),
43312
+ extractGradientScaled: (gradFill) => this.colorStyleCodec.extractGradientScaled(gradFill),
40759
43313
  normalizeStrokeDashType: (value) => this.normalizeStrokeDashType(value),
40760
43314
  normalizeConnectorArrowType: (value) => this.normalizeConnectorArrowType(value),
40761
43315
  ensureArray: (value) => this.ensureArray(value),
@@ -40829,7 +43383,7 @@ var PptxHandlerRuntime74 = class _PptxHandlerRuntime extends PptxHandlerRuntime7
40829
43383
  };
40830
43384
 
40831
43385
  // src/core/core/PptxHandlerRuntime.ts
40832
- var PptxHandlerRuntime75 = class extends PptxHandlerRuntime74 {
43386
+ var PptxHandlerRuntime76 = class extends PptxHandlerRuntime75 {
40833
43387
  };
40834
43388
 
40835
43389
  // src/core/core/PptxHandlerRuntimeFactory.ts
@@ -40840,10 +43394,10 @@ var PptxHandlerRuntimeFactory = class {
40840
43394
  * @returns A freshly constructed runtime ready for loading a PPTX file.
40841
43395
  */
40842
43396
  createRuntime() {
40843
- return new PptxHandlerRuntime75();
43397
+ return new PptxHandlerRuntime76();
40844
43398
  }
40845
43399
  };
40846
- var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime75();
43400
+ var createDefaultPptxHandlerRuntime = () => new PptxHandlerRuntime76();
40847
43401
 
40848
43402
  // src/core/PptxHandlerCore.ts
40849
43403
  var PptxHandlerCore = class {
@@ -42213,10 +44767,12 @@ function getConnectorPathGeometry(element) {
42213
44767
  const height = Math.max(element.height, 1);
42214
44768
  const normalizedType = (element.shapeType || "").toLowerCase();
42215
44769
  const point = (x, y) => `${Math.round(x)} ${Math.round(y)}`;
42216
- const startX = 0;
42217
- const startY = 0;
42218
- const endX = width;
42219
- const endY = height;
44770
+ const flipH = Boolean(element.flipHorizontal);
44771
+ const flipV = Boolean(element.flipVertical);
44772
+ const startX = flipH ? width : 0;
44773
+ const startY = flipV ? height : 0;
44774
+ const endX = flipH ? 0 : width;
44775
+ const endY = flipV ? 0 : height;
42220
44776
  if (normalizedType.includes("bentconnector5")) {
42221
44777
  const adj1 = getConnectorAdjustment(element, "adj1", 0.5);
42222
44778
  const adj2 = getConnectorAdjustment(element, "adj2", 0.5);
@@ -42229,7 +44785,7 @@ function getConnectorPathGeometry(element) {
42229
44785
  startY,
42230
44786
  endX,
42231
44787
  endY,
42232
- pathData: `M ${point(0, 0)} L ${point(x1, 0)} L ${point(x1, yMid)} L ${point(x2, yMid)} L ${point(x2, height)} L ${point(width, height)}`
44788
+ pathData: `M ${point(startX, startY)} L ${point(x1, startY)} L ${point(x1, yMid)} L ${point(x2, yMid)} L ${point(x2, endY)} L ${point(endX, endY)}`
42233
44789
  };
42234
44790
  }
42235
44791
  if (normalizedType.includes("bentconnector4")) {
@@ -42242,7 +44798,7 @@ function getConnectorPathGeometry(element) {
42242
44798
  startY,
42243
44799
  endX,
42244
44800
  endY,
42245
- pathData: `M ${point(0, 0)} L ${point(midX, 0)} L ${point(midX, midY)} L ${point(width, midY)} L ${point(width, height)}`
44801
+ pathData: `M ${point(startX, startY)} L ${point(midX, startY)} L ${point(midX, midY)} L ${point(endX, midY)} L ${point(endX, endY)}`
42246
44802
  };
42247
44803
  }
42248
44804
  if (normalizedType.includes("bentconnector3")) {
@@ -42253,7 +44809,7 @@ function getConnectorPathGeometry(element) {
42253
44809
  startY,
42254
44810
  endX,
42255
44811
  endY,
42256
- pathData: `M ${point(0, 0)} L ${point(midX, 0)} L ${point(midX, height)} L ${point(width, height)}`
44812
+ pathData: `M ${point(startX, startY)} L ${point(midX, startY)} L ${point(midX, endY)} L ${point(endX, endY)}`
42257
44813
  };
42258
44814
  }
42259
44815
  if (normalizedType.includes("bentconnector2")) {
@@ -42262,7 +44818,7 @@ function getConnectorPathGeometry(element) {
42262
44818
  startY,
42263
44819
  endX,
42264
44820
  endY,
42265
- pathData: `M ${point(0, 0)} L ${point(width, 0)} L ${point(width, height)}`
44821
+ pathData: `M ${point(startX, startY)} L ${point(endX, startY)} L ${point(endX, endY)}`
42266
44822
  };
42267
44823
  }
42268
44824
  if (normalizedType.includes("curvedconnector5")) {
@@ -42272,12 +44828,14 @@ function getConnectorPathGeometry(element) {
42272
44828
  const x1 = width * adj1;
42273
44829
  const yMid = height * adj2;
42274
44830
  const x2 = width * adj3;
44831
+ const yQuarter = startY + (yMid - startY) * 0.5;
44832
+ const yThreeQ = yMid + (endY - yMid) * 0.5;
42275
44833
  return {
42276
44834
  startX,
42277
44835
  startY,
42278
44836
  endX,
42279
44837
  endY,
42280
- pathData: `M ${point(0, 0)} C ${point(x1, 0)} ${point(x1, 0)} ${point(x1, yMid * 0.5)} C ${point(x1, yMid)} ${point(x1, yMid)} ${point((x1 + x2) / 2, yMid)} C ${point(x2, yMid)} ${point(x2, yMid)} ${point(x2, (yMid + height) / 2)} C ${point(x2, height)} ${point(x2, height)} ${point(width, height)}`
44838
+ pathData: `M ${point(startX, startY)} C ${point(x1, startY)} ${point(x1, startY)} ${point(x1, yQuarter)} C ${point(x1, yMid)} ${point(x1, yMid)} ${point((x1 + x2) / 2, yMid)} C ${point(x2, yMid)} ${point(x2, yMid)} ${point(x2, yThreeQ)} C ${point(x2, endY)} ${point(x2, endY)} ${point(endX, endY)}`
42281
44839
  };
42282
44840
  }
42283
44841
  if (normalizedType.includes("curvedconnector4")) {
@@ -42285,12 +44843,13 @@ function getConnectorPathGeometry(element) {
42285
44843
  const adj2 = getConnectorAdjustment(element, "adj2", 0.5);
42286
44844
  const midX = width * adj1;
42287
44845
  const midY = height * adj2;
44846
+ const yQuarter = startY + (midY - startY) * 0.5;
42288
44847
  return {
42289
44848
  startX,
42290
44849
  startY,
42291
44850
  endX,
42292
44851
  endY,
42293
- pathData: `M ${point(0, 0)} C ${point(midX, 0)} ${point(midX, 0)} ${point(midX, midY * 0.5)} C ${point(midX, midY)} ${point(midX, midY)} ${point((midX + width) / 2, midY)} C ${point(width, midY)} ${point(width, midY)} ${point(width, height)}`
44852
+ pathData: `M ${point(startX, startY)} C ${point(midX, startY)} ${point(midX, startY)} ${point(midX, yQuarter)} C ${point(midX, midY)} ${point(midX, midY)} ${point((midX + endX) / 2, midY)} C ${point(endX, midY)} ${point(endX, midY)} ${point(endX, endY)}`
42294
44853
  };
42295
44854
  }
42296
44855
  if (normalizedType.includes("curvedconnector3")) {
@@ -42302,7 +44861,7 @@ function getConnectorPathGeometry(element) {
42302
44861
  startY,
42303
44862
  endX,
42304
44863
  endY,
42305
- pathData: `M ${point(0, 0)} C ${point(midX, 0)} ${point(midX, 0)} ${point(midX, midY)} C ${point(midX, height)} ${point(midX, height)} ${point(width, height)}`
44864
+ pathData: `M ${point(startX, startY)} C ${point(midX, startY)} ${point(midX, startY)} ${point(midX, midY)} C ${point(midX, endY)} ${point(midX, endY)} ${point(endX, endY)}`
42306
44865
  };
42307
44866
  }
42308
44867
  if (normalizedType.includes("curvedconnector2")) {
@@ -42311,7 +44870,7 @@ function getConnectorPathGeometry(element) {
42311
44870
  startY,
42312
44871
  endX,
42313
44872
  endY,
42314
- pathData: `M ${point(0, 0)} Q ${point(width, 0)} ${point(width, height)}`
44873
+ pathData: `M ${point(startX, startY)} Q ${point(endX, startY)} ${point(endX, endY)}`
42315
44874
  };
42316
44875
  }
42317
44876
  return {
@@ -42319,7 +44878,7 @@ function getConnectorPathGeometry(element) {
42319
44878
  startY,
42320
44879
  endX,
42321
44880
  endY,
42322
- pathData: `M ${point(0, 0)} L ${point(width, height)}`
44881
+ pathData: `M ${point(startX, startY)} L ${point(endX, endY)}`
42323
44882
  };
42324
44883
  }
42325
44884
 
@@ -50481,6 +53040,7 @@ var SvgExporter = class _SvgExporter {
50481
53040
  };
50482
53041
 
50483
53042
  exports.ALL_ANIMATION_PRESETS = ALL_ANIMATION_PRESETS;
53043
+ exports.BLIP_FILL_ORDER = BLIP_FILL_ORDER;
50484
53044
  exports.COLOR_MAP_ALIAS_KEYS = COLOR_MAP_ALIAS_KEYS;
50485
53045
  exports.CONNECTOR_ARROW_OPTIONS = CONNECTOR_ARROW_OPTIONS;
50486
53046
  exports.CONNECTOR_GEOMETRY_OPTIONS = CONNECTOR_GEOMETRY_OPTIONS;
@@ -50502,6 +53062,7 @@ exports.DIGITAL_SIGNATURE_ORIGIN_REL_TYPE = DIGITAL_SIGNATURE_ORIGIN_REL_TYPE;
50502
53062
  exports.DIGITAL_SIGNATURE_REL_TYPE = DIGITAL_SIGNATURE_REL_TYPE;
50503
53063
  exports.DataIntegrityError = DataIntegrityError;
50504
53064
  exports.DocumentConverter = DocumentConverter;
53065
+ exports.EFFECT_LST_ORDER = EFFECT_LST_ORDER;
50505
53066
  exports.EMPHASIS_PRESETS = EMPHASIS_PRESETS;
50506
53067
  exports.EMU_PER_INCH = EMU_PER_INCH;
50507
53068
  exports.EMU_PER_PIXEL = EMU_PER_PIXEL2;
@@ -50554,7 +53115,7 @@ exports.PptxElementTransformUpdater = PptxElementTransformUpdater;
50554
53115
  exports.PptxElementXmlBuilder = PptxElementXmlBuilder;
50555
53116
  exports.PptxGraphicFrameParser = PptxGraphicFrameParser;
50556
53117
  exports.PptxHandler = PptxHandler;
50557
- exports.PptxHandlerRuntime = PptxHandlerRuntime75;
53118
+ exports.PptxHandlerRuntime = PptxHandlerRuntime76;
50558
53119
  exports.PptxHandlerRuntimeFactory = PptxHandlerRuntimeFactory;
50559
53120
  exports.PptxLoadDataBuilder = PptxLoadDataBuilder;
50560
53121
  exports.PptxMarkdownConverter = PptxMarkdownConverter;
@@ -50589,6 +53150,7 @@ exports.PptxXmlLookupService = PptxXmlLookupService;
50589
53150
  exports.Presentation = Presentation;
50590
53151
  exports.PresentationBuilder = PresentationBuilder;
50591
53152
  exports.SHAPE_TREE_ELEMENT_TAGS = SHAPE_TREE_ELEMENT_TAGS;
53153
+ exports.SP_PR_ORDER = SP_PR_ORDER;
50592
53154
  exports.STROKE_DASH_OPTIONS = STROKE_DASH_OPTIONS;
50593
53155
  exports.SUPPORTED_XML_CANON_TRANSFORMS = SUPPORTED_XML_CANON_TRANSFORMS;
50594
53156
  exports.SWITCHABLE_LAYOUT_TYPES = SWITCHABLE_LAYOUT_TYPES;
@@ -50598,6 +53160,7 @@ exports.SlideBuilder = SlideBuilder;
50598
53160
  exports.SlideProcessor = SlideProcessor;
50599
53161
  exports.SlideSizes = SlideSizes;
50600
53162
  exports.SvgExporter = SvgExporter;
53163
+ exports.TC_PR_BORDERS_ORDER = TC_PR_BORDERS_ORDER;
50601
53164
  exports.THEME_COLOR_SCHEME_KEYS = THEME_COLOR_SCHEME_KEYS;
50602
53165
  exports.THEME_PRESETS = THEME_PRESETS;
50603
53166
  exports.TRANSITION_VALID_DIRECTIONS = TRANSITION_VALID_DIRECTIONS;
@@ -50625,6 +53188,7 @@ exports.buildGuideListExtension = buildGuideListExtension;
50625
53188
  exports.buildLinkedTextBoxChains = buildLinkedTextBoxChains;
50626
53189
  exports.buildOle2 = buildOle2;
50627
53190
  exports.buildSingleEffectNode = buildSingleEffectNode;
53191
+ exports.buildSrgbColorChoice = buildSrgbColorChoice;
50628
53192
  exports.buildThemeColorMap = buildThemeColorMap;
50629
53193
  exports.catmullRomToBezier = catmullRomToBezier;
50630
53194
  exports.chartDataAddCategory = chartDataAddCategory;
@@ -50651,6 +53215,7 @@ exports.cloneXmlObject = cloneXmlObject;
50651
53215
  exports.cm = cm;
50652
53216
  exports.cmToEmu = cmToEmu;
50653
53217
  exports.colorWithOpacity = colorWithOpacity;
53218
+ exports.colorsEqual = colorsEqual;
50654
53219
  exports.combineShapes = combineShapes;
50655
53220
  exports.computeContrastRatio = computeContrastRatio;
50656
53221
  exports.computeCycleLayout = computeCycleLayout;
@@ -50713,6 +53278,7 @@ exports.estimateTextBoxCapacity = estimateTextBoxCapacity;
50713
53278
  exports.evaluateGeometryPaths = evaluateGeometryPaths;
50714
53279
  exports.evaluateGuides = evaluateGuides;
50715
53280
  exports.extractAllTagText = extractAllTagText;
53281
+ exports.extractColorChoiceXml = extractColorChoiceXml;
50716
53282
  exports.extractFirstTagText = extractFirstTagText;
50717
53283
  exports.extractGuidFromPartName = extractGuidFromPartName;
50718
53284
  exports.extractModel3DTransform = extractModel3DTransform;
@@ -50862,6 +53428,7 @@ exports.removeChartCategory = removeChartCategory;
50862
53428
  exports.removeChartSeries = removeChartSeries;
50863
53429
  exports.removeSection = removeSection;
50864
53430
  exports.removeSmartArtNode = removeSmartArtNode;
53431
+ exports.reorderObjectKeys = reorderObjectKeys;
50865
53432
  exports.reorderSections = reorderSections;
50866
53433
  exports.reorderSmartArtNode = reorderSmartArtNode;
50867
53434
  exports.reorderSmartArtNodeToIndex = reorderSmartArtNodeToIndex;
@@ -50882,6 +53449,7 @@ exports.resolveReferenceUriToPart = resolveReferenceUriToPart;
50882
53449
  exports.resolveTableCellStyle = resolveTableCellStyle;
50883
53450
  exports.rgbToHsl = rgbToHsl;
50884
53451
  exports.selectAlternateContentBranch = selectAlternateContentBranch;
53452
+ exports.serializeColorChoice = serializeColorChoice;
50885
53453
  exports.serializeCondition = serializeCondition;
50886
53454
  exports.serializeConditionList = serializeConditionList;
50887
53455
  exports.serializeSvgPath = serializeSvgPath;